diff --git a/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/GridEngine.java b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/GridEngine.java new file mode 100644 index 000000000..6304f3ac6 --- /dev/null +++ b/constraintlayout/core/src/main/java/androidx/constraintlayout/core/utils/GridEngine.java @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * 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 androidx.constraintlayout.core.utils; + +import java.util.Arrays; + +/** + * GridEngine class contains the main logic of the Grid Helper + */ +public class GridEngine { + + public static final int VERTICAL = 1; + public static final int HORIZONTAL = 0; + private static final int MAX_ROWS = 50; // maximum number of rows can be specified. + private static final int MAX_COLUMNS = 50; // maximum number of columns can be specified. + private static final int DEFAULT_SIZE = 3; // default rows and columns. + + /** + * number of rows of the grid + */ + private int mRows; + + /** + * number of rows set by the XML or API + */ + private int mRowsSet; + + /** + * How many widgets need to be placed in the Grid + */ + private int mNumWidgets; + + /** + * number of columns of the grid + */ + private int mColumns; + + /** + * number of columns set by the XML or API + */ + private int mColumnsSet; + + /** + * string format of the input Spans + */ + private String mStrSpans; + + /** + * string format of the input Skips + */ + private String mStrSkips; + + /** + * orientation of the view arrangement - vertical or horizontal + */ + private int mOrientation; + + /** + * Indicates what is the next available position to place an widget + */ + private int mNextAvailableIndex = 0; + + /** + * A boolean matrix that tracks the positions that are occupied by skips and spans + * true: available position + * false: non-available position + */ + private boolean[][] mPositionMatrix; + + /** + * A int matrix that contains the positions where a widget would constraint to at each direction + * Each row contains 4 values that indicate the position to constraint of a widget. + * Example row: [left, top, right, bottom] + */ + private int[][] mConstraintMatrix; + + public GridEngine() {} + + public GridEngine(int rows, int columns) { + mRowsSet = rows; + mColumnsSet = columns; + if (rows > MAX_ROWS) { + mRowsSet = DEFAULT_SIZE; + } + + if (columns > MAX_COLUMNS) { + mColumnsSet = DEFAULT_SIZE; + } + + updateActualRowsAndColumns(); + initVariables(); + } + + public GridEngine(int rows, int columns, int numWidgets) { + mRowsSet = rows; + mColumnsSet = columns; + mNumWidgets = numWidgets; + + if (rows > MAX_ROWS) { + mRowsSet = DEFAULT_SIZE; + } + + if (columns > MAX_COLUMNS) { + mColumnsSet = DEFAULT_SIZE; + } + + updateActualRowsAndColumns(); + + if (numWidgets > mRows * mColumns || numWidgets < 1) { + mNumWidgets = mRows * mColumns; + } + + initVariables(); + fillConstraintMatrix(false); + } + + /** + * Initialize the relevant variables + */ + private void initVariables() { + mPositionMatrix = new boolean[mRows][mColumns]; + for (boolean[] row : mPositionMatrix) { + Arrays.fill(row, true); + } + + if (mNumWidgets > 0) { + mConstraintMatrix = new int[mNumWidgets][4]; + for (int[] row : mConstraintMatrix) { + Arrays.fill(row, -1); + } + } + } + + /** + * Convert a 1D index to a 2D index that has index for row and index for column + * + * @param index index in 1D + * @return row as its values. + */ + private int getRowByIndex(int index) { + if (mOrientation == 1) { + return index % mRows; + + } else { + return index / mColumns; + } + } + + /** + * Convert a 1D index to a 2D index that has index for row and index for column + * + * @param index index in 1D + * @return column as its values. + */ + private int getColByIndex(int index) { + if (mOrientation == 1) { + return index / mRows; + } else { + return index % mColumns; + } + } + + /** + * Check if the value of the spans/skips is valid + * + * @param str spans/skips in string format + * @return true if it is valid else false + */ + private boolean isSpansValid(CharSequence str) { + return true; + } + + /** + * parse the skips/spans in the string format into a int matrix + * that each row has the information - [index, row_span, col_span] + * the format of the input string is index:row_spanxcol_span. + * index - the index of the starting position + * row_span - the number of rows to span + * col_span- the number of columns to span + * + * @param str string format of skips or spans + * @return a int matrix that contains skip information. + */ + private int[][] parseSpans(String str) { + if (!isSpansValid(str)) { + return null; + } + + String[] spans = str.split(","); + int[][] spanMatrix = new int[spans.length][3]; + + String[] indexAndSpan; + String[] rowAndCol; + for (int i = 0; i < spans.length; i++) { + indexAndSpan = spans[i].trim().split(":"); + rowAndCol = indexAndSpan[1].split("x"); + spanMatrix[i][0] = Integer.parseInt(indexAndSpan[0]); + spanMatrix[i][1] = Integer.parseInt(rowAndCol[0]); + spanMatrix[i][2] = Integer.parseInt(rowAndCol[1]); + } + return spanMatrix; + } + + /** + * fill the constraintMatrix based on the input attributes + * + * @param isUpdate whether to update the existing grid (true) or create a new one (false) + */ + private void fillConstraintMatrix(boolean isUpdate) { + if (isUpdate) { + for (int i = 0; i < mPositionMatrix.length; i++) { + for (int j = 0; j < mPositionMatrix[0].length; j++) { + mPositionMatrix[i][j] = true; + } + } + + for (int i = 0; i < mConstraintMatrix.length; i++) { + for (int j = 0; j < mConstraintMatrix[0].length; j++) { + mConstraintMatrix[i][j] = -1; + } + } + } + + mNextAvailableIndex = 0; + + if (mStrSkips != null && !mStrSkips.trim().isEmpty()) { + int[][] mSkips = parseSpans(mStrSkips); + if (mSkips != null) { + handleSkips(mSkips); + } + } + + if (mStrSpans != null && !mStrSpans.trim().isEmpty()) { + int[][] mSpans = parseSpans(mStrSpans); + if (mSpans != null) { + handleSpans(mSpans); + } + } + + addAllConstraintPositions(); + } + + /** + * Get the next available position for widget arrangement. + * @return int[] -> [row, column] + */ + private int getNextPosition() { + int position = 0; + boolean positionFound = false; + + while (!positionFound) { + if (mNextAvailableIndex >= mRows * mColumns) { + return -1; + } + + position = mNextAvailableIndex; + int row = getRowByIndex(mNextAvailableIndex); + int col = getColByIndex(mNextAvailableIndex); + if (mPositionMatrix[row][col]) { + mPositionMatrix[row][col] = false; + positionFound = true; + } + + mNextAvailableIndex++; + } + return position; + } + + /** + * add the constraint position info of a widget based on the input params + * + * @param widgetId the Id of the widget + * @param row row position to place the view + * @param column column position to place the view + */ + private void addConstraintPosition(int widgetId, int row, int column, + int rowSpan, int columnSpan) { + + mConstraintMatrix[widgetId][0] = column; + mConstraintMatrix[widgetId][1] = row; + mConstraintMatrix[widgetId][2] = column + columnSpan - 1; + mConstraintMatrix[widgetId][3] = row + rowSpan - 1; + } + + /** + * Handle the span use cases + * + * @param spansMatrix a int matrix that contains span information + */ + private void handleSpans(int[][] spansMatrix) { + for (int i = 0; i < spansMatrix.length; i++) { + int row = getRowByIndex(spansMatrix[i][0]); + int col = getColByIndex(spansMatrix[i][0]); + if (!invalidatePositions(row, col, + spansMatrix[i][1], spansMatrix[i][2])) { + return; + } + addConstraintPosition(i, row, col, + spansMatrix[i][1], spansMatrix[i][2]); + } + } + + /** + * Make positions in the grid unavailable based on the skips attr + * + * @param skipsMatrix a int matrix that contains skip information + */ + private void handleSkips(int[][] skipsMatrix) { + for (int i = 0; i < skipsMatrix.length; i++) { + int row = getRowByIndex(skipsMatrix[i][0]); + int col = getColByIndex(skipsMatrix[i][0]); + if (!invalidatePositions(row, col, + skipsMatrix[i][1], skipsMatrix[i][2])) { + return; + } + } + } + + /** + * Make the specified positions in the grid unavailable. + * + * @param startRow the row of the staring position + * @param startColumn the column of the staring position + * @param rowSpan how many rows to span + * @param columnSpan how many columns to span + * @return true if we could properly invalidate the positions else false + */ + private boolean invalidatePositions(int startRow, int startColumn, + int rowSpan, int columnSpan) { + for (int i = startRow; i < startRow + rowSpan; i++) { + for (int j = startColumn; j < startColumn + columnSpan; j++) { + if (i >= mPositionMatrix.length || j >= mPositionMatrix[0].length + || !mPositionMatrix[i][j]) { + // the position is already occupied. + return false; + } + mPositionMatrix[i][j] = false; + } + } + return true; + } + + /** + * Arrange the views in the constraint_referenced_ids + */ + private void addAllConstraintPositions() { + int position; + + for (int i = 0; i < mNumWidgets; i++) { + + // Already added ConstraintPosition + if (leftOfWidget(i) != -1) { + continue; + } + + position = getNextPosition(); + int row = getRowByIndex(position); + int col = getColByIndex(position); + if (position == -1) { + // no more available position. + return; + } + addConstraintPosition(i, row, col, 1, 1); + } + } + + /** + * Compute the actual rows and columns given what was set + * if 0,0 find the most square rows and columns that fits + * if 0,n or n,0 scale to fit + */ + private void updateActualRowsAndColumns() { + if (mRowsSet == 0 || mColumnsSet == 0) { + if (mColumnsSet > 0) { + mColumns = mColumnsSet; + mRows = (mNumWidgets + mColumns - 1) / mColumnsSet; // round up + } else if (mRowsSet > 0) { + mRows = mRowsSet; + mColumns = (mNumWidgets + mRowsSet - 1) / mRowsSet; // round up + } else { // as close to square as possible favoring more rows + mRows = (int) (1.5 + Math.sqrt(mNumWidgets)); + mColumns = (mNumWidgets + mRows - 1) / mRows; + } + } else { + mRows = mRowsSet; + mColumns = mColumnsSet; + } + } + + /** + * Set up the Grid engine. + */ + public void setup() { + boolean isUpdate = true; + + if (mConstraintMatrix == null + || mConstraintMatrix.length != mNumWidgets + || mPositionMatrix == null + || mPositionMatrix.length != mRows + || mPositionMatrix[0].length != mColumns) { + isUpdate = false; + } + + if (!isUpdate) { + initVariables(); + } + + fillConstraintMatrix(isUpdate); + } + + /** + * set new spans value + * + * @param spans new spans value + */ + public void setSpans(CharSequence spans) { + if (mStrSpans != null && mStrSpans.equals(spans)) { + return; + } + + mStrSpans = spans.toString(); + } + + /** + * set new skips value + * + * @param skips new spans value + */ + public void setSkips(String skips) { + if (mStrSkips != null && mStrSkips.equals(skips)) { + return; + } + + mStrSkips = skips; + + } + + /** + * set new orientation value + * + * @param orientation new orientation value + */ + public void setOrientation(int orientation) { + if (!(orientation == HORIZONTAL || orientation == VERTICAL)) { + return; + } + + if (mOrientation == orientation) { + return; + } + + mOrientation = orientation; + } + + /** + * Set new NumWidgets value + * @param num how man widgets to be arranged in Grid + */ + public void setNumWidgets(int num) { + if (num > mRows * mColumns) { + return; + } + + mNumWidgets = num; + } + + /** + * set new rows value + * + * @param rows new rows value + */ + public void setRows(int rows) { + if (rows > MAX_ROWS) { + return; + } + + if (mRowsSet == rows) { + return; + } + + mRowsSet = rows; + updateActualRowsAndColumns(); + + } + + /** + * set new columns value + * + * @param columns new rows value + */ + public void setColumns(int columns) { + if (columns > MAX_COLUMNS) { + return; + } + + if (mColumnsSet == columns) { + return; + } + + mColumnsSet = columns; + updateActualRowsAndColumns(); + } + + /** + * Get the boxView for the widget i to add a constraint on the left + * + * @param i the widget that has the order as i in the constraint_reference_ids + * @return the boxView to add a constraint on the left + */ + public int leftOfWidget(int i) { + if (mConstraintMatrix == null || i >= mConstraintMatrix.length) { + return 0; + } + return mConstraintMatrix[i][0]; + } + + /** + * Get the boxView for the widget i to add a constraint on the top + * + * @param i the widget that has the order as i in the constraint_reference_ids + * @return the boxView to add a constraint on the top + */ + public int topOfWidget(int i) { + if (mConstraintMatrix == null || i >= mConstraintMatrix.length) { + return 0; + } + return mConstraintMatrix[i][1]; + } + + /** + * Get the boxView for the widget i to add a constraint on the right + * + * @param i the widget that has the order as i in the constraint_reference_ids + * @return the boxView to add a constraint on the right + */ + public int rightOfWidget(int i) { + if (mConstraintMatrix == null || i >= mConstraintMatrix.length) { + return 0; + } + return mConstraintMatrix[i][2]; + } + + /** + * Get the boxView for the widget i to add a constraint on the bottom + * + * @param i the widget that has the order as i in the constraint_reference_ids + * @return the boxView to add a constraint on the bottom + */ + public int bottomOfWidget(int i) { + if (mConstraintMatrix == null || i >= mConstraintMatrix.length) { + return 0; + } + return mConstraintMatrix[i][3]; + } +} diff --git a/constraintlayout/core/src/test/java/androidx/constraintlayout/core/utils/GridEngineTest.java b/constraintlayout/core/src/test/java/androidx/constraintlayout/core/utils/GridEngineTest.java new file mode 100644 index 000000000..d770c73f6 --- /dev/null +++ b/constraintlayout/core/src/test/java/androidx/constraintlayout/core/utils/GridEngineTest.java @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * 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 androidx.constraintlayout.core; + +import static org.junit.Assert.assertEquals; + +import androidx.constraintlayout.core.utils.GridEngine; + +import org.junit.Test; + +public class GridEngineTest { + + @Test + public void testNoArgument() { + GridEngine engine = new GridEngine(); + engine.setRows(3); + engine.setColumns(3); + engine.setNumWidgets(5); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(0, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(0, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + + } + + @Test + public void testSimpleEngineHorizontal() { + GridEngine engine = new GridEngine(3, 3, 5); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(0, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(0, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testSimpleEngineVertical() { + // Grid engine with 3 rows, 3 columns, and 5 widgets + GridEngine engine = new GridEngine(3, 3, 5); + // Set the orientation to Vertical + engine.setOrientation(1); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(0, engine.leftOfWidget(1)); + assertEquals(1, engine.topOfWidget(1)); + assertEquals(0, engine.rightOfWidget(1)); + assertEquals(1, engine.bottomOfWidget(1)); + + assertEquals(0, engine.leftOfWidget(2)); + assertEquals(2, engine.topOfWidget(2)); + assertEquals(0, engine.rightOfWidget(2)); + assertEquals(2, engine.bottomOfWidget(2)); + + assertEquals(1, engine.leftOfWidget(3)); + assertEquals(0, engine.topOfWidget(3)); + assertEquals(1, engine.rightOfWidget(3)); + assertEquals(0, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testSpansSkipsHorizontal() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setSkips("0:2x2,6:1x1"); + engine.setSpans("12:1x3"); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(3, engine.topOfWidget(0)); + assertEquals(2, engine.rightOfWidget(0)); + assertEquals(3, engine.bottomOfWidget(0)); + + assertEquals(2, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(2, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(3, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(3, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(3, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(3, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(0, engine.leftOfWidget(4)); + assertEquals(2, engine.topOfWidget(4)); + assertEquals(0, engine.rightOfWidget(4)); + assertEquals(2, engine.bottomOfWidget(4)); + } + + @Test + public void testSpansSkipsVertical() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setOrientation(1); + engine.setSkips("0:2x2,6:2x1"); + engine.setSpans("12:3x1"); + engine.setup(); + + assertEquals(3, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(3, engine.rightOfWidget(0)); + assertEquals(2, engine.bottomOfWidget(0)); + + assertEquals(0, engine.leftOfWidget(1)); + assertEquals(2, engine.topOfWidget(1)); + assertEquals(0, engine.rightOfWidget(1)); + assertEquals(2, engine.bottomOfWidget(1)); + + assertEquals(0, engine.leftOfWidget(2)); + assertEquals(3, engine.topOfWidget(2)); + assertEquals(0, engine.rightOfWidget(2)); + assertEquals(3, engine.bottomOfWidget(2)); + + assertEquals(2, engine.leftOfWidget(3)); + assertEquals(0, engine.topOfWidget(3)); + assertEquals(2, engine.rightOfWidget(3)); + assertEquals(0, engine.bottomOfWidget(3)); + + assertEquals(2, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(2, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testSetRows() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setRows(3); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(3, engine.leftOfWidget(3)); + assertEquals(0, engine.topOfWidget(3)); + assertEquals(3, engine.rightOfWidget(3)); + assertEquals(0, engine.bottomOfWidget(3)); + + assertEquals(0, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(0, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testSetColumns() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setColumns(3); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(0, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(0, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testSetNumWidgets() { + GridEngine engine = new GridEngine(3, 3); + engine.setNumWidgets(5); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(0, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(0, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testToggleSkips() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setSkips("0:2x2,6:1x1"); + engine.setup(); + + assertEquals(2, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(2, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(3, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(3, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(3, engine.leftOfWidget(2)); + assertEquals(1, engine.topOfWidget(2)); + assertEquals(3, engine.rightOfWidget(2)); + assertEquals(1, engine.bottomOfWidget(2)); + + assertEquals(0, engine.leftOfWidget(3)); + assertEquals(2, engine.topOfWidget(3)); + assertEquals(0, engine.rightOfWidget(3)); + assertEquals(2, engine.bottomOfWidget(3)); + + assertEquals(1, engine.leftOfWidget(4)); + assertEquals(2, engine.topOfWidget(4)); + assertEquals(1, engine.rightOfWidget(4)); + assertEquals(2, engine.bottomOfWidget(4)); + + engine.setSkips(""); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(3, engine.leftOfWidget(3)); + assertEquals(0, engine.topOfWidget(3)); + assertEquals(3, engine.rightOfWidget(3)); + assertEquals(0, engine.bottomOfWidget(3)); + + assertEquals(0, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(0, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } + + @Test + public void testToggleSpans() { + GridEngine engine = new GridEngine(4, 4, 5); + engine.setSpans("0:2x2,3:2x1"); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(1, engine.rightOfWidget(0)); + assertEquals(1, engine.bottomOfWidget(0)); + + assertEquals(3, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(3, engine.rightOfWidget(1)); + assertEquals(1, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(2, engine.leftOfWidget(3)); + assertEquals(1, engine.topOfWidget(3)); + assertEquals(2, engine.rightOfWidget(3)); + assertEquals(1, engine.bottomOfWidget(3)); + + assertEquals(0, engine.leftOfWidget(4)); + assertEquals(2, engine.topOfWidget(4)); + assertEquals(0, engine.rightOfWidget(4)); + assertEquals(2, engine.bottomOfWidget(4)); + + engine.setSpans(""); + engine.setup(); + + assertEquals(0, engine.leftOfWidget(0)); + assertEquals(0, engine.topOfWidget(0)); + assertEquals(0, engine.rightOfWidget(0)); + assertEquals(0, engine.bottomOfWidget(0)); + + assertEquals(1, engine.leftOfWidget(1)); + assertEquals(0, engine.topOfWidget(1)); + assertEquals(1, engine.rightOfWidget(1)); + assertEquals(0, engine.bottomOfWidget(1)); + + assertEquals(2, engine.leftOfWidget(2)); + assertEquals(0, engine.topOfWidget(2)); + assertEquals(2, engine.rightOfWidget(2)); + assertEquals(0, engine.bottomOfWidget(2)); + + assertEquals(3, engine.leftOfWidget(3)); + assertEquals(0, engine.topOfWidget(3)); + assertEquals(3, engine.rightOfWidget(3)); + assertEquals(0, engine.bottomOfWidget(3)); + + assertEquals(0, engine.leftOfWidget(4)); + assertEquals(1, engine.topOfWidget(4)); + assertEquals(0, engine.rightOfWidget(4)); + assertEquals(1, engine.bottomOfWidget(4)); + } +}