From 789c89dcf2da8044497c322d1351d026de2ceb0c Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Tue, 17 Jul 2018 16:33:35 -0700 Subject: [PATCH 01/16] ui: Add Table Component --- .../ui/overlay/components/TableComponent.java | 351 ++++++++++++++++++ .../components/TableComponentTest.java | 81 ++++ 2 files changed, 432 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java create mode 100644 runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java new file mode 100644 index 0000000000..9424c34791 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.overlay.components; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.util.ArrayList; +import javax.annotation.Nonnull; + +public class TableComponent implements LayoutableRenderableEntity +{ + public enum TableAlignment + { + LEFT, + CENTER, + RIGHT + } + + private ArrayList cells = new ArrayList<>(); + private TableAlignment[] columnAlignments; + private Color[] columnColors; + private int numCols; + private int numRows; + + private TableAlignment defaultAlignment = TableAlignment.LEFT; + private Color defaultColor = Color.WHITE; + private Dimension gutter = new Dimension(3, 0); + private Point preferredLocation = new Point(); + private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); + + @Override + public Dimension render(final Graphics2D graphics) + { + final FontMetrics metrics = graphics.getFontMetrics(); + final int[] columnWidths = getColumnWidths(metrics); + int height = 0; + + graphics.translate(preferredLocation.x, preferredLocation.y); + + for (int row = 0; row < numRows; row++) + { + int x = 0; + int startingRowHeight = height; + for (int col = 0; col < numCols; col++) + { + int y = startingRowHeight; + final String[] lines = lineBreakText(getCellText(col, row), columnWidths[col], metrics); + for (String line : lines) + { + final int alignmentOffset = getAlignedPosition(line, getColumnAlignment(col), columnWidths[col], metrics); + final TextComponent leftLineComponent = new TextComponent(); + y += metrics.getHeight(); + + leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); + leftLineComponent.setText(line); + leftLineComponent.setColor(getColumnColor(col)); + leftLineComponent.render(graphics); + } + height = Math.max(height, y); + x += columnWidths[col] + gutter.width; + } + height += gutter.height; + } + + graphics.translate(-preferredLocation.x, -preferredLocation.y); + return new Dimension(preferredSize.width, height); + } + + public void setDefaultColor(@Nonnull final Color color) + { + this.defaultColor = color; + } + + public void setDefaultAlignment(@Nonnull final TableAlignment alignment) + { + this.defaultAlignment = alignment; + } + + public void setGutter(@Nonnull final Dimension gutter) + { + this.gutter = gutter; + } + + @Override + public void setPreferredLocation(@Nonnull final Point location) + { + this.preferredLocation = location; + } + + @Override + public void setPreferredSize(@Nonnull final Dimension size) + { + this.preferredSize = size; + } + + public void setColumnColors(@Nonnull Color... colors) + { + columnColors = colors; + } + + public void setColumnAlignments(@Nonnull TableAlignment... alignments) + { + columnAlignments = alignments; + } + + public void setColumnColor(final int col, final Color color) + { + assert columnColors.length > col; + columnColors[col] = color; + } + + public void setColumnAlignment(final int col, final TableAlignment alignment) + { + assert columnAlignments.length > col; + columnAlignments[col] = alignment; + } + + public void addRow(@Nonnull final String... cells) + { + numCols = Math.max(numCols, cells.length); + numRows++; + this.cells.add(cells); + } + + public void addRows(@Nonnull final String[]... rows) + { + for (String[] row : rows) + { + addRow(row); + } + } + + private Color getColumnColor(final int column) + { + if (columnColors == null + || columnColors.length <= column + || columnColors[column] == null) + { + return defaultColor; + } + return columnColors[column]; + } + + private TableAlignment getColumnAlignment(final int column) + { + if (columnAlignments == null + || columnAlignments.length <= column + || columnAlignments[column] == null) + { + return defaultAlignment; + } + return columnAlignments[column]; + } + + private String getCellText(final int col, final int row) + { + assert col < numCols && row < numRows; + + if (cells.get(row).length < col) + { + return ""; + } + return cells.get(row)[col]; + } + + private int[] getColumnWidths(final FontMetrics metrics) + { + // Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width + int[] maxtextw = new int[numCols]; // max text width over all rows + int[] maxwordw = new int[numCols]; // max width of longest word + boolean[] flex = new boolean[numCols]; // is column flexible? + boolean[] wrap = new boolean[numCols]; // can column be wrapped? + int[] finalcolw = new int[numCols]; // final width of columns + + for (int col = 0; col < numCols; col++) + { + for (int row = 0; row < numRows; row++) + { + final String cell = getCellText(col, row); + final int cellWidth = getTextWidth(metrics, cell); + + maxtextw[col] = Math.max(maxtextw[col], cellWidth); + for (String word : cell.split(" ")) + { + maxwordw[col] = Math.max(maxwordw[col], getTextWidth(metrics, word)); + } + + if (maxtextw[col] == cellWidth) + { + wrap[col] = cell.contains(" "); + } + } + } + + int left = preferredSize.width - (numCols - 1) * gutter.width; + final double avg = left / numCols; + int nflex = 0; + + // Determine whether columns should be flexible and assign width of non-flexible cells + for (int col = 0; col < numCols; col++) + { + // This limit can be adjusted as needed + final double maxNonFlexLimit = 1.5 * avg; + + flex[col] = maxtextw[col] > maxNonFlexLimit; + if (flex[col]) + { + nflex++; + } + else + { + finalcolw[col] = maxtextw[col]; + left -= finalcolw[col]; + } + } + + // If there is not enough space, make columns that could be word-wrapped flexible too + if (left < nflex * avg) + { + for (int col = 0; col < numCols; col++) + { + if (!flex[col] && wrap[col]) + { + left += finalcolw[col]; + finalcolw[col] = 0; + flex[col] = true; + nflex++; + } + } + } + + // Calculate weights for flexible columns. The max width is capped at the table width to + // treat columns that have to be wrapped more or less equal + int tot = 0; + for (int col = 0; col < numCols; col++) + { + if (flex[col]) + { + maxtextw[col] = Math.min(maxtextw[col], preferredSize.width); + tot += maxtextw[col]; + } + } + + // Now assign the actual width for flexible columns. Make sure that it is at least as long + // as the longest word length + for (int col = 0; col < numCols; col++) + { + if (flex[col]) + { + finalcolw[col] = left * maxtextw[col] / tot; + finalcolw[col] = Math.max(finalcolw[col], maxwordw[col]); + left -= finalcolw[col]; + } + } + + // When the sum of column widths is less than the total space available, distribute the + // extra space equally across all columns + final int extraPerCol = left / numCols; + for (int col = 0; col < numCols; col++) + { + finalcolw[col] += extraPerCol; + left -= extraPerCol; + } + // Add any remainder to the right-most column + finalcolw[finalcolw.length - 1] += left; + + return finalcolw; + } + + private static int getTextWidth(final FontMetrics metrics, final String cell) + { + return metrics.stringWidth(TextComponent.textWithoutColTags(cell)); + } + + private static String[] lineBreakText(final String text, final int maxWidth, final FontMetrics metrics) + { + final String[] words = text.split(" "); + + if (words.length == 0) + { + return new String[0]; + } + + final StringBuilder wrapped = new StringBuilder(words[0]); + int spaceLeft = maxWidth - getTextWidth(metrics, wrapped.toString()); + + for (int i = 1; i < words.length; i++) + { + final String word = words[i]; + final int wordLen = getTextWidth(metrics, word); + final int spaceWidth = metrics.stringWidth(" "); + + if (wordLen + spaceWidth > spaceLeft) + { + wrapped.append("\n").append(word); + spaceLeft = maxWidth - wordLen; + } + else + { + wrapped.append(" ").append(word); + spaceLeft -= spaceWidth + wordLen; + } + } + + return wrapped.toString().split("\n"); + } + + private static int getAlignedPosition(final String str, final TableAlignment alignment, final int columnWidth, final FontMetrics metrics) + { + final int stringWidth = getTextWidth(metrics, str); + int offset = 0; + + switch (alignment) + { + case LEFT: + break; + case CENTER: + offset = (columnWidth / 2) - (stringWidth / 2); + break; + case RIGHT: + offset = columnWidth - stringWidth; + break; + } + return offset; + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java new file mode 100644 index 0000000000..404f4f0ff4 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.overlay.components; + +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import net.runelite.client.ui.overlay.components.TableComponent.TableAlignment; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; +import org.mockito.Mock; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class TableComponentTest +{ + @Mock + private Graphics2D graphics; + + @Before + public void before() + { + when(graphics.getFontMetrics()).thenReturn(mock(FontMetrics.class)); + } + + @Test + public void testRender() + { + TableComponent tableComponent = new TableComponent(); + tableComponent.addRow("test"); + tableComponent.setDefaultAlignment(TableAlignment.CENTER); + tableComponent.setDefaultColor(Color.RED); + tableComponent.render(graphics); + verify(graphics, times(2)).drawString(eq("test"), anyInt(), anyInt()); + verify(graphics, atLeastOnce()).setColor(Color.RED); + } + + @Test + public void testColors() + { + TableComponent tableComponent = new TableComponent(); + tableComponent.addRow("test", "test", "test", "test", "test"); + tableComponent.setColumnColors(Color.RED, Color.GREEN, Color.BLUE); + tableComponent.render(graphics); + verify(graphics, atLeastOnce()).setColor(Color.RED); + verify(graphics, atLeastOnce()).setColor(Color.GREEN); + verify(graphics, atLeastOnce()).setColor(Color.BLUE); + verify(graphics, atLeastOnce()).setColor(Color.YELLOW); + verify(graphics, atLeastOnce()).setColor(Color.WHITE); + } +} From 8595713d3dafb9f21032f8830eae8314ea9497bb Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Tue, 30 Apr 2019 23:34:31 -0700 Subject: [PATCH 02/16] Add needed `bounds` variable and utilize setter annonations --- .../ui/overlay/components/TableComponent.java | 71 +++++++------------ 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java index 9424c34791..7286320ea6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Jordan Atwood + * Copyright (c) 2019, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,9 +30,14 @@ import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; +import java.awt.Rectangle; import java.util.ArrayList; import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +@Setter public class TableComponent implements LayoutableRenderableEntity { public enum TableAlignment @@ -41,16 +47,29 @@ public class TableComponent implements LayoutableRenderableEntity RIGHT } + @Getter + private final Rectangle bounds = new Rectangle(); + + @Setter(AccessLevel.NONE) private ArrayList cells = new ArrayList<>(); - private TableAlignment[] columnAlignments; - private Color[] columnColors; + @Nonnull + private TableAlignment[] columnAlignments = new TableAlignment[0]; + @Nonnull + private Color[] columnColors = new Color[0]; + @Setter(AccessLevel.NONE) private int numCols; + @Setter(AccessLevel.NONE) private int numRows; + @Nonnull private TableAlignment defaultAlignment = TableAlignment.LEFT; + @Nonnull private Color defaultColor = Color.WHITE; + @Nonnull private Dimension gutter = new Dimension(3, 0); + @Nonnull private Point preferredLocation = new Point(); + @Nonnull private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); @Override @@ -88,44 +107,10 @@ public class TableComponent implements LayoutableRenderableEntity } graphics.translate(-preferredLocation.x, -preferredLocation.y); - return new Dimension(preferredSize.width, height); - } - - public void setDefaultColor(@Nonnull final Color color) - { - this.defaultColor = color; - } - - public void setDefaultAlignment(@Nonnull final TableAlignment alignment) - { - this.defaultAlignment = alignment; - } - - public void setGutter(@Nonnull final Dimension gutter) - { - this.gutter = gutter; - } - - @Override - public void setPreferredLocation(@Nonnull final Point location) - { - this.preferredLocation = location; - } - - @Override - public void setPreferredSize(@Nonnull final Dimension size) - { - this.preferredSize = size; - } - - public void setColumnColors(@Nonnull Color... colors) - { - columnColors = colors; - } - - public void setColumnAlignments(@Nonnull TableAlignment... alignments) - { - columnAlignments = alignments; + final Dimension dimension = new Dimension(preferredSize.width, height); + bounds.setLocation(preferredLocation); + bounds.setSize(dimension); + return dimension; } public void setColumnColor(final int col, final Color color) @@ -157,8 +142,7 @@ public class TableComponent implements LayoutableRenderableEntity private Color getColumnColor(final int column) { - if (columnColors == null - || columnColors.length <= column + if (columnColors.length <= column || columnColors[column] == null) { return defaultColor; @@ -168,8 +152,7 @@ public class TableComponent implements LayoutableRenderableEntity private TableAlignment getColumnAlignment(final int column) { - if (columnAlignments == null - || columnAlignments.length <= column + if (columnAlignments.length <= column || columnAlignments[column] == null) { return defaultAlignment; From c1dddcb88171268fa0c3724f6ebb12f7a466fcd3 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Tue, 30 Apr 2019 23:48:41 -0700 Subject: [PATCH 03/16] Expand row and col styling via sub components --- .../components/table/TableAlignment.java | 8 + .../{ => table}/TableComponent.java | 151 +++++++++++++----- .../components/table/TableElement.java | 38 +++++ .../ui/overlay/components/table/TableRow.java | 38 +++++ 4 files changed, 194 insertions(+), 41 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java rename runelite-client/src/main/java/net/runelite/client/ui/overlay/components/{ => table}/TableComponent.java (68%) create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java create mode 100644 runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java new file mode 100644 index 0000000000..d7fd5ed87e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java @@ -0,0 +1,8 @@ +package net.runelite.client.ui.overlay.components.table; + +public enum TableAlignment +{ + LEFT, + CENTER, + RIGHT +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java similarity index 68% rename from runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java rename to runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 7286320ea6..63acf66389 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -23,7 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.ui.overlay.components; +package net.runelite.client.ui.overlay.components.table; import java.awt.Color; import java.awt.Dimension; @@ -32,34 +32,27 @@ import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.List; import javax.annotation.Nonnull; -import lombok.AccessLevel; +import javax.annotation.Nullable; import lombok.Getter; import lombok.Setter; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; +import net.runelite.client.ui.overlay.components.TextComponent; @Setter public class TableComponent implements LayoutableRenderableEntity { - public enum TableAlignment - { - LEFT, - CENTER, - RIGHT - } + private static final TableRow EMPTY_ROW = TableRow.builder().build(); + private static final TableElement EMPTY_ELEMENT = TableElement.builder().build(); @Getter private final Rectangle bounds = new Rectangle(); - @Setter(AccessLevel.NONE) - private ArrayList cells = new ArrayList<>(); @Nonnull - private TableAlignment[] columnAlignments = new TableAlignment[0]; - @Nonnull - private Color[] columnColors = new Color[0]; - @Setter(AccessLevel.NONE) - private int numCols; - @Setter(AccessLevel.NONE) - private int numRows; + private TableElement[] tableColumns = new TableElement[0]; + private final List tableRows = new ArrayList<>(); @Nonnull private TableAlignment defaultAlignment = TableAlignment.LEFT; @@ -81,6 +74,9 @@ public class TableComponent implements LayoutableRenderableEntity graphics.translate(preferredLocation.x, preferredLocation.y); + final int numRows = tableRows.size(); + final int numCols = tableColumns.length; + for (int row = 0; row < numRows; row++) { int x = 0; @@ -91,13 +87,16 @@ public class TableComponent implements LayoutableRenderableEntity final String[] lines = lineBreakText(getCellText(col, row), columnWidths[col], metrics); for (String line : lines) { - final int alignmentOffset = getAlignedPosition(line, getColumnAlignment(col), columnWidths[col], metrics); + final TableAlignment alignment = getCellAlignment(row, col); + final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[col], metrics); final TextComponent leftLineComponent = new TextComponent(); y += metrics.getHeight(); + final Color lineColor = getCellColor(row, col); + leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); leftLineComponent.setText(line); - leftLineComponent.setColor(getColumnColor(col)); + leftLineComponent.setColor(lineColor); leftLineComponent.render(graphics); } height = Math.max(height, y); @@ -115,21 +114,28 @@ public class TableComponent implements LayoutableRenderableEntity public void setColumnColor(final int col, final Color color) { - assert columnColors.length > col; - columnColors[col] = color; + assert tableColumns.length > col; + tableColumns[col].setColor(color); } public void setColumnAlignment(final int col, final TableAlignment alignment) { - assert columnAlignments.length > col; - columnAlignments[col] = alignment; + assert tableColumns.length > col; + tableColumns[col].setAlignment(alignment); } public void addRow(@Nonnull final String... cells) { - numCols = Math.max(numCols, cells.length); - numRows++; - this.cells.add(cells); + final TableElement[] elements = new TableElement[cells.length]; + for (int i = 0; i < cells.length; i++) + { + elements[i] = TableElement.builder().content(cells[i]).build(); + } + + final TableRow row = TableRow.builder().build(); + row.setElements(elements); + + this.tableRows.add(row); } public void addRows(@Nonnull final String[]... rows) @@ -140,39 +146,43 @@ public class TableComponent implements LayoutableRenderableEntity } } - private Color getColumnColor(final int column) + public void addRow(@Nonnull TableRow row) { - if (columnColors.length <= column - || columnColors[column] == null) - { - return defaultColor; - } - return columnColors[column]; + this.tableRows.add(row); } - private TableAlignment getColumnAlignment(final int column) + public void addRows(@Nonnull final TableRow... rows) { - if (columnAlignments.length <= column - || columnAlignments[column] == null) + for (TableRow row : rows) { - return defaultAlignment; + addRow(row); } - return columnAlignments[column]; } private String getCellText(final int col, final int row) { - assert col < numCols && row < numRows; + assert col < tableColumns.length && row < tableRows.size(); - if (cells.get(row).length < col) + if (row == -1) + { + return tableColumns[col].getContent(); + } + + TableElement[] elements = tableRows.get(row).getElements(); + if (col >= elements.length) { return ""; } - return cells.get(row)[col]; + + final String result = elements[col].content; + return result != null ? result : ""; } private int[] getColumnWidths(final FontMetrics metrics) { + final int numRows = tableRows.size(); + final int numCols = tableColumns.length; + // Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width int[] maxtextw = new int[numCols]; // max text width over all rows int[] maxwordw = new int[numCols]; // max width of longest word @@ -331,4 +341,63 @@ public class TableComponent implements LayoutableRenderableEntity } return offset; } + + private Color getCellColor(final int row, final int column) + { + assert row < tableRows.size() && column < tableColumns.length; + + // Row should be -1 for columns so use a empty TableRow + final TableRow rowEle = row != -1 ? tableRows.get(row) : EMPTY_ROW; + final TableElement columnElement = tableColumns[column]; + final TableElement[] elements = rowEle.getElements(); + + // Some rows may not have every element, even though they should.. + final TableElement ele = column < elements.length ? elements[column] : EMPTY_ELEMENT; + + // Color priorities goes as follow: cell->row->column->default + return firstNonNull( + ele.getColor(), + rowEle.getRowColor(), + columnElement.getColor(), + defaultColor); + } + + private TableAlignment getCellAlignment(final int row, final int column) + { + assert row < tableRows.size() && column < tableColumns.length; + + // Row should be -1 for columns so use a empty TableRow + final TableRow rowEle = row != -1 ? tableRows.get(row) : EMPTY_ROW; + final TableElement columnElement = tableColumns[column]; + final TableElement[] elements = rowEle.getElements(); + + // Some rows may not have every element, even though they should.. + final TableElement ele = column < elements.length ? elements[column] : EMPTY_ELEMENT; + + // Alignment priorities goes as follow: cell->row->column->default + return firstNonNull( + ele.getAlignment(), + rowEle.getRowAlignment(), + columnElement.getAlignment(), + defaultAlignment); + } + + @SafeVarargs + private static T firstNonNull(@Nullable T... elements) + { + if (elements == null || elements.length == 0) + { + return null; + } + + int i = 0; + T cur = elements[0]; + while (cur == null && i < elements.length) + { + cur = elements[i]; + i++; + } + + return cur; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java new file mode 100644 index 0000000000..8229f9533c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableElement.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.overlay.components.table; + +import java.awt.Color; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class TableElement +{ + TableAlignment alignment; + Color color; + String content; +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java new file mode 100644 index 0000000000..1f38d7368b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.ui.overlay.components.table; + +import java.awt.Color; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class TableRow +{ + Color rowColor; + TableAlignment rowAlignment; + TableElement[] elements; +} From 8a753f58724c87e877e25f26aa82ff00feb67e9c Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Tue, 30 Apr 2019 23:51:30 -0700 Subject: [PATCH 04/16] Variable rename --- .../components/table/TableComponent.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 63acf66389..14b9fa467f 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -51,8 +51,8 @@ public class TableComponent implements LayoutableRenderableEntity private final Rectangle bounds = new Rectangle(); @Nonnull - private TableElement[] tableColumns = new TableElement[0]; - private final List tableRows = new ArrayList<>(); + private TableElement[] columns = new TableElement[0]; + private final List rows = new ArrayList<>(); @Nonnull private TableAlignment defaultAlignment = TableAlignment.LEFT; @@ -74,8 +74,8 @@ public class TableComponent implements LayoutableRenderableEntity graphics.translate(preferredLocation.x, preferredLocation.y); - final int numRows = tableRows.size(); - final int numCols = tableColumns.length; + final int numRows = rows.size(); + final int numCols = columns.length; for (int row = 0; row < numRows; row++) { @@ -114,14 +114,14 @@ public class TableComponent implements LayoutableRenderableEntity public void setColumnColor(final int col, final Color color) { - assert tableColumns.length > col; - tableColumns[col].setColor(color); + assert columns.length > col; + columns[col].setColor(color); } public void setColumnAlignment(final int col, final TableAlignment alignment) { - assert tableColumns.length > col; - tableColumns[col].setAlignment(alignment); + assert columns.length > col; + columns[col].setAlignment(alignment); } public void addRow(@Nonnull final String... cells) @@ -135,7 +135,7 @@ public class TableComponent implements LayoutableRenderableEntity final TableRow row = TableRow.builder().build(); row.setElements(elements); - this.tableRows.add(row); + this.rows.add(row); } public void addRows(@Nonnull final String[]... rows) @@ -148,7 +148,7 @@ public class TableComponent implements LayoutableRenderableEntity public void addRow(@Nonnull TableRow row) { - this.tableRows.add(row); + this.rows.add(row); } public void addRows(@Nonnull final TableRow... rows) @@ -161,14 +161,14 @@ public class TableComponent implements LayoutableRenderableEntity private String getCellText(final int col, final int row) { - assert col < tableColumns.length && row < tableRows.size(); + assert col < columns.length && row < rows.size(); if (row == -1) { - return tableColumns[col].getContent(); + return columns[col].getContent(); } - TableElement[] elements = tableRows.get(row).getElements(); + TableElement[] elements = rows.get(row).getElements(); if (col >= elements.length) { return ""; @@ -180,8 +180,8 @@ public class TableComponent implements LayoutableRenderableEntity private int[] getColumnWidths(final FontMetrics metrics) { - final int numRows = tableRows.size(); - final int numCols = tableColumns.length; + final int numRows = rows.size(); + final int numCols = columns.length; // Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width int[] maxtextw = new int[numCols]; // max text width over all rows @@ -344,11 +344,11 @@ public class TableComponent implements LayoutableRenderableEntity private Color getCellColor(final int row, final int column) { - assert row < tableRows.size() && column < tableColumns.length; + assert row < rows.size() && column < columns.length; // Row should be -1 for columns so use a empty TableRow - final TableRow rowEle = row != -1 ? tableRows.get(row) : EMPTY_ROW; - final TableElement columnElement = tableColumns[column]; + final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; + final TableElement columnElement = columns[column]; final TableElement[] elements = rowEle.getElements(); // Some rows may not have every element, even though they should.. @@ -364,11 +364,11 @@ public class TableComponent implements LayoutableRenderableEntity private TableAlignment getCellAlignment(final int row, final int column) { - assert row < tableRows.size() && column < tableColumns.length; + assert row < rows.size() && column < columns.length; // Row should be -1 for columns so use a empty TableRow - final TableRow rowEle = row != -1 ? tableRows.get(row) : EMPTY_ROW; - final TableElement columnElement = tableColumns[column]; + final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; + final TableElement columnElement = columns[column]; final TableElement[] elements = rowEle.getElements(); // Some rows may not have every element, even though they should.. From 831c32c5bb34529073a456abf54defe63707cf22 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Tue, 30 Apr 2019 23:59:19 -0700 Subject: [PATCH 05/16] Columns to list and add methods --- .../components/table/TableComponent.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 14b9fa467f..63b22bd083 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -50,8 +50,7 @@ public class TableComponent implements LayoutableRenderableEntity @Getter private final Rectangle bounds = new Rectangle(); - @Nonnull - private TableElement[] columns = new TableElement[0]; + private final List columns = new ArrayList<>(); private final List rows = new ArrayList<>(); @Nonnull @@ -75,7 +74,7 @@ public class TableComponent implements LayoutableRenderableEntity graphics.translate(preferredLocation.x, preferredLocation.y); final int numRows = rows.size(); - final int numCols = columns.length; + final int numCols = columns.size(); for (int row = 0; row < numRows; row++) { @@ -114,14 +113,14 @@ public class TableComponent implements LayoutableRenderableEntity public void setColumnColor(final int col, final Color color) { - assert columns.length > col; - columns[col].setColor(color); + assert columns.size() > col; + columns.get(col).setColor(color); } public void setColumnAlignment(final int col, final TableAlignment alignment) { - assert columns.length > col; - columns[col].setAlignment(alignment); + assert columns.size() > col; + columns.get(col).setAlignment(alignment); } public void addRow(@Nonnull final String... cells) @@ -146,6 +145,35 @@ public class TableComponent implements LayoutableRenderableEntity } } + public void addColumn(@Nonnull TableElement element) + { + this.columns.add(element); + } + + public void addColumns(@Nonnull final TableElement... elements) + { + for (TableElement ele : elements) + { + addColumn(ele); + } + } + + public void addColumn(@Nonnull final String... cells) + { + for (int i = 0; i < cells.length; i++) + { + this.columns.add(TableElement.builder().content(cells[i]).build()); + } + } + + public void addColumns(@Nonnull final String[]... columns) + { + for (String[] col : columns) + { + addColumn(col); + } + } + public void addRow(@Nonnull TableRow row) { this.rows.add(row); @@ -161,11 +189,11 @@ public class TableComponent implements LayoutableRenderableEntity private String getCellText(final int col, final int row) { - assert col < columns.length && row < rows.size(); + assert col < columns.size() && row < rows.size(); if (row == -1) { - return columns[col].getContent(); + return columns.get(col).getContent(); } TableElement[] elements = rows.get(row).getElements(); @@ -181,7 +209,7 @@ public class TableComponent implements LayoutableRenderableEntity private int[] getColumnWidths(final FontMetrics metrics) { final int numRows = rows.size(); - final int numCols = columns.length; + final int numCols = columns.size(); // Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width int[] maxtextw = new int[numCols]; // max text width over all rows @@ -344,11 +372,11 @@ public class TableComponent implements LayoutableRenderableEntity private Color getCellColor(final int row, final int column) { - assert row < rows.size() && column < columns.length; + assert row < rows.size() && column < columns.size(); // Row should be -1 for columns so use a empty TableRow final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; - final TableElement columnElement = columns[column]; + final TableElement columnElement = columns.get(column); final TableElement[] elements = rowEle.getElements(); // Some rows may not have every element, even though they should.. @@ -364,11 +392,11 @@ public class TableComponent implements LayoutableRenderableEntity private TableAlignment getCellAlignment(final int row, final int column) { - assert row < rows.size() && column < columns.length; + assert row < rows.size() && column < columns.size(); // Row should be -1 for columns so use a empty TableRow final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; - final TableElement columnElement = columns[column]; + final TableElement columnElement = columns.get(column); final TableElement[] elements = rowEle.getElements(); // Some rows may not have every element, even though they should.. From 395793b716a64f7f43fd77e89b7b4ee38ee638dc Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:00:57 -0700 Subject: [PATCH 06/16] set column colors --- .../ui/overlay/components/table/TableComponent.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 63b22bd083..317ab3bd04 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -123,6 +123,14 @@ public class TableComponent implements LayoutableRenderableEntity columns.get(col).setAlignment(alignment); } + public void setColumnColors(@Nonnull final Color... colors) + { + for (int i = 0; i < colors.length; i++) + { + setColumnColor(i, colors[i]); + } + } + public void addRow(@Nonnull final String... cells) { final TableElement[] elements = new TableElement[cells.length]; From f2c2ebe711e3c6470be4113f90e33efe15224f90 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:08:50 -0700 Subject: [PATCH 07/16] change addColumns to setColumns --- .../components/table/TableComponent.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 317ab3bd04..158b7b82cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -32,6 +32,7 @@ import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -50,7 +51,9 @@ public class TableComponent implements LayoutableRenderableEntity @Getter private final Rectangle bounds = new Rectangle(); + @Getter private final List columns = new ArrayList<>(); + @Getter private final List rows = new ArrayList<>(); @Nonnull @@ -158,24 +161,23 @@ public class TableComponent implements LayoutableRenderableEntity this.columns.add(element); } - public void addColumns(@Nonnull final TableElement... elements) + public void setColumns(@Nonnull final TableElement... elements) { - for (TableElement ele : elements) - { - addColumn(ele); - } + this.columns.clear(); + this.columns.addAll(Arrays.asList(elements)); } public void addColumn(@Nonnull final String... cells) { - for (int i = 0; i < cells.length; i++) + for (String cell : cells) { - this.columns.add(TableElement.builder().content(cells[i]).build()); + this.columns.add(TableElement.builder().content(cell).build()); } } - public void addColumns(@Nonnull final String[]... columns) + public void setColumns(@Nonnull final String[]... columns) { + this.columns.clear(); for (String[] col : columns) { addColumn(col); From 38e921c04c30f85bd54feedb846139117d47accc Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:10:12 -0700 Subject: [PATCH 08/16] More fixes! --- .../ui/overlay/components/table/TableComponent.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 158b7b82cf..aec2213bd8 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -167,18 +167,15 @@ public class TableComponent implements LayoutableRenderableEntity this.columns.addAll(Arrays.asList(elements)); } - public void addColumn(@Nonnull final String... cells) + public void addColumn(@Nonnull final String col) { - for (String cell : cells) - { - this.columns.add(TableElement.builder().content(cell).build()); - } + this.columns.add(TableElement.builder().content(col).build()); } - public void setColumns(@Nonnull final String[]... columns) + public void setColumns(@Nonnull final String... columns) { this.columns.clear(); - for (String[] col : columns) + for (String col : columns) { addColumn(col); } From e68e402779a3a2c941b1f457ce57ccdc574419ba Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:17:58 -0700 Subject: [PATCH 09/16] Auto add blank columns we adding rows --- .../client/ui/overlay/components/table/TableComponent.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index aec2213bd8..5bbb866cec 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -136,6 +136,11 @@ public class TableComponent implements LayoutableRenderableEntity public void addRow(@Nonnull final String... cells) { + while (cells.length > columns.size()) + { + columns.add(TableElement.builder().build()); + } + final TableElement[] elements = new TableElement[cells.length]; for (int i = 0; i < cells.length; i++) { From 22626abbf797be9f203c8257cd70fc37d74828bd Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:18:48 -0700 Subject: [PATCH 10/16] Move table component test to matching directory --- .../overlay/components/{ => table}/TableComponentTest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) rename runelite-client/src/test/java/net/runelite/client/ui/overlay/components/{ => table}/TableComponentTest.java (95%) diff --git a/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java similarity index 95% rename from runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java rename to runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java index 404f4f0ff4..19fde8fe35 100644 --- a/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/TableComponentTest.java +++ b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java @@ -22,12 +22,11 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.client.ui.overlay.components; +package net.runelite.client.ui.overlay.components.table; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics2D; -import net.runelite.client.ui.overlay.components.TableComponent.TableAlignment; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,8 +36,8 @@ import org.mockito.Mock; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) From d23b126a5e5fc8150280a7bf26cbf65cdbd63b36 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:22:09 -0700 Subject: [PATCH 11/16] Update TableComponent.java --- .../client/ui/overlay/components/table/TableComponent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 5bbb866cec..7d9b7a90dd 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -140,7 +140,7 @@ public class TableComponent implements LayoutableRenderableEntity { columns.add(TableElement.builder().build()); } - + final TableElement[] elements = new TableElement[cells.length]; for (int i = 0; i < cells.length; i++) { From f50abdf1b192b7fe354808578b3aebbb6e1a00a1 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:26:26 -0700 Subject: [PATCH 12/16] Set column alignments...will this ever stop --- .../overlay/components/table/TableComponent.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 7d9b7a90dd..ab13eeb6cd 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -120,17 +120,25 @@ public class TableComponent implements LayoutableRenderableEntity columns.get(col).setColor(color); } + public void setColumnColors(@Nonnull final Color... colors) + { + for (int i = 0; i < colors.length; i++) + { + setColumnColor(i, colors[i]); + } + } + public void setColumnAlignment(final int col, final TableAlignment alignment) { assert columns.size() > col; columns.get(col).setAlignment(alignment); } - public void setColumnColors(@Nonnull final Color... colors) + public void setColumnAlignments(@Nonnull final TableAlignment... alignments) { - for (int i = 0; i < colors.length; i++) + for (int i = 0; i < alignments.length; i++) { - setColumnColor(i, colors[i]); + setColumnAlignment(i, alignments[i]); } } From e01acaa3617e93aefee425234a4d243a2455b28a Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:32:45 -0700 Subject: [PATCH 13/16] Add copyright --- .../components/table/TableAlignment.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java index d7fd5ed87e..d55080a3e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableAlignment.java @@ -1,3 +1,27 @@ +/* +* Copyright (c) 2019, TheStonedTurtle +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ package net.runelite.client.ui.overlay.components.table; public enum TableAlignment From f7da72d6b9a5a1dcdc05bdb1ec2f37722b5908b3 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Wed, 1 May 2019 00:37:58 -0700 Subject: [PATCH 14/16] Ensure columns exist when calling necessary functions --- .../overlay/components/table/TableComponent.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index ab13eeb6cd..7aab87a9ee 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -114,6 +114,14 @@ public class TableComponent implements LayoutableRenderableEntity return dimension; } + private void ensureColumnSize(final int size) + { + while (size > columns.size()) + { + columns.add(TableElement.builder().build()); + } + } + public void setColumnColor(final int col, final Color color) { assert columns.size() > col; @@ -122,6 +130,7 @@ public class TableComponent implements LayoutableRenderableEntity public void setColumnColors(@Nonnull final Color... colors) { + ensureColumnSize(colors.length); for (int i = 0; i < colors.length; i++) { setColumnColor(i, colors[i]); @@ -136,6 +145,7 @@ public class TableComponent implements LayoutableRenderableEntity public void setColumnAlignments(@Nonnull final TableAlignment... alignments) { + ensureColumnSize(alignments.length); for (int i = 0; i < alignments.length; i++) { setColumnAlignment(i, alignments[i]); @@ -144,10 +154,7 @@ public class TableComponent implements LayoutableRenderableEntity public void addRow(@Nonnull final String... cells) { - while (cells.length > columns.size()) - { - columns.add(TableElement.builder().build()); - } + ensureColumnSize(cells.length); final TableElement[] elements = new TableElement[cells.length]; for (int i = 0; i < cells.length; i++) From eaae5b406701ec742a2ee8c96611c1c14940a19a Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Sat, 11 May 2019 09:39:53 -0700 Subject: [PATCH 15/16] Code cleanup --- .../components/table/TableComponent.java | 326 ++++++++---------- .../ui/overlay/components/table/TableRow.java | 3 +- .../components/table/TableComponentTest.java | 7 +- 3 files changed, 153 insertions(+), 183 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 7aab87a9ee..3e9b838ce5 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -37,6 +37,7 @@ import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import lombok.Getter; +import lombok.NonNull; import lombok.Setter; import net.runelite.client.ui.overlay.components.ComponentConstants; import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; @@ -45,211 +46,111 @@ import net.runelite.client.ui.overlay.components.TextComponent; @Setter public class TableComponent implements LayoutableRenderableEntity { - private static final TableRow EMPTY_ROW = TableRow.builder().build(); private static final TableElement EMPTY_ELEMENT = TableElement.builder().build(); - @Getter - private final Rectangle bounds = new Rectangle(); - @Getter private final List columns = new ArrayList<>(); @Getter private final List rows = new ArrayList<>(); - @Nonnull + @Getter + private final Rectangle bounds = new Rectangle(); + private TableAlignment defaultAlignment = TableAlignment.LEFT; - @Nonnull private Color defaultColor = Color.WHITE; - @Nonnull private Dimension gutter = new Dimension(3, 0); - @Nonnull private Point preferredLocation = new Point(); - @Nonnull private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); @Override public Dimension render(final Graphics2D graphics) { final FontMetrics metrics = graphics.getFontMetrics(); - final int[] columnWidths = getColumnWidths(metrics); - int height = 0; + final TableRow colRow = TableRow.builder().elements(this.columns).build(); + final int[] columnWidths = getColumnWidths(metrics, colRow); graphics.translate(preferredLocation.x, preferredLocation.y); - final int numRows = rows.size(); - final int numCols = columns.size(); + // Display the columns first + int height = displayRow(graphics, colRow, 0, columnWidths, metrics); - for (int row = 0; row < numRows; row++) + for (TableRow row : this.rows) { - int x = 0; - int startingRowHeight = height; - for (int col = 0; col < numCols; col++) - { - int y = startingRowHeight; - final String[] lines = lineBreakText(getCellText(col, row), columnWidths[col], metrics); - for (String line : lines) - { - final TableAlignment alignment = getCellAlignment(row, col); - final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[col], metrics); - final TextComponent leftLineComponent = new TextComponent(); - y += metrics.getHeight(); - - final Color lineColor = getCellColor(row, col); - - leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); - leftLineComponent.setText(line); - leftLineComponent.setColor(lineColor); - leftLineComponent.render(graphics); - } - height = Math.max(height, y); - x += columnWidths[col] + gutter.width; - } - height += gutter.height; + height += displayRow(graphics, row, height, columnWidths, metrics); } graphics.translate(-preferredLocation.x, -preferredLocation.y); + final Dimension dimension = new Dimension(preferredSize.width, height); bounds.setLocation(preferredLocation); bounds.setSize(dimension); + return dimension; } - private void ensureColumnSize(final int size) + private int displayRow(Graphics2D graphics, TableRow row, int height, int[] columnWidths, FontMetrics metrics) { - while (size > columns.size()) + int x = 0; + int startingRowHeight = height; + + final List elements = row.getElements(); + for (int i = 0; i < elements.size(); i++) { - columns.add(TableElement.builder().build()); - } - } + int y = startingRowHeight; + final TableElement cell = elements.get(i); - public void setColumnColor(final int col, final Color color) - { - assert columns.size() > col; - columns.get(col).setColor(color); - } + final String[] lines = lineBreakText(cell.getContent(), columnWidths[i], metrics); + final TableAlignment alignment = getCellAlignment(row, i); + final Color color = getCellColor(row, i); - public void setColumnColors(@Nonnull final Color... colors) - { - ensureColumnSize(colors.length); - for (int i = 0; i < colors.length; i++) - { - setColumnColor(i, colors[i]); - } - } + for (String line : lines) + { + final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[i], metrics); + final TextComponent leftLineComponent = new TextComponent(); + y += metrics.getHeight(); - public void setColumnAlignment(final int col, final TableAlignment alignment) - { - assert columns.size() > col; - columns.get(col).setAlignment(alignment); - } - - public void setColumnAlignments(@Nonnull final TableAlignment... alignments) - { - ensureColumnSize(alignments.length); - for (int i = 0; i < alignments.length; i++) - { - setColumnAlignment(i, alignments[i]); - } - } - - public void addRow(@Nonnull final String... cells) - { - ensureColumnSize(cells.length); - - final TableElement[] elements = new TableElement[cells.length]; - for (int i = 0; i < cells.length; i++) - { - elements[i] = TableElement.builder().content(cells[i]).build(); + leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); + leftLineComponent.setText(line); + leftLineComponent.setColor(color); + leftLineComponent.render(graphics); + } + height = Math.max(height, y); + x += columnWidths[i] + gutter.width; } - final TableRow row = TableRow.builder().build(); - row.setElements(elements); - - this.rows.add(row); + return height + gutter.height; } - public void addRows(@Nonnull final String[]... rows) + /** + * Returns the width that each column should take up + * Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width + * @param metrics + * @return int[] of column width + */ + private int[] getColumnWidths(final FontMetrics metrics, final TableRow columnRow) { - for (String[] row : rows) + int numCols = columns.size(); + for (final TableRow r : rows) { - addRow(row); - } - } - - public void addColumn(@Nonnull TableElement element) - { - this.columns.add(element); - } - - public void setColumns(@Nonnull final TableElement... elements) - { - this.columns.clear(); - this.columns.addAll(Arrays.asList(elements)); - } - - public void addColumn(@Nonnull final String col) - { - this.columns.add(TableElement.builder().content(col).build()); - } - - public void setColumns(@Nonnull final String... columns) - { - this.columns.clear(); - for (String col : columns) - { - addColumn(col); - } - } - - public void addRow(@Nonnull TableRow row) - { - this.rows.add(row); - } - - public void addRows(@Nonnull final TableRow... rows) - { - for (TableRow row : rows) - { - addRow(row); - } - } - - private String getCellText(final int col, final int row) - { - assert col < columns.size() && row < rows.size(); - - if (row == -1) - { - return columns.get(col).getContent(); + numCols = Math.max(r.getElements().size(), numCols); } - TableElement[] elements = rows.get(row).getElements(); - if (col >= elements.length) - { - return ""; - } - - final String result = elements[col].content; - return result != null ? result : ""; - } - - private int[] getColumnWidths(final FontMetrics metrics) - { - final int numRows = rows.size(); - final int numCols = columns.size(); - - // Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width int[] maxtextw = new int[numCols]; // max text width over all rows int[] maxwordw = new int[numCols]; // max width of longest word boolean[] flex = new boolean[numCols]; // is column flexible? boolean[] wrap = new boolean[numCols]; // can column be wrapped? int[] finalcolw = new int[numCols]; // final width of columns - for (int col = 0; col < numCols; col++) + final List rows = new ArrayList<>(this.rows); + rows.add(columnRow); + + for (final TableRow r : rows) { - for (int row = 0; row < numRows; row++) + final List elements = r.getElements(); + for (int col = 0; col < elements.size(); col++) { - final String cell = getCellText(col, row); + final TableElement ele = elements.get(col); + final String cell = ele.getContent(); final int cellWidth = getTextWidth(metrics, cell); maxtextw[col] = Math.max(maxtextw[col], cellWidth); @@ -397,43 +298,41 @@ public class TableComponent implements LayoutableRenderableEntity return offset; } - private Color getCellColor(final int row, final int column) + /** + * Returns the color for the specified table element. + * Priority order: cell->row->column->default + * @param row TableRow element + * @param colIndex column index + */ + private Color getCellColor(final TableRow row, final int colIndex) { - assert row < rows.size() && column < columns.size(); + final List rowElements = row.getElements(); + final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; + final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - // Row should be -1 for columns so use a empty TableRow - final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; - final TableElement columnElement = columns.get(column); - final TableElement[] elements = rowEle.getElements(); - - // Some rows may not have every element, even though they should.. - final TableElement ele = column < elements.length ? elements[column] : EMPTY_ELEMENT; - - // Color priorities goes as follow: cell->row->column->default return firstNonNull( - ele.getColor(), - rowEle.getRowColor(), - columnElement.getColor(), + cell.getColor(), + row.getRowColor(), + column.getColor(), defaultColor); } - private TableAlignment getCellAlignment(final int row, final int column) + /** + * Returns the alignment for the specified table element. + * Priority order: cell->row->column->default + * @param row TableRow element + * @param colIndex column index + */ + private TableAlignment getCellAlignment(final TableRow row, final int colIndex) { - assert row < rows.size() && column < columns.size(); + final List rowElements = row.getElements(); + final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; + final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - // Row should be -1 for columns so use a empty TableRow - final TableRow rowEle = row != -1 ? rows.get(row) : EMPTY_ROW; - final TableElement columnElement = columns.get(column); - final TableElement[] elements = rowEle.getElements(); - - // Some rows may not have every element, even though they should.. - final TableElement ele = column < elements.length ? elements[column] : EMPTY_ELEMENT; - - // Alignment priorities goes as follow: cell->row->column->default return firstNonNull( - ele.getAlignment(), - rowEle.getRowAlignment(), - columnElement.getAlignment(), + cell.getAlignment(), + row.getRowAlignment(), + column.getAlignment(), defaultAlignment); } @@ -455,4 +354,69 @@ public class TableComponent implements LayoutableRenderableEntity return cur; } + + // Helper functions for cleaner overlay code + public void addRow(@Nonnull final String... cells) + { + final List elements = new ArrayList<>(); + for (final String cell : cells) + { + elements.add(TableElement.builder().content(cell).build()); + } + + final TableRow row = TableRow.builder().build(); + row.setElements(elements); + + this.rows.add(row); + } + + public void addRows(@Nonnull final String[]... rows) + { + for (String[] row : rows) + { + addRow(row); + } + } + + public void addRows(@NonNull final TableRow... rows) + { + this.rows.addAll(Arrays.asList(rows)); + } + + public void setRows(@Nonnull final String[]... elements) + { + this.rows.clear(); + addRows(elements); + } + + public void setRows(@Nonnull final TableRow... elements) + { + this.rows.clear(); + this.rows.addAll(Arrays.asList(elements)); + } + + public void addColumn(@Nonnull final String col) + { + this.columns.add(TableElement.builder().content(col).build()); + } + + public void addColumns(@NonNull final TableElement... columns) + { + this.columns.addAll(Arrays.asList(columns)); + } + + public void setColumns(@Nonnull final TableElement... elements) + { + this.columns.clear(); + this.columns.addAll(Arrays.asList(elements)); + } + + public void setColumns(@Nonnull final String... columns) + { + this.columns.clear(); + for (String col : columns) + { + addColumn(col); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java index 1f38d7368b..d80c40e4db 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java @@ -25,6 +25,7 @@ package net.runelite.client.ui.overlay.components.table; import java.awt.Color; +import java.util.List; import lombok.Builder; import lombok.Data; @@ -34,5 +35,5 @@ public class TableRow { Color rowColor; TableAlignment rowAlignment; - TableElement[] elements; + List elements; } diff --git a/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java index 19fde8fe35..1c7e95d37a 100644 --- a/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java +++ b/runelite-client/src/test/java/net/runelite/client/ui/overlay/components/table/TableComponentTest.java @@ -27,6 +27,7 @@ package net.runelite.client.ui.overlay.components.table; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics2D; +import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -69,7 +70,11 @@ public class TableComponentTest { TableComponent tableComponent = new TableComponent(); tableComponent.addRow("test", "test", "test", "test", "test"); - tableComponent.setColumnColors(Color.RED, Color.GREEN, Color.BLUE); + tableComponent.setColumns("", "", ""); + List elements = tableComponent.getColumns(); + elements.get(0).setColor(Color.RED); + elements.get(1).setColor(Color.GREEN); + elements.get(2).setColor(Color.BLUE); tableComponent.render(graphics); verify(graphics, atLeastOnce()).setColor(Color.RED); verify(graphics, atLeastOnce()).setColor(Color.GREEN); From 7b45cbe3487dbe3b145758e6d2dbeab37655d4f3 Mon Sep 17 00:00:00 2001 From: TheStonedTurtle Date: Sat, 11 May 2019 10:11:24 -0700 Subject: [PATCH 16/16] More code fixes --- .../overlay/components/table/TableComponent.java | 15 +++++++++++++-- .../ui/overlay/components/table/TableRow.java | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java index 3e9b838ce5..5dec2a7ac6 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableComponent.java @@ -76,7 +76,7 @@ public class TableComponent implements LayoutableRenderableEntity for (TableRow row : this.rows) { - height += displayRow(graphics, row, height, columnWidths, metrics); + height = displayRow(graphics, row, height, columnWidths, metrics); } graphics.translate(-preferredLocation.x, -preferredLocation.y); @@ -99,7 +99,13 @@ public class TableComponent implements LayoutableRenderableEntity int y = startingRowHeight; final TableElement cell = elements.get(i); - final String[] lines = lineBreakText(cell.getContent(), columnWidths[i], metrics); + final String content = cell.getContent(); + if (content == null) + { + continue; + } + + final String[] lines = lineBreakText(content, columnWidths[i], metrics); final TableAlignment alignment = getCellAlignment(row, i); final Color color = getCellColor(row, i); @@ -151,6 +157,11 @@ public class TableComponent implements LayoutableRenderableEntity { final TableElement ele = elements.get(col); final String cell = ele.getContent(); + if (cell == null) + { + continue; + } + final int cellWidth = getTextWidth(metrics, cell); maxtextw[col] = Math.max(maxtextw[col], cellWidth); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java index d80c40e4db..8879b08c5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/table/TableRow.java @@ -25,6 +25,7 @@ package net.runelite.client.ui.overlay.components.table; import java.awt.Color; +import java.util.ArrayList; import java.util.List; import lombok.Builder; import lombok.Data; @@ -35,5 +36,6 @@ public class TableRow { Color rowColor; TableAlignment rowAlignment; - List elements; + @Builder.Default + List elements = new ArrayList<>(); }