/* * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores * CA 94065 USA or visit www.oracle.com if you need additional information or * have any questions. */ package com.codename1.ui.table; import com.codename1.io.Log; import com.codename1.ui.layouts.*; import com.codename1.ui.Component; import com.codename1.ui.Container; import com.codename1.ui.Display; import com.codename1.ui.geom.Dimension; import com.codename1.ui.plaf.Style; import java.util.Vector; /** * <p>TableLayout is a very elaborate <b>constraint based</b> layout manager that can arrange elements * in rows/columns while defining constraints to control complex behavior such as spanning, alignment/weight * etc.<br> * Notice that the table layout is in the <code>com.codename1.ui.table</code> package and not in the * layouts package. <br> * This is due to the fact that <code>TableLayout</code> was originally designed for the * {@link Table} class.</p> * * <p>Despite being constraint based the table layout isn't strict about constraints and will implicitly add a * constraint when one is missing. However, unlike grid layout table layout won't implicitly add a row if the * row/column count is incorrect<br> * E.g this creates a 2x2 table but adds 5 elements. The 5th element won't show:</p> * <script src="https://gist.github.com/codenameone/a25944769128d5330cd4.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/table-layout-2x2.png" alt="TableLayout 2x2 missing component" /> * * <p>Table layout supports the ability to grow the last column which can be enabled using the * <code>setGrowHorizontally</code> method. You can also use a shortened terse syntax to construct a table * layout however since the table layout is a constraint based layout you won't be able to utilize its full power * with this syntax.</> * * <p>The default usage of the encloseIn below uses the <code>setGrowHorizontally</code> flag.</p> *<script src="https://gist.github.com/codenameone/2b4d9a13f409e297fb2e.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/table-layout-enclose.png" alt="TableLayout that grows the last column" /> * * <h4>The Full Potential</h4> *<p>To truly appreciate the {@code TableLayout} we need to use the constraint syntax which allows * us to span, align and set width/height for the rows & columns.<br> * Table layout works with a {@link Constraint} instance that can communicate our intentions into the * layout manager. Such constraints can include more than one attribute e.g. span and height.</p> * * <p>Notice that table layout constraints can't be reused for more than one component.<br> * The constraint class supports the following attributes:</p> * * <table summary="" border="1"> * <tr> * <td> column </td><td> The column for the table cell. This defaults to -1 which will just place the component in the next available cell</td> * </tr> * <tr> * <td> row </td><td> Similar to column, defaults to -1 as well</td> * </tr> * <tr> * <td> width </td><td> The column width in percentages, -1 will use the preferred size. -2 for width will take up the rest of the available space</td> * </tr> * <tr> * <td> height </td><td> Similar to width but doesn't support the -2 value</td> * </tr> * <tr> * <td> spanHorizontal </td><td> The cells that should be occupied horizontally defaults to 1 and can't exceed the column count - current offset.</td> * </tr> * <tr> * <td> spanVertical </td><td> Similar to spanHorizontal with the same limitations</td> * </tr> * <tr> * <td> horizontalAlign </td><td> The horizontal alignment of the content within the cell, defaults to the special case -1 value to take up all the cell space can be either `-1`, `Component.LEFT`, `Component.RIGHT` or `Component.CENTER`</td> * </tr> * <tr> * <td> verticalAlign </td><td> Similar to horizontalAlign can be one of `-1`, `Component.TOP`, `Component.BOTTOM` or `Component.CENTER`</td> * </tr> * </table> * * <p> Notice that you only need to set `width`/`height` to one cell in a column/row.<br> * The table layout constraint sample tries to demonstrate some of the unique things you can do with constraints.<br> * * We go into further details on this in the <a href="https://www.codenameone.com/manual/basics.html#_table_layout">developer guide</a> * so check that out.</p> * <script src="https://gist.github.com/codenameone/573f73164df4af00b7b1.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/table-layout-constraints.png" alt="TableLayout with complex constraints" /> * * @author Shai Almog */ public class TableLayout extends Layout { /** * Represents the layout constraint for an entry within the table indicating * the desired position/behavior of the component. */ public static class Constraint { private Component parent; private int row = -1; private int column = -1; private int width = defaultColumnWidth; private int height = defaultRowHeight; private int spanHorizontal = 1; private int spanVertical = 1; private int align = -1; private int valign = -1; int actualRow = -1; int actualColumn = -1; /** * {@inheritDoc} */ public String toString() { return "row: " + row + " column: " + column + " width: " + width + " height: " + height + " hspan: " + spanHorizontal + " vspan: " + spanVertical + " align " + align + " valign " + valign; } /** * Sets the cells to span vertically, this number must never be smaller than 1 * * @param span a number larger than 1 */ public void setVerticalSpan(int span) { if(span < 1) { throw new IllegalArgumentException("Illegal span"); } spanVertical = span; } /** * Sets the cells to span vertically, this number must never be smaller than 1 * * @param span a number larger than 1 * @return this */ public Constraint verticalSpan(int span) { setVerticalSpan(span); return this; } /** * Sets the cells to span horizontally, this number must never be smaller than 1 * * @param span a number larger than 1 */ public void setHorizontalSpan(int span) { if(span < 1) { throw new IllegalArgumentException("Illegal span"); } spanHorizontal = span; } /** * Sets the cells to span horizontally, this number must never be smaller than 1 * * @param span a number larger than 1 */ public Constraint horizontalSpan(int span) { setHorizontalSpan(span); return this; } /** * Sets the column width based on percentage of the parent * * @param width negative number indicates ignoring this member */ public void setWidthPercentage(int width) { this.width = width; } /** * Sets the column width based on percentage of the parent * * @param width negative number indicates ignoring this member */ public Constraint widthPercentage(int width) { this.width = width; return this; } /** * Sets the row height based on percentage of the parent * * @param height negative number indicates ignoring this member */ public void setHeightPercentage(int height) { this.height = height; } /** * Sets the row height based on percentage of the parent * * @param height negative number indicates ignoring this member */ public Constraint heightPercentage(int height) { this.height = height; return this; } /** * Sets the horizontal alignment of the table cell * * @param align Component.LEFT/RIGHT/CENTER */ public void setHorizontalAlign(int align) { this.align = align; } /** * Sets the horizontal alignment of the table cell * * @param align Component.LEFT/RIGHT/CENTER */ public Constraint horizontalAlign(int align) { this.align = align; return this; } /** * Sets the vertical alignment of the table cell * * @param valign Component.TOP/BOTTOM/CENTER */ public void setVerticalAlign(int valign) { this.valign = valign; } /** * Sets the vertical alignment of the table cell * * @param valign Component.TOP/BOTTOM/CENTER */ public Constraint verticalAlign(int valign) { this.valign = valign; return this; } /** * @return the row */ public int getRow() { return row; } /** * @return the column */ public int getColumn() { return column; } /** * @return the width */ public int getWidthPercentage() { return width; } /** * @return the height */ public int getHeightPercentage() { return height; } /** * @return the spanHorizontal */ public int getHorizontalSpan() { return spanHorizontal; } /** * @return the spanVertical */ public int getVerticalSpan() { return spanVertical; } /** * @return the align */ public int getHorizontalAlign() { return align; } /** * @return the valign */ public int getVerticalAlign() { return valign; } } private int currentRow; private int currentColumn; private static int minimumSizePerColumn = 10; private Constraint[] tablePositions; private int[] columnSizes; private int[] columnPositions; private int[] rowPositions; private boolean[] modifableColumnSize; /** * Special case marker SPAN constraint reserving place for other elements */ private static final Constraint H_SPAN_CONSTRAINT = new Constraint(); private static final Constraint V_SPAN_CONSTRAINT = new Constraint(); private static final Constraint VH_SPAN_CONSTRAINT = new Constraint(); private static int defaultColumnWidth = -1; private static int defaultRowHeight = -1; private boolean horizontalSpanningExists; private boolean verticalSpanningExists; private int rows, columns; private boolean growHorizontally; /** * A table must declare the amount of rows and columns in advance * * @param rows rows of the table * @param columns columns of the table */ public TableLayout(int rows, int columns) { this.rows = rows; this.columns = columns; tablePositions = new Constraint[rows * columns]; } /** * Get the number of rows * @return number of rows */ public int getRows() { return rows; } /** * Get the number of columns * @return number of columns */ public int getColumns() { return columns; } /** * Returns the component at the given row/column * * @param row the row of the component * @param column the column of the component * @return the component instance */ public Component getComponentAt(int row, int column) { int pos = row * columns + column; if(pos > -1 && pos < tablePositions.length) { Constraint c =tablePositions[pos]; return c != null ? c.parent : null; } return null; } /** * {@inheritDoc} */ public void layoutContainer(Container parent) { try { verticalSpanningExists = false; horizontalSpanningExists = false; // column and row size in pixels Style s = parent.getStyle(); int top = s.getPaddingTop(); int left = s.getPaddingLeft(parent.isRTL()); int bottom = s.getPaddingBottom(); int right = s.getPaddingRight(parent.isRTL()); boolean rtl = parent.isRTL(); columnSizes = new int[columns]; if(modifableColumnSize == null || columns != modifableColumnSize.length) { modifableColumnSize = new boolean[columns]; } columnPositions = new int[columns]; int[] rowSizes = new int[rows]; rowPositions = new int[rows]; int pWidth = parent.getLayoutWidth() - parent.getSideGap() - left - right; int pHeight = parent.getLayoutHeight() - parent.getBottomGap() - top - bottom; int currentX = left; int availableReminder = pWidth; int cslen = columnSizes.length; for(int iter = 0 ; iter < cslen ; iter++) { columnSizes[iter] = getColumnWidthPixels(iter, pWidth, availableReminder); availableReminder -= columnSizes[iter]; } // try to recalculate the columns for none horizontally scrollable tables // so they are distributed sensibly if no room is available if(!parent.isScrollableX()) { int totalWidth = 0; int totalModifyablePixels = 0; // check how many columns we can modify (the user hasn't requested a specific size for those) for(int iter = 0 ; iter < modifableColumnSize.length ; iter++) { if(modifableColumnSize[iter]) { totalModifyablePixels += columnSizes[iter]; } totalWidth += columnSizes[iter]; } if(pWidth < totalWidth) { int totalPixelsToRemove = totalWidth - pWidth; int totalPixelsNecessary = totalModifyablePixels - totalPixelsToRemove; // Go over the modifyable columns and remove the right pixels according to the ratio for(int iter = 0 ; iter < modifableColumnSize.length ; iter++) { if(modifableColumnSize[iter]) { columnSizes[iter] = (int)(((float)columnSizes[iter]) / ((float)totalModifyablePixels) * totalPixelsNecessary); } } } } for(int iter = 0 ; iter < columnSizes.length ; iter++) { if(rtl) { currentX += columnSizes[iter]; columnPositions[iter] = pWidth - currentX; } else { columnPositions[iter] = currentX; currentX += columnSizes[iter]; } } int currentY = top; int rlen = rowSizes.length; for(int iter = 0 ; iter < rlen ; iter++) { if(parent.isScrollableY()) { rowSizes[iter] = getRowHeightPixels(iter, pHeight, -1); } else { rowSizes[iter] = getRowHeightPixels(iter, pHeight, pHeight - currentY + top); } rowPositions[iter] = currentY; currentY += rowSizes[iter]; } int clen = columnSizes.length; for(int r = 0 ; r < rlen ; r++) { for(int c = 0 ; c < clen ; c++) { Constraint con = tablePositions[r * columns + c]; int conX, conY, conW, conH; if(con != null && con != H_SPAN_CONSTRAINT && con != V_SPAN_CONSTRAINT && con != VH_SPAN_CONSTRAINT) { Style componentStyle = con.parent.getStyle(); int leftMargin = componentStyle.getMarginLeft(parent.isRTL()); int topMargin = componentStyle.getMarginTop(); // conX = left + leftMargin + columnPositions[c]; // bugfix table with padding not drawn correctly // conY = top + topMargin + rowPositions[r]; // bugfix table with padding not drawn correctly conX = leftMargin + columnPositions[c]; conY = topMargin + rowPositions[r]; if(con.spanHorizontal > 1) { horizontalSpanningExists = true; int w = columnSizes[c]; for(int sh = 1 ; sh < con.spanHorizontal ; sh++) { w += columnSizes[Math.min(c + sh, columnSizes.length - 1)]; } // for RTL we need to move the component to the side so spanning will work if(rtl) { conX = left + leftMargin + columnPositions[c + con.spanHorizontal - 1]; } conW = w - leftMargin - componentStyle.getMarginLeft(parent.isRTL()); } else { conW = columnSizes[c] - leftMargin - componentStyle.getMarginRight(parent.isRTL()); } if(con.spanVertical > 1) { verticalSpanningExists = true; int h = rowSizes[r]; for(int sv = 1 ; sv < con.spanVertical ; sv++) { h += rowSizes[Math.min(r + sv, rowSizes.length - 1)]; } conH = h - topMargin - componentStyle.getMarginBottom(); } else { conH = rowSizes[r] - topMargin - componentStyle.getMarginBottom(); } placeComponent(rtl, con, conX, conY, conW, conH); } } } } catch(ArrayIndexOutOfBoundsException err) { Log.e(err); } } /** * Returns the position of the given table row. A valid value is only returned after the * layout occurred. * * @param row the row in the table * @return the Y position in pixels or -1 if layout hasn't occured/row is too large etc. */ public int getRowPosition(int row) { if(rowPositions != null && rowPositions.length > row) { return rowPositions[row]; } return -1; } /** * Returns the position of the given table column. A valid value is only returned after the * layout occurred. * * @param col the column in the table * @return the X position in pixels or -1 if layout hasn't occured/column is too large etc. */ public int getColumnPosition(int col) { if(columnPositions != null && columnPositions.length > col) { return columnPositions[col]; } return -1; } /** * Places the component/constraint in the proper alignment within the cell whose bounds are given */ private void placeComponent(boolean rtl, Constraint con, int x, int y, int width, int height) { con.parent.setX(x); con.parent.setY(y); con.parent.setWidth(width); con.parent.setHeight(height); Dimension pref = con.parent.getPreferredSize(); int pWidth = pref.getWidth(); int pHeight = pref.getHeight(); if(pWidth < width) { int d = (width - pWidth); int a = con.align; if(rtl) { switch(a) { case Component.LEFT: a = Component.RIGHT; break; case Component.RIGHT: a = Component.LEFT; break; } } switch(a) { case Component.LEFT: con.parent.setX(x); con.parent.setWidth(width - d); break; case Component.RIGHT: con.parent.setX(x + d); con.parent.setWidth(width - d); break; case Component.CENTER: con.parent.setX(x + d / 2); con.parent.setWidth(width - d); break; } } if(pHeight < height) { int d = (height - pHeight); switch(con.valign) { case Component.TOP: con.parent.setY(y); con.parent.setHeight(height - d); break; case Component.BOTTOM: con.parent.setY(y + d); con.parent.setHeight(height - d); break; case Component.CENTER: con.parent.setY(y + d / 2); con.parent.setHeight(height - d); break; } } } private int getColumnWidthPixels(int column, int percentageOf, int available) { int current = 0; if(modifableColumnSize == null) { modifableColumnSize = new boolean[columns]; } int availableSpaceColumn = -1; boolean foundExplicitWidth = false; for(int iter = 0 ; iter < rows ; iter++) { Constraint c = tablePositions[iter * columns + column]; if(c == null || c == H_SPAN_CONSTRAINT || c == V_SPAN_CONSTRAINT || c == VH_SPAN_CONSTRAINT || c.spanHorizontal > 1) { continue; } // width in percentage of the parent container if(c.width > 0 && available > -1) { current = Math.max(current, c.width * percentageOf / 100); foundExplicitWidth = true; modifableColumnSize[column] = false; } else if (!foundExplicitWidth) { // special case, width -2 gives the column the rest of the available space if(c.width == -2 || (growHorizontally && column == columns - 1)) { if(available < 0) { return Display.getInstance().getDisplayWidth(); } return available; } Style s = c.parent.getStyle(); current = Math.max(current, c.parent.getPreferredW() + s.getMarginLeftNoRTL() + s.getMarginRightNoRTL()); modifableColumnSize[column] = true; } if(available > -1) { current = Math.min(available, current); } } if(availableSpaceColumn > -1) { modifableColumnSize[availableSpaceColumn] = false; return percentageOf - current; } return current; } private int getRowHeightPixels(int row, int percentageOf, int available) { int current = 0; for(int iter = 0 ; iter < columns ; iter++) { Constraint c = tablePositions[row * columns + iter]; if(c == null || c == H_SPAN_CONSTRAINT || c == V_SPAN_CONSTRAINT || c == VH_SPAN_CONSTRAINT || c.spanVertical > 1) { continue; } // height in percentage of the parent container if(c.height > 0) { current = Math.max(current, c.height * percentageOf / 100); } else { Style s = c.parent.getStyle(); current = Math.max(current, c.parent.getPreferredH() + s.getMarginTop() + s.getMarginBottom()); } if(available > -1) { current = Math.min(available, current); } } return current; } /** * {@inheritDoc} */ public Dimension getPreferredSize(Container parent) { Style s = parent.getStyle(); int w = s.getPaddingLeftNoRTL() + s.getPaddingRightNoRTL(); int h = s.getPaddingTop() + s.getPaddingBottom(); int maxW = Display.getInstance().getDisplayWidth() * 2; int maxH = Display.getInstance().getDisplayHeight() * 2; for(int iter = 0 ; iter < columns ; iter++) { w += getColumnWidthPixels(iter, maxW, -1); } for(int iter = 0 ; iter < rows ; iter++) { h += getRowHeightPixels(iter, maxH, -1); } return new Dimension(w, h); } /** * Returns the row where the next operation of add will appear * * @return the row where the next operation of add will appear */ public int getNextRow() { return currentRow; } /** * Returns the column where the next operation of add will appear * * @return the column where the next operation of add will appear */ public int getNextColumn() { return currentColumn; } private void shiftCell(int row, int column) { Constraint currentConstraint = tablePositions[row * columns + column]; for(int iter = column + 1 ; iter < columns ; iter++) { if(tablePositions[row * columns + iter] != null) { Constraint tmp = tablePositions[row * columns + iter]; tablePositions[row * columns + iter] = currentConstraint; currentConstraint = tmp; } else { tablePositions[row * columns + iter] = currentConstraint; return; } } for(int rowIter = row + 1 ; rowIter < getRows() ; rowIter++) { for(int colIter = 0 ; colIter < getColumns() ; colIter++) { if(tablePositions[rowIter * columns + colIter] != null) { Constraint tmp = tablePositions[rowIter * columns + colIter]; tablePositions[rowIter * columns + colIter] = currentConstraint; currentConstraint = tmp; } else { tablePositions[rowIter * columns + colIter] = currentConstraint; return; } } } // if we reached this point there aren't enough rows addRow(); } private void addRow() { rows++; Constraint[] newArr = new Constraint[rows * columns]; System.arraycopy(tablePositions, 0, newArr, 0, tablePositions.length); tablePositions = newArr; } /** * {@inheritDoc} */ public void addLayoutComponent(Object value, Component comp, Container c) { Constraint con = null; if(!(value instanceof Constraint)) { con = createConstraint(); } else { con = (Constraint)value; if(con.parent != null) { Constraint con2 = createConstraint(); con2.align = con.align; con2.column = con.column; con2.height = con.height; con2.parent = c; con2.row = con.row; con2.spanHorizontal = con.spanHorizontal; con2.spanVertical = con.spanVertical; con2.valign = con.valign; con2.width = con.width; con = con2; } } con.actualRow = con.row; con.actualColumn = con.column; if(con.actualRow < 0) { con.actualRow = currentRow; } if(con.actualColumn < 0) { con.actualColumn = currentColumn; } con.parent = comp; if(con.actualRow >= rows) { // increase the table row count implicitly addRow(); } if(tablePositions[con.actualRow * columns + con.actualColumn] != null) { if(tablePositions[con.actualRow * columns + con.actualColumn].row != -1 || tablePositions[con.actualRow * columns + con.actualColumn].column != -1) { throw new IllegalArgumentException("Row: " + con.row + " and column: " + con.column + " already occupied"); } // try to reflow the table from this row/column onwards shiftCell(con.actualRow, con.actualColumn); tablePositions[con.actualRow * columns + con.actualColumn] = con; } tablePositions[con.actualRow * columns + con.actualColumn] = con; if(con.spanHorizontal > 1 || con.spanVertical > 1) { for(int sh = 0 ; sh < con.spanHorizontal ; sh++) { for(int sv = 0 ; sv < con.spanVertical ; sv++) { if((sh > 0 || sv > 0) && rows > con.actualRow + sv && columns > con.actualColumn + sh) { if(tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] == null) { if(con.spanHorizontal > 1) { if(con.spanVertical > 1) { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = VH_SPAN_CONSTRAINT; } else { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = V_SPAN_CONSTRAINT; } } else { tablePositions[(con.actualRow + sv) * columns + con.actualColumn + sh] = H_SPAN_CONSTRAINT; } } } } } } updateRowColumn(); } private void updateRowColumn() { if(currentRow >= rows) { return; } while(tablePositions[currentRow * columns + currentColumn] != null) { currentColumn++; if(currentColumn >= columns) { currentColumn = 0; currentRow++; if(currentRow >= rows) { return; } } } } /** * Returns the spanning for the table cell at the given coordinate * * @param row row in the table * @param column column within the table * @return the amount of spanning 1 for no spanning */ public int getCellHorizontalSpan(int row, int column) { return tablePositions[row * columns + column].spanHorizontal; } /** * Returns the spanning for the table cell at the given coordinate * * @param row row in the table * @param column column within the table * @return the amount of spanning 1 for no spanning */ public int getCellVerticalSpan(int row, int column) { return tablePositions[row * columns + column].spanVertical; } /** * Returns true if the cell at the given position is spanned through vertically * * @param row cell row * @param column cell column * @return true if the cell is a part of a span for another cell */ public boolean isCellSpannedThroughVertically(int row, int column) { return tablePositions[row * columns + column] == V_SPAN_CONSTRAINT || tablePositions[row * columns + column] == VH_SPAN_CONSTRAINT; } /** * Returns true if the cell at the given position is spanned through horizontally * * @param row cell row * @param column cell column * @return true if the cell is a part of a span for another cell */ public boolean isCellSpannedThroughHorizontally(int row, int column) { return tablePositions[row * columns + column] == H_SPAN_CONSTRAINT || tablePositions[row * columns + column] == VH_SPAN_CONSTRAINT; } /** * Indicates whether there is spanning within this layout * * @return true if the layout makes use of spanning */ public boolean hasVerticalSpanning() { return verticalSpanningExists; } /** * Indicates whether there is spanning within this layout * * @return true if the layout makes use of spanning */ public boolean hasHorizontalSpanning() { return horizontalSpanningExists; } /** * {@inheritDoc} */ public void removeLayoutComponent(Component comp) { // reflow the table Vector comps = new Vector(); for(int r = 0 ; r < rows ; r++) { for(int c = 0 ; c < columns ; c++) { if(tablePositions[r * columns + c] != null) { if(tablePositions[r * columns + c].parent != comp) { comps.addElement(tablePositions[r * columns + c]); } else { tablePositions[r * columns + c].parent = null; } } tablePositions[r * columns + c] = null; } } currentRow = 0; currentColumn = 0; int count = comps.size(); for(int iter = 0 ; iter < count ; iter++) { Constraint con = (Constraint)comps.elementAt(iter); if(con == H_SPAN_CONSTRAINT || con == V_SPAN_CONSTRAINT || con == VH_SPAN_CONSTRAINT) { continue; } Component c = con.parent; con.parent = null; addLayoutComponent(con, c, c.getParent()); } } /** * {@inheritDoc} */ public Object getComponentConstraint(Component comp) { for(int r = 0 ; r < rows ; r++) { for(int c = 0 ; c < columns ; c++) { if(tablePositions[r * columns + c] != null && tablePositions[r * columns + c].parent == comp) { return tablePositions[r * columns + c]; } } } return null; } /** * Creates a new Constraint instance to add to the layout * * @return the default constraint */ public Constraint createConstraint() { return new Constraint(); } /** * Creates a new Constraint instance to add to the layout * * @param row the row for the table starting with 0 * @param column the column for the table starting with 0 * @return the new constraint */ public Constraint createConstraint(int row, int column) { Constraint c = createConstraint(); c.row = row; c.column = column; return c; } /** * Sets the minimum size for a column in the table, this is applicable for tables that are * not scrollable on the X axis. This will force the earlier columns to leave room for * the latter columns. * * @param minimumSize the minimum width of the column */ public static void setMinimumSizePerColumn(int minimumSize) { minimumSizePerColumn = minimumSize; } /** * Indicates the minimum size for a column in the table, this is applicable for tables that are * not scrollable on the X axis. This will force the earlier columns to leave room for * the latter columns. * * @return the minimum width of the column */ public static int getMinimumSizePerColumn() { return minimumSizePerColumn; } /** * Indicates the default (in percentage) for the column width, -1 indicates * automatic sizing * * @param w width in percentage */ public static void setDefaultColumnWidth(int w) { defaultColumnWidth = w; } /** * Indicates the default (in percentage) for the column width, -1 indicates * automatic sizing * * @return width in percentage */ public static int getDefaultColumnWidth() { return defaultColumnWidth; } /** * Indicates the default (in percentage) for the row height, -1 indicates * automatic sizing * * @param h height in percentage */ public static void setDefaultRowHeight(int h) { defaultRowHeight = h; } /** * Indicates the default (in percentage) for the row height, -1 indicates * automatic sizing * * @return height in percentage */ public static int getDefaultRowHeight() { return defaultRowHeight; } /** * {@inheritDoc} */ public String toString() { return "TableLayout"; } /** * {@inheritDoc} */ public boolean equals(Object o) { return super.equals(o) && ((TableLayout)o).getRows() == getRows() && ((TableLayout)o).getColumns() == getColumns(); } /** * {@inheritDoc} */ public boolean isConstraintTracking() { return true; } /** * Indicates whether the table layout should grow horizontally to take up available space by stretching the last column * @return the growHorizontally */ public boolean isGrowHorizontally() { return growHorizontally; } /** * Indicates whether the table layout should grow horizontally to take up available space by stretching the last column * @param growHorizontally the growHorizontally to set */ public void setGrowHorizontally(boolean growHorizontally) { this.growHorizontally = growHorizontally; } /** * <p>Creates a table layout container that grows the last column horizontally, the number of rows is automatically * calculated based on the number of columns. See usage:</p> *<script src="https://gist.github.com/codenameone/2b4d9a13f409e297fb2e.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/table-layout-enclose.png" alt="TableLayout that grows the last column" /> * @param columns the number of columns * @param cmps components to add * @return a newly created table layout container with the components in it */ public static Container encloseIn(int columns, Component... cmps) { return encloseIn(columns, true, cmps); } /** * <p>Creates a table layout container, the number of rows is automatically calculated based on the number * of columns. See usage:</p> *<script src="https://gist.github.com/codenameone/2b4d9a13f409e297fb2e.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/table-layout-enclose.png" alt="TableLayout that grows the last column" /> * * @param columns the number of columns * @param growHorizontally true to grow the last column to fit available width * @param cmps components to add * @return a newly created table layout container with the components in it */ public static Container encloseIn(int columns, boolean growHorizontally, Component... cmps) { int rows = cmps.length; if(rows % columns > 0) { rows = rows / columns + 1; } else { rows = rows / columns; } TableLayout tl = new TableLayout(rows, columns); tl.setGrowHorizontally(growHorizontally); return Container.encloseIn(tl, cmps); } }