diff options
Diffstat (limited to 'dozentenmodul/src/main/java')
| -rw-r--r-- | dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridManager.java | 329 | ||||
| -rw-r--r-- | dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridPos.java | 5 |
2 files changed, 334 insertions, 0 deletions
diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridManager.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridManager.java new file mode 100644 index 00000000..8b941f6a --- /dev/null +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridManager.java @@ -0,0 +1,329 @@ +package org.openslx.dozmod.gui.helper; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.ArrayList; + +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JPanel; + +/** + * Helper class for using the GridBagLayout. + */ +public class GridManager { + + /** + * Setting this to true will insert green panels in cells where nothing was + * added + */ + public static boolean debugEmptyCells = false; + + private final JComponent container; + private final Insets defaultInsets; + private final int columnCount; + private final boolean strict; + private int nextColumn = 0; + private int currentRow = 0; + private boolean valid = true; + + private final ArrayList<Component[]> currentRows = new ArrayList<>(); + + private final GridBagConstraints mainConstraints = new GridBagConstraints(); + private GBC currentGbc = null; + + // Static general constraints + + private static final GridBagConstraints emptyFiller = new GridBagConstraints(0, 0, 1, 1, 0, 0, + GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + + /** + * Create a new GridManager for the given component. The manager will use + * strict mode and use an inset of 1px for every side of every cell. + * + * @param container The component to apply the layout to + * @param columnCount The number of columns per row + */ + public GridManager(JComponent container, int columnCount) { + this(container, columnCount, true); + } + + /** + * Create a new GridManager for the given component. The manager will use an + * inset of 1px for every side of every cell. + * + * @param container The component to apply the layout to + * @param columnCount The number of columns per row + * @param strict If true, the manager will ensure you call + * {@link #nextRow()} after finishing each row, and that you + * don't call it if the current row is empty. Otherwise, the + * manager will silently advance to the next row if the current + * row is full, and will ignore calls to {@link #nextRow()} if + * the current row is empty + */ + public GridManager(JComponent container, int columnCount, boolean strict) { + this(container, columnCount, strict, new Insets(1, 1, 1, 1)); + } + + /** + * Create a new GridManager for the given component. The manager will use an + * inset of 1px for every side of every cell. + * + * @param container The component to apply the layout to + * @param columnCount The number of columns per row + * @param strict If true, the manager will ensure you call + * {@link #nextRow()} after finishing each row, and that you + * don't call it if the current row is empty. Otherwise, the + * manager will silently advance to the next row if the current + * row is full, and will ignore calls to {@link #nextRow()} if + * the current row is empty + * @param defaultInsets an {@link Insets} instance to use for every cell by + * default + */ + public GridManager(JComponent container, int columnCount, boolean strict, Insets defaultInsets) { + this.defaultInsets = defaultInsets; + this.container = container; + this.columnCount = columnCount; + this.strict = strict; + this.container.setLayout(new GridBagLayout()); + this.currentRows.add(new Component[columnCount]); + } + + /** + * Add the given component to the next free grid cell. + * + * @param component Component to add + * @return A {@link GBC} instance that can be used to further influence the + * behavior of this component + * @throws IllegalArgumentException If there aren't enough columns left in + * the current row to add the given component with the desired + * horizontal span, or if one of the span parameters is + * <code>< 1</code> + */ + public GBC add(Component component) { + return add(component, 1, 1); + } + + /** + * Add the given component to the next free grid cell, applying the given + * horizontal and vertical cell-span. + * + * @param component Component to add + * @param spanX horizontal span + * @param spanY vertical span + * @return A {@link GBC} instance that can be used to further influence the + * behavior of this component + * @throws IllegalArgumentException If there aren't enough columns left in + * the current row to add the given component with the desired + * horizontal span, or if one of the span parameters is + * <code>< 1</code> + */ + public GBC add(Component component, int spanX, int spanY) { + checkValid(); + if (spanX < 1 || spanY < 1) + throw new IllegalArgumentException("Span must be >= 1"); + // Automatically advance to next row if strict mode is not enabled and we're at the end of the current one + if (!strict && nextColumn == columnCount) { + nextRow(); + } + if (!hasFreeColumns(spanX)) + throw new IllegalArgumentException("Cannot add component: Not enough columns left in row"); + addCurrentControl(); + currentGbc = new GBC(component, spanX, spanY); + nextColumn += spanX; + skipToFreeColumn(); + return currentGbc; + } + + /** + * Advance to next row. + * + * @throws IllegalStateException if strict mode is enabled and the current + * row is empty + */ + public void nextRow() { + checkValid(); + if (nextColumn == 0 && currentRows.size() == 1 && allCellsEmpty()) { + if (strict) + throw new IllegalStateException("Cannot call nextRow when current row is empty"); + return; + } + addCurrentControl(); + if (nextColumn < columnCount && debugEmptyCells) { + emptyFiller.gridy = currentRow; + Component[] row = currentRows.get(0); + for (int i = nextColumn; i < columnCount; ++i) { + if (row[i] != null) + continue; + JPanel p = new JPanel(); + p.setBackground(Color.GREEN); + emptyFiller.gridx = i; + container.add(p, emptyFiller); + } + } + currentRow++; + if (currentRows.size() == 1) { + Component[] row = currentRows.get(0); + for (int i = 0; i < columnCount; ++i) { + row[i] = null; + } + } else { + currentRows.remove(0); + } + nextColumn = 0; + skipToFreeColumn(); + } + + /** + * Finish the layout. Further calls to the <code>add</code>-methods will + * result in an exception being thrown + * + * @param addVerticalGlue Whether to add expanding vertical glue to the + * layout, so all components will be pushed to the top of the + * container + */ + public void finish(boolean addVerticalGlue) { + checkValid(); + if (nextColumn != 0) { + nextRow(); + } + if (addVerticalGlue) { + while (currentRows.size() > 1 || !allCellsEmpty()) { + nextRow(); + } + add(Box.createGlue(), columnCount, 1).expand(true, true).fill(true, true); + nextRow(); + } + valid = false; + } + + // Private helpers + + private boolean hasFreeColumns(int num) { + num--; + Component[] row = currentRows.get(0); + for (int i = nextColumn; i < columnCount; ++i) { + if (row[i] != null) + return false; + if (i - nextColumn >= num) + return true; + } + return false; + } + + private void addCurrentControl() { + if (currentGbc == null) + return; + container.add(currentGbc.component, mainConstraints); + // Remember placement for future sanity checks + for (int relrow = 0; relrow < mainConstraints.gridheight; ++relrow) { + if (currentRows.size() <= relrow) { + currentRows.add(new Component[columnCount]); + } + Component[] row = currentRows.get(relrow); + for (int relcol = 0; relcol < mainConstraints.gridwidth; ++relcol) { + if (row[relcol + mainConstraints.gridx] != null) + throw new IllegalStateException("Collision detected in cell (" + + (relcol + mainConstraints.gridx) + "|" + (relrow + mainConstraints.gridy) + + "): Have " + row[relcol + mainConstraints.gridx].getClass().getSimpleName() + + ", trying to add " + currentGbc.component.getClass().getSimpleName()); + row[relcol + mainConstraints.gridx] = currentGbc.component; + } + } + currentGbc.valid = false; + currentGbc = null; + } + + private boolean allCellsEmpty() { + Component[] row = currentRows.get(0); + for (int i = 0; i < columnCount; ++i) { + if (row[i] != null) + return false; + } + return true; + } + + private void checkValid() { + if (!valid) + throw new IllegalStateException("Layout is already finalized!"); + } + + private void skipToFreeColumn() { + Component[] row = currentRows.get(0); + while (nextColumn < columnCount && row[nextColumn] != null) { + nextColumn++; + } + } + + // + + public class GBC { + private boolean valid = true; + private final Component component; + + /** + * Set the fill properties of the element being added. + * + * @param fillX Fill the cell horizontally, enlarging the control + * @param fillY Fill the cell vertically, enlarging the control + * @return This instance, so calls can be chained + */ + public GBC fill(boolean fillX, boolean fillY) { + checkValid(); + if (fillX && fillY) { + mainConstraints.fill = GridBagConstraints.BOTH; + } else if (fillX) { + mainConstraints.fill = GridBagConstraints.HORIZONTAL; + } else if (fillY) { + mainConstraints.fill = GridBagConstraints.VERTICAL; + } + return this; + } + + /** + * Set the expand properties of the element being added. + * If multiple components are set to expand, they'll distribute the + * excess space among them. + * + * @param expandX Assign any remaining horizontal space to this cell + * @param expandY Assign any remaining vertical space to this cell + * @return This instance, so calls can be chained + */ + public GBC expand(boolean expandX, boolean expandY) { + checkValid(); + if (expandX) { + mainConstraints.weightx = 1; + } + if (expandY) { + mainConstraints.weighty = 1; + } + return this; + } + + // Extend with more helpers as needed + + private GBC(Component component, int spanX, int spanY) { + mainConstraints.gridx = nextColumn; + mainConstraints.gridy = currentRow; + mainConstraints.gridwidth = spanX; + mainConstraints.gridheight = spanY; + mainConstraints.weightx = 0; + mainConstraints.weighty = 0; + mainConstraints.anchor = GridBagConstraints.LINE_START; + mainConstraints.fill = GridBagConstraints.NONE; + mainConstraints.insets = defaultInsets; + mainConstraints.ipadx = 0; + mainConstraints.ipady = 0; + this.component = component; + } + + private void checkValid() { + if (!valid) + throw new IllegalAccessError("Cannot modify constraints after adding next control"); + } + } + +} diff --git a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridPos.java b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridPos.java index 82871fec..2a1b0200 100644 --- a/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridPos.java +++ b/dozentenmodul/src/main/java/org/openslx/dozmod/gui/helper/GridPos.java @@ -3,6 +3,11 @@ package org.openslx.dozmod.gui.helper; import java.awt.GridBagConstraints; import java.awt.Insets; +/** + * Helper for creating Constraints when using GridBagLayout. + * Deprecated, use GridManager instead. + */ +@Deprecated public class GridPos { private static final Insets inset = new Insets(1, 1, 1, 1); |
