@Override public TerminalSize getPreferredSize(List<Component> components) { TerminalSize preferredSize = TerminalSize.ZERO; if (components.isEmpty()) { return preferredSize.withRelative( leftMarginSize + rightMarginSize, topMarginSize + bottomMarginSize); } Component[][] table = buildTable(components); table = eliminateUnusedRowsAndColumns(table); // Figure out each column first, this can be done independently of the row heights int preferredWidth = 0; int preferredHeight = 0; for (int width : getPreferredColumnWidths(table)) { preferredWidth += width; } for (int height : getPreferredRowHeights(table)) { preferredHeight += height; } preferredSize = preferredSize.withRelative(preferredWidth, preferredHeight); preferredSize = preferredSize.withRelativeColumns( leftMarginSize + rightMarginSize + (table[0].length - 1) * horizontalSpacing); preferredSize = preferredSize.withRelativeRows( topMarginSize + bottomMarginSize + (table.length - 1) * verticalSpacing); return preferredSize; }
@Override public void scrollLines(int firstLine, int lastLine, int distance) { if (firstLine < 0) { firstLine = 0; } if (lastLine >= size.getRows()) { lastLine = size.getRows() - 1; } if (firstLine < lastLine) { if (distance > 0) { // scrolling up: start with first line as target: int curLine = firstLine; // copy lines from further "below": for (; curLine <= lastLine - distance; curLine++) { buffer[curLine] = buffer[curLine + distance]; } // blank out the remaining lines: for (; curLine <= lastLine; curLine++) { buffer[curLine] = newBlankLine(); } } else if (distance < 0) { // scrolling down: start with last line as target: int curLine = lastLine; distance = -distance; // copy lines from further "above": for (; curLine >= firstLine + distance; curLine--) { buffer[curLine] = buffer[curLine - distance]; } // blank out the remaining lines: for (; curLine >= firstLine; curLine--) { buffer[curLine] = newBlankLine(); } } /* else: distance == 0 => no-op */ } }
private void buttonMoveCursorActionPerformed( java.awt.event.ActionEvent evt) { // GEN-FIRST:event_buttonMoveCursorActionPerformed TerminalSize terminalSize = scrollingSwingTerminal.getTerminalSize(); Random random = new Random(); scrollingSwingTerminal.setCursorPosition( random.nextInt(terminalSize.getColumns()), random.nextInt(terminalSize.getRows())); scrollingSwingTerminal.flush(); } // GEN-LAST:event_buttonMoveCursorActionPerformed
@Override public void copyTo( TextImage destination, int startRowIndex, int rows, int startColumnIndex, int columns, int destinationRowOffset, int destinationColumnOffset) { // If the source image position is negative, offset the whole image if (startColumnIndex < 0) { destinationColumnOffset += -startColumnIndex; columns += startColumnIndex; startColumnIndex = 0; } if (startRowIndex < 0) { startRowIndex += -startRowIndex; rows = startRowIndex; startRowIndex = 0; } // Make sure we can't copy more than is available columns = Math.min(buffer[0].length - startColumnIndex, columns); rows = Math.min(buffer.length - startRowIndex, rows); // Adjust target lengths as well columns = Math.min(destination.getSize().getColumns() - destinationColumnOffset, columns); rows = Math.min(destination.getSize().getRows() - destinationRowOffset, rows); if (columns <= 0 || rows <= 0) { return; } TerminalSize destinationSize = destination.getSize(); if (destination instanceof BasicTextImage) { int targetRow = destinationRowOffset; for (int y = startRowIndex; y < startRowIndex + rows && targetRow < destinationSize.getRows(); y++) { System.arraycopy( buffer[y], startColumnIndex, ((BasicTextImage) destination).buffer[targetRow++], destinationColumnOffset, columns); } } else { // Manually copy character by character for (int y = startRowIndex; y < startRowIndex + rows; y++) { for (int x = startColumnIndex; x < startColumnIndex + columns; x++) { destination.setCharacterAt( x - startColumnIndex + destinationColumnOffset, y - startRowIndex + destinationRowOffset, buffer[y][x]); } } } }
@Override public void onAdded(WindowBasedTextGUI textGUI, Window window, List<Window> allWindows) { WindowDecorationRenderer decorationRenderer = getWindowDecorationRenderer(window); TerminalSize expectedDecoratedSize = decorationRenderer.getDecoratedSize(window, window.getPreferredSize()); window.setDecoratedSize(expectedDecoratedSize); if (window.getHints().contains(Window.Hint.FIXED_POSITION)) { // Don't place the window, assume the position is already set } else if (allWindows.isEmpty()) { window.setPosition(TerminalPosition.OFFSET_1x1); } else if (window.getHints().contains(Window.Hint.CENTERED)) { int left = (lastKnownScreenSize.getColumns() - expectedDecoratedSize.getColumns()) / 2; int top = (lastKnownScreenSize.getRows() - expectedDecoratedSize.getRows()) / 2; window.setPosition(new TerminalPosition(left, top)); } else { TerminalPosition nextPosition = allWindows.get(allWindows.size() - 1).getPosition().withRelative(2, 1); if (nextPosition.getColumn() + expectedDecoratedSize.getColumns() > lastKnownScreenSize.getColumns() || nextPosition.getRow() + expectedDecoratedSize.getRows() > lastKnownScreenSize.getRows()) { nextPosition = TerminalPosition.OFFSET_1x1; } window.setPosition(nextPosition); } // Finally, run through the usual calculations so the window manager's usual prepare method can // have it's say prepareWindow(lastKnownScreenSize, window); }
@Override public TextGraphics drawImage( TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize) { // If the source image position is negative, offset the whole image if (sourceImageTopLeft.getColumn() < 0) { topLeft = topLeft.withRelativeColumn(-sourceImageTopLeft.getColumn()); sourceImageSize = sourceImageSize.withRelativeColumns(sourceImageTopLeft.getColumn()); sourceImageTopLeft = sourceImageTopLeft.withColumn(0); } if (sourceImageTopLeft.getRow() < 0) { topLeft = topLeft.withRelativeRow(-sourceImageTopLeft.getRow()); sourceImageSize = sourceImageSize.withRelativeRows(sourceImageTopLeft.getRow()); sourceImageTopLeft = sourceImageTopLeft.withRow(0); } // cropping specified image-subrectangle to the image itself: int fromRow = Math.max(sourceImageTopLeft.getRow(), 0); int untilRow = Math.min( sourceImageTopLeft.getRow() + sourceImageSize.getRows(), image.getSize().getRows()); int fromColumn = Math.max(sourceImageTopLeft.getColumn(), 0); int untilColumn = Math.min( sourceImageTopLeft.getColumn() + sourceImageSize.getColumns(), image.getSize().getColumns()); // difference between position in image and position on target: int diffRow = topLeft.getRow() - sourceImageTopLeft.getRow(); int diffColumn = topLeft.getColumn() - sourceImageTopLeft.getColumn(); // top/left-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative // coordinate) fromRow = Math.max(fromRow, -diffRow); fromColumn = Math.max(fromColumn, -diffColumn); // bot/right-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative // coordinate) untilRow = Math.min(untilRow, getSize().getRows() - diffRow); untilColumn = Math.min(untilColumn, getSize().getColumns() - diffColumn); if (fromRow >= untilRow || fromColumn >= untilColumn) { return this; } for (int row = fromRow; row < untilRow; row++) { for (int column = fromColumn; column < untilColumn; column++) { setCharacter(column + diffColumn, row + diffRow, image.getCharacterAt(column, row)); } } return this; }
@Override public void drawComponent(TextGUIGraphics graphics) { TerminalSize area = graphics.getSize(); String absolutePath = directory.getAbsolutePath(); if (area.getColumns() < absolutePath.length()) { absolutePath = absolutePath.substring(absolutePath.length() - area.getColumns()); absolutePath = "..." + absolutePath.substring(Math.min(absolutePath.length(), 3)); } setText(absolutePath); super.drawComponent(graphics); }
@Override public BasicTextImage resize(TerminalSize newSize, TextCharacter filler) { if (newSize == null || filler == null) { throw new IllegalArgumentException( "Cannot resize BasicTextImage with null " + (newSize == null ? "newSize" : "filler")); } if (newSize.getRows() == buffer.length && (buffer.length == 0 || newSize.getColumns() == buffer[0].length)) { return this; } return new BasicTextImage(newSize, buffer, filler); }
@Override public TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException { TerminalSize writableArea = getSize(); if (topLeftCorner.getColumn() + size.getColumns() <= 0 || topLeftCorner.getColumn() >= writableArea.getColumns() || topLeftCorner.getRow() + size.getRows() <= 0 || topLeftCorner.getRow() >= writableArea.getRows()) { // The area selected is completely outside of this TextGraphics, so we can return a "null" // object that doesn't // do anything because it is impossible to change anything anyway return new NullTextGraphics(size); } return new SubTextGraphics(this, topLeftCorner, size); }
public TextBox(TerminalSize preferredSize) { this( preferredSize, (preferredSize != null && preferredSize.getRows() > 1) ? Style.MULTI_LINE : Style.SINGLE_LINE); }
@Override public TerminalSize getPreferredSize(final ComboBox<V> comboBox) { TerminalSize size = TerminalSize.ONE.withColumns( (comboBox.getItemCount() == 0 ? CJKUtils.getColumnWidth(comboBox.getText()) : 0) + 2); synchronized (comboBox) { for (int i = 0; i < comboBox.getItemCount(); i++) { V item = comboBox.getItem(i); size = size.max( new TerminalSize( CJKUtils.getColumnWidth(item.toString()) + 2 + 1, 1)); // +1 to add a single column of space } } return size; }
public TextBox(TerminalSize preferredSize, String initialContent) { this( preferredSize, initialContent, (preferredSize != null && preferredSize.getRows() > 1) || initialContent.contains("\n") ? Style.MULTI_LINE : Style.SINGLE_LINE); }
@Override public String toString() { StringBuilder sb = new StringBuilder(size.getRows() * (size.getColumns() + 1) + 50); sb.append('{') .append(size.getColumns()) .append('x') .append(size.getRows()) .append('}') .append('\n'); for (TextCharacter[] line : buffer) { for (TextCharacter tc : line) { sb.append(tc.getCharacter()); } sb.append('\n'); } return sb.toString(); }
private int shrinkWidthToFitArea(TerminalSize area, int[] columnWidths) { int totalWidth = 0; for (int width : columnWidths) { totalWidth += width; } if (totalWidth > area.getColumns()) { int columnOffset = 0; do { if (columnWidths[columnOffset] > 0) { columnWidths[columnOffset]--; totalWidth--; } if (++columnOffset == numberOfColumns) { columnOffset = 0; } } while (totalWidth > area.getColumns()); } return totalWidth; }
private int shrinkHeightToFitArea(TerminalSize area, int[] rowHeights) { int totalHeight = 0; for (int height : rowHeights) { totalHeight += height; } if (totalHeight > area.getRows()) { int rowOffset = 0; do { if (rowHeights[rowOffset] > 0) { rowHeights[rowOffset]--; totalHeight--; } if (++rowOffset == rowHeights.length) { rowOffset = 0; } } while (totalHeight > area.getRows()); } return totalHeight; }
private int grabExtraVerticalSpace( TerminalSize area, int[] rowHeights, Set<Integer> expandableRows, int totalHeight) { for (int rowIndex : expandableRows) { rowHeights[rowIndex]++; totalHeight++; if (area.getColumns() == totalHeight) { break; } } return totalHeight; }
private int grabExtraHorizontalSpace( TerminalSize area, int[] columnWidths, Set<Integer> expandableColumns, int totalWidth) { for (int columnIndex : expandableColumns) { columnWidths[columnIndex]++; totalWidth++; if (area.getColumns() == totalWidth) { break; } } return totalWidth; }
/** * Creates a new BasicTextImage by copying a region of a two-dimensional array of TextCharacter:s. * If the area to be copied to larger than the source array, a filler character is used. * * @param size Size to create the new BasicTextImage as (and size to copy from the array) * @param toCopy Array to copy initial data from * @param initialContent Filler character to use if the source array is smaller than the requested * size */ private BasicTextImage( TerminalSize size, TextCharacter[][] toCopy, TextCharacter initialContent) { if (size == null || toCopy == null || initialContent == null) { throw new IllegalArgumentException( "Cannot create BasicTextImage with null " + (size == null ? "size" : (toCopy == null ? "toCopy" : "filler"))); } this.size = size; int rows = size.getRows(); int columns = size.getColumns(); buffer = new TextCharacter[rows][]; for (int y = 0; y < rows; y++) { buffer[y] = new TextCharacter[columns]; for (int x = 0; x < columns; x++) { if (y < toCopy.length && x < toCopy[y].length) { buffer[y][x] = toCopy[y][x]; } else { buffer[y][x] = initialContent; } } } }
@Override public void doLayout(TerminalSize area, List<Component> components) { EnumMap<Location, Component> layout = makeLookupMap(components); int availableHorizontalSpace = area.getColumns(); int availableVerticalSpace = area.getRows(); // We'll need this later on int topComponentHeight = 0; int leftComponentWidth = 0; // First allocate the top if (layout.containsKey(Location.TOP)) { Component topComponent = layout.get(Location.TOP); topComponentHeight = Math.min(topComponent.getPreferredSize().getRows(), availableVerticalSpace); topComponent.setPosition(TerminalPosition.TOP_LEFT_CORNER); topComponent.setSize(new TerminalSize(availableHorizontalSpace, topComponentHeight)); availableVerticalSpace -= topComponentHeight; } // Next allocate the bottom if (layout.containsKey(Location.BOTTOM)) { Component bottomComponent = layout.get(Location.BOTTOM); int bottomComponentHeight = Math.min(bottomComponent.getPreferredSize().getRows(), availableVerticalSpace); bottomComponent.setPosition(new TerminalPosition(0, area.getRows() - bottomComponentHeight)); bottomComponent.setSize(new TerminalSize(availableHorizontalSpace, bottomComponentHeight)); availableVerticalSpace -= bottomComponentHeight; } // Now divide the remaining space between LEFT, CENTER and RIGHT if (layout.containsKey(Location.LEFT)) { Component leftComponent = layout.get(Location.LEFT); leftComponentWidth = Math.min(leftComponent.getPreferredSize().getColumns(), availableHorizontalSpace); leftComponent.setPosition(new TerminalPosition(0, topComponentHeight)); leftComponent.setSize(new TerminalSize(leftComponentWidth, availableVerticalSpace)); availableHorizontalSpace -= leftComponentWidth; } if (layout.containsKey(Location.RIGHT)) { Component rightComponent = layout.get(Location.RIGHT); int rightComponentWidth = Math.min(rightComponent.getPreferredSize().getColumns(), availableHorizontalSpace); rightComponent.setPosition( new TerminalPosition(area.getColumns() - rightComponentWidth, topComponentHeight)); rightComponent.setSize(new TerminalSize(rightComponentWidth, availableVerticalSpace)); availableHorizontalSpace -= rightComponentWidth; } if (layout.containsKey(Location.CENTER)) { Component centerComponent = layout.get(Location.CENTER); centerComponent.setPosition(new TerminalPosition(leftComponentWidth, topComponentHeight)); centerComponent.setSize(new TerminalSize(availableHorizontalSpace, availableVerticalSpace)); } // Set the remaining components to 0x0 for (Component component : components) { if (!layout.values().contains(component)) { component.setPosition(TerminalPosition.TOP_LEFT_CORNER); component.setSize(TerminalSize.ZERO); } } }
private TextCharacter[] newBlankLine() { TextCharacter[] line = new TextCharacter[size.getColumns()]; Arrays.fill(line, TextCharacter.DEFAULT_CHARACTER); return line; }
/** * Called by {@link DefaultWindowManager} when iterating through all windows to decide their size * and position. If you override {@link DefaultWindowManager} to add your own logic to how windows * are placed on the screen, you can override this method and selectively choose which window to * interfere with. Note that the two key properties that are read by the GUI system after * preparing all windows are the position and decorated size. Your custom implementation should * set these two fields directly on the window. You can infer the decorated size from the content * size by using the window decoration renderer that is attached to the window manager. * * @param screenSize Size of the terminal that is available to draw on * @param window Window to prepare decorated size and position for */ protected void prepareWindow(TerminalSize screenSize, Window window) { WindowDecorationRenderer decorationRenderer = getWindowDecorationRenderer(window); TerminalSize contentAreaSize; if (window.getHints().contains(Window.Hint.FIXED_SIZE)) { contentAreaSize = window.getSize(); } else { contentAreaSize = window.getPreferredSize(); } TerminalSize size = decorationRenderer.getDecoratedSize(window, contentAreaSize); TerminalPosition position = window.getPosition(); if (window.getHints().contains(Window.Hint.FULL_SCREEN)) { position = TerminalPosition.TOP_LEFT_CORNER; size = screenSize; } else if (window.getHints().contains(Window.Hint.EXPANDED)) { position = TerminalPosition.OFFSET_1x1; size = screenSize.withRelative( -Math.min(4, screenSize.getColumns()), -Math.min(3, screenSize.getRows())); if (!size.equals(window.getDecoratedSize())) { window.invalidate(); } } else if (window.getHints().contains(Window.Hint.FIT_TERMINAL_WINDOW) || window.getHints().contains(Window.Hint.CENTERED)) { // If the window is too big for the terminal, move it up towards 0x0 and if that's not enough // then shrink // it instead while (position.getRow() > 0 && position.getRow() + size.getRows() > screenSize.getRows()) { position = position.withRelativeRow(-1); } while (position.getColumn() > 0 && position.getColumn() + size.getColumns() > screenSize.getColumns()) { position = position.withRelativeColumn(-1); } if (position.getRow() + size.getRows() > screenSize.getRows()) { size = size.withRows(screenSize.getRows() - position.getRow()); } if (position.getColumn() + size.getColumns() > screenSize.getColumns()) { size = size.withColumns(screenSize.getColumns() - position.getColumn()); } if (window.getHints().contains(Window.Hint.CENTERED)) { int left = (lastKnownScreenSize.getColumns() - size.getColumns()) / 2; int top = (lastKnownScreenSize.getRows() - size.getRows()) / 2; position = new TerminalPosition(left, top); } } window.setPosition(position); window.setDecoratedSize(size); }
public FileDialog( String title, String description, String actionLabel, TerminalSize dialogSize, boolean showHiddenFilesAndDirs, File selectedObject) { super(title); this.selectedFile = null; this.showHiddenFilesAndDirs = showHiddenFilesAndDirs; if (selectedObject == null || !selectedObject.exists()) { selectedObject = new File("").getAbsoluteFile(); } selectedObject = selectedObject.getAbsoluteFile(); Panel contentPane = new Panel(); contentPane.setLayoutManager(new GridLayout(2)); if (description != null) { new Label(description) .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.BEGINNING, GridLayout.Alignment.CENTER, false, false, 2, 1)) .addTo(contentPane); } int unitWidth = dialogSize.getColumns() / 3; int unitHeight = dialogSize.getRows(); new FileSystemLocationLabel() .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.FILL, GridLayout.Alignment.CENTER, true, false, 2, 1)) .addTo(contentPane); fileListBox = new ActionListBox(new TerminalSize(unitWidth * 2, unitHeight)); fileListBox .withBorder(Borders.singleLine()) .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.BEGINNING, GridLayout.Alignment.CENTER, false, false)) .addTo(contentPane); directoryListBox = new ActionListBox(new TerminalSize(unitWidth, unitHeight)); directoryListBox.withBorder(Borders.singleLine()).addTo(contentPane); fileBox = new TextBox() .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.FILL, GridLayout.Alignment.CENTER, true, false, 2, 1)) .addTo(contentPane); new Separator(Direction.HORIZONTAL) .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.FILL, GridLayout.Alignment.CENTER, true, false, 2, 1)) .addTo(contentPane); okButton = new Button(actionLabel, new OkHandler()); Panels.grid(2, okButton, new Button("Cancel", new CancelHandler())) .setLayoutData( GridLayout.createLayoutData( GridLayout.Alignment.END, GridLayout.Alignment.CENTER, false, false, 2, 1)) .addTo(contentPane); if (selectedObject.isFile()) { directory = selectedObject.getParentFile(); fileBox.setText(selectedObject.getName()); } else if (selectedObject.isDirectory()) { directory = selectedObject; } reloadViews(directory); setComponent(contentPane); }
@Override public void doLayout(TerminalSize area, List<Component> components) { // Sanity check, if the area is way too small, just return Component[][] table = buildTable(components); table = eliminateUnusedRowsAndColumns(table); if (area.equals(TerminalSize.ZERO) || table.length == 0 || area.getColumns() <= leftMarginSize + rightMarginSize + ((table[0].length - 1) * horizontalSpacing) || area.getRows() <= bottomMarginSize + topMarginSize + ((table.length - 1) * verticalSpacing)) { return; } // Adjust area to the margins area = area.withRelative(-leftMarginSize - rightMarginSize, -topMarginSize - bottomMarginSize); Map<Component, TerminalSize> sizeMap = new IdentityHashMap<Component, TerminalSize>(); Map<Component, TerminalPosition> positionMap = new IdentityHashMap<Component, TerminalPosition>(); // Figure out each column first, this can be done independently of the row heights int[] columnWidths = getPreferredColumnWidths(table); // Take notes of which columns we can expand if the usable area is larger than what the // components want Set<Integer> expandableColumns = getExpandableColumns(table); // Next, start shrinking to make sure it fits the size of the area we are trying to lay out on. // Notice we subtract the horizontalSpacing to take the space between components into account TerminalSize areaWithoutHorizontalSpacing = area.withRelativeColumns(-horizontalSpacing * (table[0].length - 1)); int totalWidth = shrinkWidthToFitArea(areaWithoutHorizontalSpacing, columnWidths); // Finally, if there is extra space, make the expandable columns larger while (areaWithoutHorizontalSpacing.getColumns() > totalWidth && !expandableColumns.isEmpty()) { totalWidth = grabExtraHorizontalSpace( areaWithoutHorizontalSpacing, columnWidths, expandableColumns, totalWidth); } // Now repeat for rows int[] rowHeights = getPreferredRowHeights(table); Set<Integer> expandableRows = getExpandableRows(table); TerminalSize areaWithoutVerticalSpacing = area.withRelativeRows(-verticalSpacing * (table.length - 1)); int totalHeight = shrinkHeightToFitArea(areaWithoutVerticalSpacing, rowHeights); while (areaWithoutVerticalSpacing.getRows() > totalHeight && !expandableRows.isEmpty()) { totalHeight = grabExtraVerticalSpace( areaWithoutVerticalSpacing, rowHeights, expandableRows, totalHeight); } // Ok, all constraints are in place, we can start placing out components. To simplify, do it // horizontally first // and vertically after TerminalPosition tableCellTopLeft = TerminalPosition.TOP_LEFT_CORNER; for (int y = 0; y < table.length; y++) { tableCellTopLeft = tableCellTopLeft.withColumn(0); for (int x = 0; x < table[y].length; x++) { Component component = table[y][x]; if (component != null && !positionMap.containsKey(component)) { GridLayoutData layoutData = getLayoutData(component); TerminalSize size = component.getPreferredSize(); TerminalPosition position = tableCellTopLeft; int availableHorizontalSpace = 0; int availableVerticalSpace = 0; for (int i = 0; i < layoutData.horizontalSpan; i++) { availableHorizontalSpace += columnWidths[x + i] + (i > 0 ? horizontalSpacing : 0); } for (int i = 0; i < layoutData.verticalSpan; i++) { availableVerticalSpace += rowHeights[y + i] + (i > 0 ? verticalSpacing : 0); } // Make sure to obey the size restrictions size = size.withColumns(Math.min(size.getColumns(), availableHorizontalSpace)); size = size.withRows(Math.min(size.getRows(), availableVerticalSpace)); switch (layoutData.horizontalAlignment) { case CENTER: position = position.withRelativeColumn((availableHorizontalSpace - size.getColumns()) / 2); break; case END: position = position.withRelativeColumn(availableHorizontalSpace - size.getColumns()); break; case FILL: size = size.withColumns(availableHorizontalSpace); break; default: break; } switch (layoutData.verticalAlignment) { case CENTER: position = position.withRelativeRow((availableVerticalSpace - size.getRows()) / 2); break; case END: position = position.withRelativeRow(availableVerticalSpace - size.getRows()); break; case FILL: size = size.withRows(availableVerticalSpace); break; default: break; } sizeMap.put(component, size); positionMap.put(component, position); } tableCellTopLeft = tableCellTopLeft.withRelativeColumn(columnWidths[x] + horizontalSpacing); } tableCellTopLeft = tableCellTopLeft.withRelativeRow(rowHeights[y] + verticalSpacing); } // Apply the margins here for (Component component : components) { component.setPosition(positionMap.get(component).withRelative(leftMarginSize, topMarginSize)); component.setSize(sizeMap.get(component)); } this.changed = false; }