In the class that extends JFrame, I have the following code:
public Frame() {
setTitle("WORDLE");
// creates all containers
Container mainContainer = getContentPane();
JPanel gridContainer = new JPanel();
JPanel optionsContainer = new JPanel();
// sets size of main frame
setSize(WIDTH, 900);
// sets background size and background colors of all containers
gridContainer.setBackground(GameTheme.BACKGROUND); // TODO: add message label inside of gridContainer below grid JPanel
gridContainer.setPreferredSize(new Dimension(WIDTH - 150, 500));
gridContainer.setMaximumSize(new Dimension(WIDTH - 150, 500));
gridContainer.setLayout(new BorderLayout(10, 0));
optionsContainer.setBackground(GameTheme.BACKGROUND);
optionsContainer.setPreferredSize(new Dimension(WIDTH, 100));
// creates grid with boxes for letter
grid = new JPanel();
grid.setLayout(new GridLayout(6, 5, 5, 5));
grid.setPreferredSize(new Dimension(WIDTH - 150, 400));
grid.setMaximumSize(new Dimension(WIDTH - 150, 400));
grid.setBackground(GameTheme.BACKGROUND);
createBoxes();
// JLabel for messages
JLabel alertLabel = new JLabel("test");
alertLabel.setOpaque(true);
alertLabel.setForeground(GameTheme.WHITE);
alertLabel.setBackground(GameTheme.BACKGROUND);
// listens for presses in the JFrame, with the Game class implementing the method for the keypress event
addKeyListener(new Game(boxes));
// lays out all containers and their sub-containers
// title label at top, grid container at center, options container at bottom
mainContainer.setLayout(new BorderLayout());
mainContainer.add(createTitle(), BorderLayout.NORTH); // createTitle returns a JLabel
mainContainer.add(gridContainer, BorderLayout.CENTER);
mainContainer.add(optionsContainer, BorderLayout.SOUTH);
gridContainer.add(grid, BorderLayout.NORTH);
gridContainer.add(alertLabel, BorderLayout.SOUTH);
// boilerplate frame configuration
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
setLocationRelativeTo(null);
pack();
setVisible(true);
}
public void createBoxes() {
// initializes 2d array for letter boxes
boxes = new LetterBox[6][5];
// populates 2d array and adds boxes to the grid container
for (int r = 0; r < 6; r ) {
for (int c = 0; c < 5; c ) {
LetterBox letterBox = new LetterBox(r, c);
boxes[r][c] = letterBox;
grid.add(letterBox.getBox());
}
}
}
I have set a maximum size on all of the components within gridContainer
, and even on the gridContainer
itself, yet the JLabels are still stretching to take up the size of the GUI. Can someone help me figure out why this is happening, and how I can fix it?
To make the problem clear, here are some images that show what I want vs what I am getting:
Since the code I added is probably not enough, I add the full source code to GitHub: https://github.com/jgorelik23/JavaWordle
I tried to describe as clearly as possible please comment and let me know if there is anything I need to clarify.
CodePudding user response:
Don't keep playing with the preferred/minimum/maximum sizes of parent panels and the frame. Each panel should be able to calculate its preferred size based on the preferred size of the components added to the panel. So you add components to a panel and the panels to the frame, then you pack the frame and everything will be displayed at the proper size.
So in your case you can use a GridLayout
for your "gridContainer". Then you add your JLabels to the gridContainer. For your LetterBox component you should use a "monospaced" Font so all characters will take the same space. Then you can initialize your label with a default value of " " to give the label a size.
This size will be too small so you will then also want to add an EmptyBorder
to the LetterBox
. So in the constructor you can add something like:
setBorder( new EmptyBorder(10, 10, 10, 10) );
Now the LeterBox can calculates its preferred size correctly and the "gridContainer" will be able to calculate its preferred size correctly.
However, when you add the panel the the CENTER of the BorderLayout, the BorderLayout will attempt to resize each component to fill the space available in the frame. To prevent this you can use a "wrapper" panel:
JPanel wrapper = new JPanel(); // uses FlowLayout by default
wrapper.add( gridContainer );
mainContainer.add(wrapper, BorderLayout.CENTER);
//mainContainer.add(gridContainer, BorderLayout.CENTER);
Now if the frame is resized the extra space will go to the wrapper panel NOT the gridContainer.
CodePudding user response:
In case you would like to relax a little the requirement of the frame being unresizable, here follow some implementations which restrict the maximum size of the grid to the preferred, but do not restrict the minimum. As a result in both implementations the grid will not get bigger than its preferred size, making it possible for the user to enlarge the frame and empty spaces to appear by the LayoutManager
s used to fill the remaining space.
The minimum size of the grid is not restricted however, just in case the user wants to resize the frame to a smaller size. This is supposed to be an extra feature, although if you want to disable it the easiest way would be to uncomment the frame.setMinimumSize(frame.getSize());
line in each implementation.
Both implementations take into account the sizes of the grid.
BoxLayout
implementation:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class BoxMain {
private static void createAndShowGUI() {
final int rows = 6, cols = 15;
final JPanel grid = new JPanel(new GridLayout(rows, 0, 5, 5));
for (int row = 0; row < rows; row)
for (int col = 0; col < cols; col)
grid.add(new JLabel(Integer.toString(row * cols col), JLabel.CENTER));
final Dimension sz = grid.getPreferredSize();
grid.setMaximumSize(sz);
grid.setMinimumSize(new Dimension(0, sz.height));
final JPanel box = new JPanel();
box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS));
box.add(Box.createGlue());
box.add(grid);
box.add(Box.createGlue());
final JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(box, BorderLayout.CENTER);
mainPanel.add(new JLabel("Alert label", JLabel.CENTER), BorderLayout.PAGE_END);
final JFrame frame = new JFrame("Wordle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
//frame.setMinimumSize(frame.getSize());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(BoxMain::createAndShowGUI);
}
}
SpringLayout
implementation:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Spring;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;
public class SpringMain {
private static void createAndShowGUI() {
final int rows = 6, cols = 15;
final JPanel grid = new JPanel(new GridLayout(rows, 0, 5, 5));
for (int row = 0; row < rows; row)
for (int col = 0; col < cols; col)
grid.add(new JLabel(Integer.toString(row * cols col), JLabel.CENTER));
grid.setMaximumSize(grid.getPreferredSize());
grid.setMinimumSize(new Dimension());
final SpringLayout layout = new SpringLayout();
final JPanel spring = new JPanel(layout);
spring.add(grid);
final Spring flexible = Spring.constant(0, 0, Short.MAX_VALUE);
layout.putConstraint(SpringLayout.WEST, grid, flexible, SpringLayout.WEST, spring);
layout.putConstraint(SpringLayout.EAST, spring, flexible, SpringLayout.EAST, grid);
layout.putConstraint(SpringLayout.NORTH, grid, flexible, SpringLayout.NORTH, spring);
layout.putConstraint(SpringLayout.SOUTH, spring, flexible, SpringLayout.SOUTH, grid);
final JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.add(spring, BorderLayout.CENTER);
mainPanel.add(new JLabel("Alert label", JLabel.CENTER), BorderLayout.PAGE_END);
final JFrame frame = new JFrame("Wordle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
//frame.setMinimumSize(frame.getSize());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(SpringMain::createAndShowGUI);
}
}