Skip to content

Commit

Permalink
Fixed the annoying vertical alignment issue in the table header (both…
Browse files Browse the repository at this point in the history
… - game and edit tables). Also, replaced the text pane height calculation approach with a more reliable one. Determining the preferred height by using an invisible text pane that is set to have the same text and font.
  • Loading branch information
mrzee committed Apr 4, 2024
1 parent a3172de commit 8510b7e
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 58 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>net.curre.jjeopardy</groupId>
<artifactId>jjeopardy</artifactId>
<packaging>jar</packaging>
<version>0.3.0</version>
<version>0.3.1</version>
<properties>
<!-- User facing application name. -->
<project.appName>JJeopardy</project.appName>
Expand Down
33 changes: 0 additions & 33 deletions src/main/java/net/curre/jjeopardy/service/UiService.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.Component;
import java.awt.Font;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Service responsible for common UI tasks like opening dialogs.
Expand Down Expand Up @@ -165,34 +162,4 @@ public static JTextPane createDefaultTextPane() {

return textPane;
}

/**
* Determines the height of the text area given a string and a width bound.
* @param parent component to obtain font metrics from
* @param font font that's being used
* @param value the string to measure
* @param widthBound width bound (in px)
* @param addNewLine add number of new lines
* @return recommended height for the text area the string is going to be rendered in
*/
public static int getHeightOfTextArea(Component parent, Font font, String value, int widthBound, int addNewLine) {
int stringWidth = parent.getFontMetrics(font).stringWidth(value);
int newLineChars = countNewLineChars(value);
int lineCount = ((stringWidth / widthBound) + newLineChars + addNewLine) | 1;
return lineCount * parent.getFontMetrics(font).getHeight();
}

