Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.badlogic.gdx.scenes.scene2d.ui.Table Maven / Gradle / Ivy
/*******************************************************************************
* 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.ui;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.Label.LabelStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Value.Fixed;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.Layout;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.Pools;
/** A group that sizes and positions children using table constraints. By default, {@link #getTouchable()} is
* {@link Touchable#childrenOnly}.
*
* 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 public Color debugTableColor = new Color(0, 0, 1, 1);
static public Color debugCellColor = new Color(1, 0, 0, 1);
static public Color debugActorColor = new Color(0, 1, 0, 1);
static final Pool cellPool = new Pool() {
protected Cell newObject () {
return new Cell();
}
};
static private float[] columnWeightedWidth, rowWeightedHeight;
private int columns, rows;
private final Array cells = new Array(4);
private final Cell cellDefaults;
private final Array columnDefaults = new Array(2);
private Cell rowDefaults;
private boolean sizeInvalid = true;
private float[] columnMinWidth, rowMinHeight;
private float[] columnPrefWidth, rowPrefHeight;
private float tableMinWidth, tableMinHeight;
private float tablePrefWidth, tablePrefHeight;
private float[] columnWidth, rowHeight;
private float[] expandWidth, expandHeight;
Value padTop = backgroundTop, padLeft = backgroundLeft, padBottom = backgroundBottom, padRight = backgroundRight;
int align = Align.center;
Debug debug = Debug.none;
Array debugRects;
Drawable background;
private boolean clip;
private Skin skin;
boolean round = true;
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;
cellDefaults = obtainCell();
setTransform(false);
setTouchable(Touchable.childrenOnly);
}
private Cell obtainCell () {
Cell cell = cellPool.obtain();
cell.setLayout(this);
return cell;
}
public void draw (Batch batch, float parentAlpha) {
validate();
if (isTransform()) {
applyTransform(batch, computeTransform());
drawBackground(batch, parentAlpha, 0, 0);
if (clip) {
batch.flush();
float x = 0, y = 0, width = getWidth(), height = getHeight();
if (background != null) {
x = padLeft.get(this);
y = padBottom.get(this);
width -= x + padRight.get(this);
height -= y + padTop.get(this);
}
if (clipBegin(x, y, width, height)) {
drawChildren(batch, parentAlpha);
batch.flush();
clipEnd();
}
} else
drawChildren(batch, parentAlpha);
resetTransform(batch);
} else {
drawBackground(batch, parentAlpha, getX(), getY());
super.draw(batch, parentAlpha);
}
}
/** Called to draw the background, before clipping is applied (if enabled). Default implementation draws the background
* drawable. */
protected void drawBackground (Batch batch, float parentAlpha, float x, float y) {
if (background == null) return;
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
background.draw(batch, x, y, getWidth(), getHeight());
}
/** Sets the background drawable from the skin and adjusts the table's padding to match the background. This may only be called
* if {@link Table#Table(Skin)} or {@link #setSkin(Skin)} was used.
* @see #setBackground(Drawable) */
public void setBackground (String drawableName) {
if (skin == null) throw new IllegalStateException("Table must have a skin set to use this method.");
setBackground(skin.getDrawable(drawableName));
}
/** @param background May be null to clear the background. */
public void setBackground (Drawable background) {
if (this.background == background) return;
float padTopOld = getPadTop(), padLeftOld = getPadLeft(), padBottomOld = getPadBottom(), padRightOld = getPadRight();
this.background = background;
float padTopNew = getPadTop(), padLeftNew = getPadLeft(), padBottomNew = getPadBottom(), padRightNew = getPadRight();
if (padTopOld + padBottomOld != padTopNew + padBottomNew || padLeftOld + padRightOld != padLeftNew + padRightNew)
invalidateHierarchy();
else if (padTopOld != padTopNew || padLeftOld != padLeftNew || padBottomOld != padBottomNew || padRightOld != padRightNew)
invalidate();
}
/** @see #setBackground(Drawable) */
public Table background (Drawable background) {
setBackground(background);
return this;
}
/** @see #setBackground(String) */
public Table background (String drawableName) {
setBackground(drawableName);
return this;
}
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 actor's bounds. Enabling clipping will set
* {@link #setTransform(boolean)} to true. */
public void setClip (boolean enabled) {
clip = enabled;
setTransform(enabled);
invalidate();
}
public boolean getClip () {
return clip;
}
public void invalidate () {
sizeInvalid = true;
super.invalidate();
}
/** Adds a new cell to the table with the specified actor. */
public Cell add (T actor) {
Cell cell = obtainCell();
cell.actor = actor;
Array cells = this.cells;
int cellCount = cells.size;
if (cellCount > 0) {
// Set cell column and row.
Cell lastCell = cells.peek();
if (!lastCell.endRow) {
cell.column = lastCell.column + lastCell.colspan;
cell.row = lastCell.row;
} else {
cell.column = 0;
cell.row = lastCell.row + 1;
}
// Set the index of the cell above.
if (cell.row > 0) {
outer:
for (int i = cellCount - 1; i >= 0; i--) {
Cell other = cells.get(i);
for (int column = other.column, nn = column + other.colspan; column < nn; column++) {
if (column == cell.column) {
cell.cellAboveIndex = i;
break outer;
}
}
}
}
} else {
cell.column = 0;
cell.row = 0;
}
cells.add(cell);
cell.set(cellDefaults);
if (cell.column < columnDefaults.size) {
Cell columnCell = columnDefaults.get(cell.column);
if (columnCell != null) cell.merge(columnCell);
}
cell.merge(rowDefaults);
if (actor != null) addActor(actor);
return cell;
}
public void add (Actor... actors) {
for (int i = 0, n = actors.length; i < n; i++)
add(actors[i]);
}
/** 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 an actor. */
public Cell add () {
return add((Actor)null);
}
/** 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);
}
public boolean removeActor (Actor actor) {
if (!super.removeActor(actor)) return false;
Cell cell = getCell(actor);
if (cell != null) cell.actor = null;
return true;
}
/** Removes all actors and cells from the table. */
public void clearChildren () {
Array cells = this.cells;
for (int i = cells.size - 1; i >= 0; i--) {
Cell cell = cells.get(i);
Actor actor = cell.actor;
if (actor != null) actor.remove();
}
cellPool.freeAll(cells);
cells.clear();
rows = 0;
columns = 0;
if (rowDefaults != null) cellPool.free(rowDefaults);
rowDefaults = null;
super.clearChildren();
}
/** 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 () {
clear();
padTop = backgroundTop;
padLeft = backgroundLeft;
padBottom = backgroundBottom;
padRight = backgroundRight;
align = Align.center;
debug(Debug.none);
cellDefaults.reset();
for (int i = 0, n = columnDefaults.size; i < n; i++) {
Cell columnCell = columnDefaults.get(i);
if (columnCell != null) cellPool.free(columnCell);
}
columnDefaults.clear();
}
/** 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 () {
if (cells.size > 0) {
endRow();
invalidate();
}
if (rowDefaults != null) cellPool.free(rowDefaults);
rowDefaults = obtainCell();
rowDefaults.clear();
return rowDefaults;
}
private void endRow () {
Array cells = this.cells;
int rowColumns = 0;
for (int i = cells.size - 1; i >= 0; i--) {
Cell cell = cells.get(i);
if (cell.endRow) break;
rowColumns += cell.colspan;
}
columns = Math.max(columns, rowColumns);
rows++;
cells.peek().endRow = true;
}
/** 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) {
Cell cell = columnDefaults.size > column ? columnDefaults.get(column) : null;
if (cell == null) {
cell = obtainCell();
cell.clear();
if (column >= columnDefaults.size) {
for (int i = columnDefaults.size; i < column; i++)
columnDefaults.add(null);
columnDefaults.add(cell);
} else
columnDefaults.set(column, cell);
}
return cell;
}
/** Returns the cell for the specified actor in this table, or null. */
public Cell getCell (T actor) {
Array cells = this.cells;
for (int i = 0, n = cells.size; i < n; i++) {
Cell c = cells.get(i);
if (c.actor == actor) return c;
}
return null;
}
/** Returns the cells for this table. */
public Array getCells () {
return cells;
}
public float getPrefWidth () {
if (sizeInvalid) computeSize();
float width = tablePrefWidth;
if (background != null) return Math.max(width, background.getMinWidth());
return width;
}
public float getPrefHeight () {
if (sizeInvalid) computeSize();
float height = tablePrefHeight;
if (background != null) return Math.max(height, background.getMinHeight());
return height;
}
public float getMinWidth () {
if (sizeInvalid) computeSize();
return tableMinWidth;
}
public float getMinHeight () {
if (sizeInvalid) computeSize();
return tableMinHeight;
}
/** The cell values that will be used as the defaults for all cells. */
public Cell defaults () {
return cellDefaults;
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public Table pad (Value pad) {
if (pad == null) throw new IllegalArgumentException("pad cannot be null.");
padTop = pad;
padLeft = pad;
padBottom = pad;
padRight = pad;
sizeInvalid = true;
return this;
}
public Table pad (Value top, Value left, Value bottom, Value right) {
if (top == null) throw new IllegalArgumentException("top cannot be null.");
if (left == null) throw new IllegalArgumentException("left cannot be null.");
if (bottom == null) throw new IllegalArgumentException("bottom cannot be null.");
if (right == null) throw new IllegalArgumentException("right cannot be null.");
padTop = top;
padLeft = left;
padBottom = bottom;
padRight = right;
sizeInvalid = true;
return this;
}
/** Padding at the top edge of the table. */
public Table padTop (Value padTop) {
if (padTop == null) throw new IllegalArgumentException("padTop cannot be null.");
this.padTop = padTop;
sizeInvalid = true;
return this;
}
/** Padding at the left edge of the table. */
public Table padLeft (Value padLeft) {
if (padLeft == null) throw new IllegalArgumentException("padLeft cannot be null.");
this.padLeft = padLeft;
sizeInvalid = true;
return this;
}
/** Padding at the bottom edge of the table. */
public Table padBottom (Value padBottom) {
if (padBottom == null) throw new IllegalArgumentException("padBottom cannot be null.");
this.padBottom = padBottom;
sizeInvalid = true;
return this;
}
/** Padding at the right edge of the table. */
public Table padRight (Value padRight) {
if (padRight == null) throw new IllegalArgumentException("padRight cannot be null.");
this.padRight = padRight;
sizeInvalid = true;
return this;
}
/** Sets the padTop, padLeft, padBottom, and padRight around the table to the specified value. */
public Table pad (float pad) {
pad(new Fixed(pad));
return this;
}
public Table pad (float top, float left, float bottom, float right) {
padTop = new Fixed(top);
padLeft = new Fixed(left);
padBottom = new Fixed(bottom);
padRight = new Fixed(right);
sizeInvalid = true;
return this;
}
/** Padding at the top edge of the table. */
public Table padTop (float padTop) {
this.padTop = new Fixed(padTop);
sizeInvalid = true;
return this;
}
/** Padding at the left edge of the table. */
public Table padLeft (float padLeft) {
this.padLeft = new Fixed(padLeft);
sizeInvalid = true;
return this;
}
/** Padding at the bottom edge of the table. */
public Table padBottom (float padBottom) {
this.padBottom = new Fixed(padBottom);
sizeInvalid = true;
return this;
}
/** Padding at the right edge of the table. */
public Table padRight (float padRight) {
this.padRight = new Fixed(padRight);
sizeInvalid = true;
return this;
}
/** Alignment of the logical table within the table actor. 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) {
this.align = align;
return this;
}
/** Sets the alignment of the logical table within the table actor to {@link Align#center}. This clears any other alignment. */
public Table center () {
align = Align.center;
return this;
}
/** Adds {@link Align#top} and clears {@link Align#bottom} for the alignment of the logical table within the table actor. */
public Table top () {
align |= Align.top;
align &= ~Align.bottom;
return this;
}
/** Adds {@link Align#left} and clears {@link Align#right} for the alignment of the logical table within the table actor. */
public Table left () {
align |= Align.left;
align &= ~Align.right;
return this;
}
/** Adds {@link Align#bottom} and clears {@link Align#top} for the alignment of the logical table within the table actor. */
public Table bottom () {
align |= Align.bottom;
align &= ~Align.top;
return this;
}
/** Adds {@link Align#right} and clears {@link Align#left} for the alignment of the logical table within the table actor. */
public Table right () {
align |= Align.right;
align &= ~Align.left;
return this;
}
public void setDebug (boolean enabled) {
debug(enabled ? Debug.all : Debug.none);
}
public Table debug () {
super.debug();
return this;
}
public Table debugAll () {
super.debugAll();
return this;
}
/** Turns on table debug lines. */
public Table debugTable () {
super.setDebug(true);
if (debug != Debug.table) {
this.debug = Debug.table;
invalidate();
}
return this;
}
/** Turns on cell debug lines. */
public Table debugCell () {
super.setDebug(true);
if (debug != Debug.cell) {
this.debug = Debug.cell;
invalidate();
}
return this;
}
/** Turns on actor debug lines. */
public Table debugActor () {
super.setDebug(true);
if (debug != Debug.actor) {
this.debug = Debug.actor;
invalidate();
}
return this;
}
/** Turns debug lines on or off. */
public Table debug (Debug debug) {
super.setDebug(debug != Debug.none);
if (this.debug != debug) {
this.debug = debug;
if (debug == Debug.none)
clearDebugRects();
else
invalidate();
}
return this;
}
public Debug getTableDebug () {
return debug;
}
public Value getPadTopValue () {
return padTop;
}
public float getPadTop () {
return padTop.get(this);
}
public Value getPadLeftValue () {
return padLeft;
}
public float getPadLeft () {
return padLeft.get(this);
}
public Value getPadBottomValue () {
return padBottom;
}
public float getPadBottom () {
return padBottom.get(this);
}
public Value getPadRightValue () {
return padRight;
}
public float getPadRight () {
return padRight.get(this);
}
/** Returns {@link #getPadLeft()} plus {@link #getPadRight()}. */
public float getPadX () {
return padLeft.get(this) + padRight.get(this);
}
/** Returns {@link #getPadTop()} plus {@link #getPadBottom()}. */
public float getPadY () {
return padTop.get(this) + padBottom.get(this);
}
public int getAlign () {
return align;
}
/** Returns the row index for the y coordinate, or -1 if there are no cells.
* @param y The y coordinate, where 0 is the top of the table. */
public int getRow (float y) {
Array cells = this.cells;
int row = 0;
y += getPadTop();
int i = 0, n = cells.size;
if (n == 0) return -1;
if (n == 1) return 0;
while (i < n) {
Cell c = cells.get(i++);
if (c.actorY + c.computedPadTop < y) break;
if (c.endRow) row++;
}
return row;
}
public void setSkin (Skin skin) {
this.skin = skin;
}
/** If true (the default), positions and sizes are rounded to integers. */
public void setRound (boolean round) {
this.round = round;
}
public int getRows () {
return rows;
}
public int getColumns () {
return columns;
}
private float[] ensureSize (float[] array, int size) {
if (array == null || array.length < size) return new float[size];
for (int i = 0, n = array.length; i < n; i++)
array[i] = 0;
return array;
}
public void layout () {
float width = getWidth();
float height = getHeight();
layout(0, 0, width, height);
Array cells = this.cells;
if (round) {
for (int i = 0, n = cells.size; i < n; i++) {
Cell c = cells.get(i);
float actorWidth = Math.round(c.actorWidth);
float actorHeight = Math.round(c.actorHeight);
float actorX = Math.round(c.actorX);
float actorY = height - Math.round(c.actorY) - actorHeight;
c.setActorBounds(actorX, actorY, actorWidth, actorHeight);
Actor actor = c.actor;
if (actor != null) actor.setBounds(actorX, actorY, actorWidth, actorHeight);
}
} else {
for (int i = 0, n = cells.size; i < n; i++) {
Cell c = cells.get(i);
float actorHeight = c.actorHeight;
float actorY = height - c.actorY - actorHeight;
c.setActorY(actorY);
Actor actor = c.actor;
if (actor != null) actor.setBounds(c.actorX, actorY, c.actorWidth, actorHeight);
}
}
// Validate children separately from sizing actors to ensure actors without a cell are validated.
Array children = getChildren();
for (int i = 0, n = children.size; i < n; i++) {
Actor child = children.get(i);
if (child instanceof Layout) ((Layout)child).validate();
}
}
private void computeSize () {
sizeInvalid = false;
Array cells = this.cells;
int cellCount = cells.size;
if (cellCount > 0 && !cells.peek().endRow) endRow();
int columns = this.columns, rows = this.rows;
float[] columnMinWidth = this.columnMinWidth = ensureSize(this.columnMinWidth, columns);
float[] rowMinHeight = this.rowMinHeight = ensureSize(this.rowMinHeight, rows);
float[] columnPrefWidth = this.columnPrefWidth = ensureSize(this.columnPrefWidth, columns);
float[] rowPrefHeight = this.rowPrefHeight = ensureSize(this.rowPrefHeight, rows);
float[] columnWidth = this.columnWidth = ensureSize(this.columnWidth, columns);
float[] rowHeight = this.rowHeight = ensureSize(this.rowHeight, rows);
float[] expandWidth = this.expandWidth = ensureSize(this.expandWidth, columns);
float[] expandHeight = this.expandHeight = ensureSize(this.expandHeight, rows);
float spaceRightLast = 0;
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
int column = c.column, row = c.row, colspan = c.colspan;
Actor a = c.actor;
// Collect columns/rows that expand.
if (c.expandY != 0 && expandHeight[row] == 0) expandHeight[row] = c.expandY;
if (colspan == 1 && c.expandX != 0 && expandWidth[column] == 0) expandWidth[column] = c.expandX;
// Compute combined padding/spacing for cells.
// Spacing between actors isn't additive, the larger is used. Also, no spacing around edges.
c.computedPadLeft = c.padLeft.get(a) + (column == 0 ? 0 : Math.max(0, c.spaceLeft.get(a) - spaceRightLast));
c.computedPadTop = c.padTop.get(a);
if (c.cellAboveIndex != -1) {
Cell above = cells.get(c.cellAboveIndex);
c.computedPadTop += Math.max(0, c.spaceTop.get(a) - above.spaceBottom.get(a));
}
float spaceRight = c.spaceRight.get(a);
c.computedPadRight = c.padRight.get(a) + ((column + colspan) == columns ? 0 : spaceRight);
c.computedPadBottom = c.padBottom.get(a) + (row == rows - 1 ? 0 : c.spaceBottom.get(a));
spaceRightLast = spaceRight;
// Determine minimum and preferred cell sizes.
float prefWidth = c.prefWidth.get(a);
float prefHeight = c.prefHeight.get(a);
float minWidth = c.minWidth.get(a);
float minHeight = c.minHeight.get(a);
float maxWidth = c.maxWidth.get(a);
float maxHeight = c.maxHeight.get(a);
if (prefWidth < minWidth) prefWidth = minWidth;
if (prefHeight < minHeight) prefHeight = minHeight;
if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
if (maxHeight > 0 && prefHeight > maxHeight) prefHeight = maxHeight;
if (colspan == 1) { // Spanned column min and pref width is added later.
float hpadding = c.computedPadLeft + c.computedPadRight;
columnPrefWidth[column] = Math.max(columnPrefWidth[column], prefWidth + hpadding);
columnMinWidth[column] = Math.max(columnMinWidth[column], minWidth + hpadding);
}
float vpadding = c.computedPadTop + c.computedPadBottom;
rowPrefHeight[row] = Math.max(rowPrefHeight[row], prefHeight + vpadding);
rowMinHeight[row] = Math.max(rowMinHeight[row], minHeight + vpadding);
}
// Colspan with expand will expand all spanned columns if none of the spanned columns have expand.
outer:
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
int expandX = c.expandX;
if (expandX == 0) continue;
int column = c.column, nn = column + c.colspan;
for (int ii = column; ii < nn; ii++)
if (expandWidth[ii] != 0) continue outer;
for (int ii = column; ii < nn; ii++)
expandWidth[ii] = expandX;
}
// Distribute any additional min and pref width added by colspanned cells to the columns spanned.
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
int colspan = c.colspan;
if (colspan == 1) continue;
int column = c.column;
Actor a = c.actor;
float minWidth = c.minWidth.get(a);
float prefWidth = c.prefWidth.get(a);
float maxWidth = c.maxWidth.get(a);
if (prefWidth < minWidth) prefWidth = minWidth;
if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
float spannedMinWidth = -(c.computedPadLeft + c.computedPadRight), spannedPrefWidth = spannedMinWidth;
for (int ii = column, nn = ii + colspan; ii < nn; ii++) {
spannedMinWidth += columnMinWidth[ii];
spannedPrefWidth += columnPrefWidth[ii];
}
// Distribute extra space using expand, if any columns have expand.
float totalExpandWidth = 0;
for (int ii = column, nn = ii + colspan; ii < nn; ii++)
totalExpandWidth += expandWidth[ii];
float extraMinWidth = Math.max(0, minWidth - spannedMinWidth);
float extraPrefWidth = Math.max(0, prefWidth - spannedPrefWidth);
for (int ii = column, nn = ii + colspan; ii < nn; ii++) {
float ratio = totalExpandWidth == 0 ? 1f / colspan : expandWidth[ii] / totalExpandWidth;
columnMinWidth[ii] += extraMinWidth * ratio;
columnPrefWidth[ii] += extraPrefWidth * ratio;
}
}
// Collect uniform size.
float uniformMinWidth = 0, uniformMinHeight = 0;
float uniformPrefWidth = 0, uniformPrefHeight = 0;
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
// Collect uniform sizes.
if (c.uniformX == Boolean.TRUE && c.colspan == 1) {
float hpadding = c.computedPadLeft + c.computedPadRight;
uniformMinWidth = Math.max(uniformMinWidth, columnMinWidth[c.column] - hpadding);
uniformPrefWidth = Math.max(uniformPrefWidth, columnPrefWidth[c.column] - hpadding);
}
if (c.uniformY == Boolean.TRUE) {
float vpadding = c.computedPadTop + c.computedPadBottom;
uniformMinHeight = Math.max(uniformMinHeight, rowMinHeight[c.row] - vpadding);
uniformPrefHeight = Math.max(uniformPrefHeight, rowPrefHeight[c.row] - vpadding);
}
}
// Size uniform cells to the same width/height.
if (uniformPrefWidth > 0 || uniformPrefHeight > 0) {
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
if (uniformPrefWidth > 0 && c.uniformX == Boolean.TRUE && c.colspan == 1) {
float hpadding = c.computedPadLeft + c.computedPadRight;
columnMinWidth[c.column] = uniformMinWidth + hpadding;
columnPrefWidth[c.column] = uniformPrefWidth + hpadding;
}
if (uniformPrefHeight > 0 && c.uniformY == Boolean.TRUE) {
float vpadding = c.computedPadTop + c.computedPadBottom;
rowMinHeight[c.row] = uniformMinHeight + vpadding;
rowPrefHeight[c.row] = uniformPrefHeight + vpadding;
}
}
}
// Determine table min and pref size.
tableMinWidth = 0;
tableMinHeight = 0;
tablePrefWidth = 0;
tablePrefHeight = 0;
for (int i = 0; i < columns; i++) {
tableMinWidth += columnMinWidth[i];
tablePrefWidth += columnPrefWidth[i];
}
for (int i = 0; i < rows; i++) {
tableMinHeight += rowMinHeight[i];
tablePrefHeight += Math.max(rowMinHeight[i], rowPrefHeight[i]);
}
float hpadding = padLeft.get(this) + padRight.get(this);
float vpadding = padTop.get(this) + padBottom.get(this);
tableMinWidth = tableMinWidth + hpadding;
tableMinHeight = tableMinHeight + vpadding;
tablePrefWidth = Math.max(tablePrefWidth + hpadding, tableMinWidth);
tablePrefHeight = Math.max(tablePrefHeight + vpadding, tableMinHeight);
}
/** Positions and sizes children of the table using the cell associated with each child. The values given are the position
* within the parent and size of the table. */
private void layout (float layoutX, float layoutY, float layoutWidth, float layoutHeight) {
Array cells = this.cells;
int cellCount = cells.size;
if (sizeInvalid) computeSize();
float padLeft = this.padLeft.get(this);
float hpadding = padLeft + padRight.get(this);
float padTop = this.padTop.get(this);
float vpadding = padTop + padBottom.get(this);
int columns = this.columns, rows = this.rows;
float[] expandWidth = this.expandWidth, expandHeight = this.expandHeight;
float[] columnWidth = this.columnWidth, rowHeight = this.rowHeight;
float totalExpandWidth = 0, totalExpandHeight = 0;
for (int i = 0; i < columns; i++)
totalExpandWidth += expandWidth[i];
for (int i = 0; i < rows; i++)
totalExpandHeight += expandHeight[i];
// Size columns and rows between min and pref size using (preferred - min) size to weight distribution of extra space.
float[] columnWeightedWidth;
float totalGrowWidth = tablePrefWidth - tableMinWidth;
if (totalGrowWidth == 0)
columnWeightedWidth = columnMinWidth;
else {
float extraWidth = Math.min(totalGrowWidth, Math.max(0, layoutWidth - tableMinWidth));
columnWeightedWidth = Table.columnWeightedWidth = ensureSize(Table.columnWeightedWidth, columns);
float[] columnMinWidth = this.columnMinWidth, columnPrefWidth = this.columnPrefWidth;
for (int i = 0; i < columns; i++) {
float growWidth = columnPrefWidth[i] - columnMinWidth[i];
float growRatio = growWidth / totalGrowWidth;
columnWeightedWidth[i] = columnMinWidth[i] + extraWidth * growRatio;
}
}
float[] rowWeightedHeight;
float totalGrowHeight = tablePrefHeight - tableMinHeight;
if (totalGrowHeight == 0)
rowWeightedHeight = rowMinHeight;
else {
rowWeightedHeight = Table.rowWeightedHeight = ensureSize(Table.rowWeightedHeight, rows);
float extraHeight = Math.min(totalGrowHeight, Math.max(0, layoutHeight - tableMinHeight));
float[] rowMinHeight = this.rowMinHeight, rowPrefHeight = this.rowPrefHeight;
for (int i = 0; i < rows; i++) {
float growHeight = rowPrefHeight[i] - rowMinHeight[i];
float growRatio = growHeight / totalGrowHeight;
rowWeightedHeight[i] = rowMinHeight[i] + extraHeight * growRatio;
}
}
// Determine actor and cell sizes (before expand or fill).
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
int column = c.column, row = c.row;
Actor a = c.actor;
float spannedWeightedWidth = 0;
int colspan = c.colspan;
for (int ii = column, nn = ii + colspan; ii < nn; ii++)
spannedWeightedWidth += columnWeightedWidth[ii];
float weightedHeight = rowWeightedHeight[row];
float prefWidth = c.prefWidth.get(a);
float prefHeight = c.prefHeight.get(a);
float minWidth = c.minWidth.get(a);
float minHeight = c.minHeight.get(a);
float maxWidth = c.maxWidth.get(a);
float maxHeight = c.maxHeight.get(a);
if (prefWidth < minWidth) prefWidth = minWidth;
if (prefHeight < minHeight) prefHeight = minHeight;
if (maxWidth > 0 && prefWidth > maxWidth) prefWidth = maxWidth;
if (maxHeight > 0 && prefHeight > maxHeight) prefHeight = maxHeight;
c.actorWidth = Math.min(spannedWeightedWidth - c.computedPadLeft - c.computedPadRight, prefWidth);
c.actorHeight = Math.min(weightedHeight - c.computedPadTop - c.computedPadBottom, prefHeight);
if (colspan == 1) columnWidth[column] = Math.max(columnWidth[column], spannedWeightedWidth);
rowHeight[row] = Math.max(rowHeight[row], weightedHeight);
}
// Distribute remaining space to any expanding columns/rows.
if (totalExpandWidth > 0) {
float extra = layoutWidth - hpadding;
for (int i = 0; i < columns; i++)
extra -= columnWidth[i];
float used = 0;
int lastIndex = 0;
for (int i = 0; i < columns; i++) {
if (expandWidth[i] == 0) continue;
float amount = extra * expandWidth[i] / totalExpandWidth;
columnWidth[i] += amount;
used += amount;
lastIndex = i;
}
columnWidth[lastIndex] += extra - used;
}
if (totalExpandHeight > 0) {
float extra = layoutHeight - vpadding;
for (int i = 0; i < rows; i++)
extra -= rowHeight[i];
float used = 0;
int lastIndex = 0;
for (int i = 0; i < rows; i++) {
if (expandHeight[i] == 0) continue;
float amount = extra * expandHeight[i] / totalExpandHeight;
rowHeight[i] += amount;
used += amount;
lastIndex = i;
}
rowHeight[lastIndex] += extra - used;
}
// Distribute any additional width added by colspanned cells to the columns spanned.
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
int colspan = c.colspan;
if (colspan == 1) continue;
float extraWidth = 0;
for (int column = c.column, nn = column + colspan; column < nn; column++)
extraWidth += columnWeightedWidth[column] - columnWidth[column];
extraWidth -= Math.max(0, c.computedPadLeft + c.computedPadRight);
extraWidth /= colspan;
if (extraWidth > 0) {
for (int column = c.column, nn = column + colspan; column < nn; column++)
columnWidth[column] += extraWidth;
}
}
// Determine table size.
float tableWidth = hpadding, tableHeight = vpadding;
for (int i = 0; i < columns; i++)
tableWidth += columnWidth[i];
for (int i = 0; i < rows; i++)
tableHeight += rowHeight[i];
// Position table within the container.
int align = this.align;
float x = layoutX + padLeft;
if ((align & Align.right) != 0)
x += layoutWidth - tableWidth;
else if ((align & Align.left) == 0) // Center
x += (layoutWidth - tableWidth) / 2;
float y = layoutY + padTop;
if ((align & Align.bottom) != 0)
y += layoutHeight - tableHeight;
else if ((align & Align.top) == 0) // Center
y += (layoutHeight - tableHeight) / 2;
// Position actors within cells.
float currentX = x, currentY = y;
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
float spannedCellWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
spannedCellWidth += columnWidth[column];
spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
currentX += c.computedPadLeft;
float fillX = c.fillX, fillY = c.fillY;
if (fillX > 0) {
c.actorWidth = Math.max(spannedCellWidth * fillX, c.minWidth.get(c.actor));
float maxWidth = c.maxWidth.get(c.actor);
if (maxWidth > 0) c.actorWidth = Math.min(c.actorWidth, maxWidth);
}
if (fillY > 0) {
c.actorHeight = Math.max(rowHeight[c.row] * fillY - c.computedPadTop - c.computedPadBottom, c.minHeight.get(c.actor));
float maxHeight = c.maxHeight.get(c.actor);
if (maxHeight > 0) c.actorHeight = Math.min(c.actorHeight, maxHeight);
}
align = c.align;
if ((align & Align.left) != 0)
c.actorX = currentX;
else if ((align & Align.right) != 0)
c.actorX = currentX + spannedCellWidth - c.actorWidth;
else
c.actorX = currentX + (spannedCellWidth - c.actorWidth) / 2;
if ((align & Align.top) != 0)
c.actorY = currentY + c.computedPadTop;
else if ((align & Align.bottom) != 0)
c.actorY = currentY + rowHeight[c.row] - c.actorHeight - c.computedPadBottom;
else
c.actorY = currentY + (rowHeight[c.row] - c.actorHeight + c.computedPadTop - c.computedPadBottom) / 2;
if (c.endRow) {
currentX = x;
currentY += rowHeight[c.row];
} else
currentX += spannedCellWidth + c.computedPadRight;
}
// Store debug rectangles.
if (debug == Debug.none) return;
clearDebugRects();
currentX = x;
currentY = y;
if (debug == Debug.table || debug == Debug.all) {
addDebugRect(layoutX, layoutY, layoutWidth, layoutHeight, debugTableColor);
addDebugRect(x, y, tableWidth - hpadding, tableHeight - vpadding, debugTableColor);
}
for (int i = 0; i < cellCount; i++) {
Cell c = cells.get(i);
// Actor bounds.
if (debug == Debug.actor || debug == Debug.all)
addDebugRect(c.actorX, c.actorY, c.actorWidth, c.actorHeight, debugActorColor);
// Cell bounds.
float spannedCellWidth = 0;
for (int column = c.column, nn = column + c.colspan; column < nn; column++)
spannedCellWidth += columnWidth[column];
spannedCellWidth -= c.computedPadLeft + c.computedPadRight;
currentX += c.computedPadLeft;
if (debug == Debug.cell || debug == Debug.all) {
addDebugRect(currentX, currentY + c.computedPadTop, spannedCellWidth, rowHeight[c.row] - c.computedPadTop
- c.computedPadBottom, debugCellColor);
}
if (c.endRow) {
currentX = x;
currentY += rowHeight[c.row];
} else
currentX += spannedCellWidth + c.computedPadRight;
}
}
private void clearDebugRects () {
if (debugRects == null) return;
DebugRect.pool.freeAll(debugRects);
debugRects.clear();
}
private void addDebugRect (float x, float y, float w, float h, Color color) {
if (debugRects == null) debugRects = new Array();
DebugRect rect = DebugRect.pool.obtain();
rect.color = color;
rect.set(x, getHeight() - y - h, w, h);
debugRects.add(rect);
}
public void drawDebug (ShapeRenderer shapes) {
if (isTransform()) {
applyTransform(shapes, computeTransform());
drawDebugRects(shapes);
if (clip) {
shapes.flush();
float x = 0, y = 0, width = getWidth(), height = getHeight();
if (background != null) {
x = padLeft.get(this);
y = padBottom.get(this);
width -= x + padRight.get(this);
height -= y + padTop.get(this);
}
if (clipBegin(x, y, width, height)) {
drawDebugChildren(shapes);
clipEnd();
}
} else
drawDebugChildren(shapes);
resetTransform(shapes);
} else {
drawDebugRects(shapes);
super.drawDebug(shapes);
}
}
protected void drawDebugBounds (ShapeRenderer shapes) {
}
private void drawDebugRects (ShapeRenderer shapes) {
if (debugRects == null || !getDebug()) return;
shapes.set(ShapeType.Line);
shapes.setColor(getStage().getDebugColor());
float x = 0, y = 0;
if (!isTransform()) {
x = getX();
y = getY();
}
for (int i = 0, n = debugRects.size; i < n; i++) {
DebugRect debugRect = debugRects.get(i);
shapes.setColor(debugRect.color);
shapes.rect(x + debugRect.x, y + debugRect.y, debugRect.width, debugRect.height);
}
}
/** @author Nathan Sweet */
static public class DebugRect extends Rectangle {
static Pool pool = Pools.get(DebugRect.class);
Color color;
}
/** @author Nathan Sweet */
static public enum Debug {
none, all, table, cell, actor
}
/** Value that is the top padding of the table's background.
* @author Nathan Sweet */
static public Value backgroundTop = new Value() {
public float get (Actor context) {
Drawable background = ((Table)context).background;
return background == null ? 0 : background.getTopHeight();
}
};
/** Value that is the left padding of the table's background.
* @author Nathan Sweet */
static public Value backgroundLeft = new Value() {
public float get (Actor context) {
Drawable background = ((Table)context).background;
return background == null ? 0 : background.getLeftWidth();
}
};
/** Value that is the bottom padding of the table's background.
* @author Nathan Sweet */
static public Value backgroundBottom = new Value() {
public float get (Actor context) {
Drawable background = ((Table)context).background;
return background == null ? 0 : background.getBottomHeight();
}
};
/** Value that is the right padding of the table's background.
* @author Nathan Sweet */
static public Value backgroundRight = new Value() {
public float get (Actor context) {
Drawable background = ((Table)context).background;
return background == null ? 0 : background.getRightWidth();
}
};
}
| | | | | | | | | | | | |