/**
* Counts new line characters in a given string.
* @param text string to review
* @return the number of new line characters
*/
public static int countNewLineChars(String text) {
Matcher m = Pattern.compile("\r\n|\r|\n").matcher(text);
int count = 0;
while (m.find()) {
count++;
}
return count;
}
}
14 changes: 13 additions & 1 deletion src/main/java/net/curre/jjeopardy/ui/dialog/BasicDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.Component;
Expand Down Expand Up @@ -60,6 +61,12 @@ public abstract class BasicDialog extends JDialog {
/** Max height for the main content text area. */
private static final int MAX_TEXT_AREA_HEIGHT = 540;

/** An invisible text pane to help determining the text areas sizes (not thread safe!). */
private static final JTextPane HELPER_TEXT_PANE = UiService.createDefaultTextPane();

/** Bottom padding for the main text area. */
private static final int TEXT_BOTTOM_PADDING = 30;

/** Ctor. */
public BasicDialog() {
this.setModal(true);
Expand Down Expand Up @@ -182,8 +189,13 @@ protected JTextArea createTextArea(String message, float fontSizeScale) {
JTextArea textArea = createDefaultTextArea(font);
textArea.setText(message);

HELPER_TEXT_PANE.setFont(font);
HELPER_TEXT_PANE.setSize(new Dimension(TEXT_COLUMN_WIDTH, 1000));
HELPER_TEXT_PANE.setText(message);
int textAreaHeight = HELPER_TEXT_PANE.getPreferredSize().height + TEXT_BOTTOM_PADDING;

// Determine the approximate minimum height of the text pane.
int textAreaHeight = UiService.getHeightOfTextArea(this, font, message, TEXT_COLUMN_WIDTH, 3);
// int textAreaHeight = UiService.getHeightOfTextArea(this, font, message, TEXT_COLUMN_WIDTH, 3);
if (textAreaHeight > MAX_TEXT_AREA_HEIGHT) {
textAreaHeight = MAX_TEXT_AREA_HEIGHT;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/net/curre/jjeopardy/ui/edit/EditCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class EditCell extends JPanel {
BorderFactory.createLineBorder(Color.BLACK, BORDER_WIDTH));

/** An invisible text pane to help determining the text areas sizes (not thread safe!). */
private static final JTextPane TEST_TEXT_PANE = UiService.createDefaultTextPane();
private static final JTextPane HELPER_TEXT_PANE = UiService.createDefaultTextPane();

/** The question to use for this cell. */
private final Question question;
Expand Down Expand Up @@ -371,9 +371,9 @@ private int updateAnswerImageLabel(boolean isVisible, int width) {
* @return preferred height of the text pane for the given text
*/
private static int getPreferredHeightForText(String text, Font font, int width) {
TEST_TEXT_PANE.setSize(width, 500);
TEST_TEXT_PANE.setText(text);
TEST_TEXT_PANE.setFont(font);
return TEST_TEXT_PANE.getPreferredSize().height + CONTENT_SPACING;
HELPER_TEXT_PANE.setSize(width, 500);
HELPER_TEXT_PANE.setText(text);
HELPER_TEXT_PANE.setFont(font);
return HELPER_TEXT_PANE.getPreferredSize().height + CONTENT_SPACING;
}
}
23 changes: 17 additions & 6 deletions src/main/java/net/curre/jjeopardy/ui/edit/EditHeaderCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@

package net.curre.jjeopardy.ui.edit;

import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;
import net.curre.jjeopardy.service.AppRegistry;
import net.curre.jjeopardy.service.UiService;
import net.curre.jjeopardy.ui.laf.theme.LafTheme;
import net.curre.jjeopardy.util.JjDefaults;
import org.apache.commons.lang3.StringUtils;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Dimension;

/**
* Represents a table header cell where game category name
Expand All @@ -39,6 +41,9 @@ public class EditHeaderCell extends JPanel {
/** Background color to use for print. */
private static final Color PRINT_BACKGROUND_COLOR = new Color(210, 233, 248);

/** An invisible text pane to help determining the text areas sizes (not thread safe!). */
private static final JTextPane HELPER_TEXT_PANE = UiService.createDefaultTextPane();

/** Text area where category name is rendered. */
private final JTextPane textPane;

Expand All @@ -47,16 +52,21 @@ public class EditHeaderCell extends JPanel {
* @param name header cell text (category name)
*/
public EditHeaderCell(String name) {
this.setLayout(new GridBagLayout());
// Layout helps to vertically center the text content.
this.setLayout(new TableLayout(new double[][] {
{TableLayout.FILL}, // columns
{TableLayout.FILL}})); // rows

LafTheme lafTheme = AppRegistry.getInstance().getLafService().getCurrentLafTheme();
this.textPane = UiService.createDefaultTextPane();
this.textPane.setFont(lafTheme.getEditTableHeaderFont());
HELPER_TEXT_PANE.setFont(lafTheme.getEditTableHeaderFont());
if (!StringUtils.isBlank(name)) {
this.textPane.setText(name.toUpperCase());
}

this.add(this.textPane, new GridBagConstraints());
this.add(this.textPane, new TableLayoutConstraints(
0, 0, 0, 0, TableLayout.CENTER, TableLayout.CENTER));
this.activateViewStyle();
}

Expand All @@ -68,8 +78,9 @@ public EditHeaderCell(String name) {
* @return preferred height of this cell
*/
protected int getPreferredCellHeight(int columnWidth) {
return UiService.getHeightOfTextArea(
this, this.textPane.getFont(), this.textPane.getText(), columnWidth, 2);
HELPER_TEXT_PANE.setSize(new Dimension(columnWidth, 500));
HELPER_TEXT_PANE.setText(this.textPane.getText());
return Math.max(HELPER_TEXT_PANE.getPreferredSize().height, JjDefaults.GAME_TABLE_HEADER_HEIGHT);
}

/** Activates the cell's view style/presentation. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@

package net.curre.jjeopardy.ui.game;

import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;
import net.curre.jjeopardy.service.AppRegistry;
import net.curre.jjeopardy.service.UiService;
import net.curre.jjeopardy.ui.laf.theme.LafTheme;
import net.curre.jjeopardy.util.JjDefaults;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
Expand All @@ -27,8 +30,6 @@
import javax.swing.table.TableCellRenderer;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

/**
* Represents a Game table header cell.
Expand All @@ -41,11 +42,21 @@ public class GameTableHeaderRenderer extends JPanel implements TableCellRenderer
/** Text area where category name is rendered. */
private final JTextPane textPane;

/** An invisible text pane to help determining the text areas sizes (not thread safe!). */
private static final JTextPane HELPER_TEXT_PANE = UiService.createDefaultTextPane();

/** Padding for the cell content. */
private static final int CELL_PADDING = 5;

/** Ctor. */
public GameTableHeaderRenderer() {
this.setLayout(new GridBagLayout());
// Layout helps to vertically center the text content.
this.setLayout(new TableLayout(new double[][] {
{TableLayout.FILL}, // columns
{TableLayout.FILL}})); // rows

LafTheme lafTheme = AppRegistry.getInstance().getLafService().getCurrentLafTheme();
HELPER_TEXT_PANE.setFont(lafTheme.getGameTableHeaderFont());
this.setBackground(lafTheme.getGameTableHeaderBackgroundColor());
this.textPane = UiService.createDefaultTextPane();
this.textPane.setFont(lafTheme.getGameTableHeaderFont());
Expand All @@ -54,22 +65,30 @@ public GameTableHeaderRenderer() {

this.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(lafTheme.getGameTableBorderColor(), 4),
BorderFactory.createEmptyBorder(0, 5, 0, 5)));
BorderFactory.createEmptyBorder(0, CELL_PADDING, 0, CELL_PADDING)));

this.add(this.textPane, new GridBagConstraints());
this.add(this.textPane, new TableLayoutConstraints(
0, 0, 0, 0, TableLayout.CENTER, TableLayout.CENTER));
}

/** {@inheritDoc} */
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
LafTheme lafTheme = AppRegistry.getInstance().getLafService().getCurrentLafTheme();
String text = value.toString().toUpperCase();

int textWidth = table.getColumnModel().getColumn(column).getWidth() - 2 * CELL_PADDING;
this.setSize(new Dimension(textWidth, JjDefaults.GAME_TABLE_HEADER_HEIGHT));
this.textPane.setText(text);
int width = table.getColumnModel().getColumn(column).getWidth();
int height = UiService.getHeightOfTextArea(this, lafTheme.getGameTableHeaderFont(), text, width, 2);
this.textPane.setPreferredSize(new Dimension(width, height));
this.setPreferredSize(new Dimension(width, height));

// Using a non-rendered helper text pane, determine the preferred height of the text.
HELPER_TEXT_PANE.setSize(textWidth, 500);
HELPER_TEXT_PANE.setText(text);
int textHeight = HELPER_TEXT_PANE.getPreferredSize().height;

// Now, set the preferred height on the real text pane.
this.textPane.setPreferredSize(new Dimension(textWidth, textHeight));
this.textPane.setSize(new Dimension(textWidth, textHeight));
return this;
}
}
4 changes: 2 additions & 2 deletions src/main/resources/default.properties
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ jj.defaults.game.window.min.height = 1000
# Rows' height will not be smaller than this value.
jj.defaults.min.row.height = 50

# Preferred game table header height (where game categories are displayed).
jj.defaults.preferred.header.height = 86
# Preferred game and edit table header height (where game categories are displayed).
jj.defaults.preferred.header.height = 100

# Add/remove players dialog width.
jj.defaults.player.dialog.width = 460
Expand Down

0 comments on commit 8510b7e

Please sign in to comment.