Java GUI: Making coordinates align properly - java

I am making a simple Java Swing GUI chessboard where the player can drag and drop pieces. The problem is that, because of the border around the frame (with the title on top, maximize/minimize/close buttons, etc), the coordinates are skewed off - (0, 0) is the upper-left-hand corner of the frame, that is, a little above the X button, but the GUI starts building itself right below the title bar, so the GUI doesn't align with the coordinates, and things do not end up working the way they should. Additionally, when I set the size of the frame to, for instance, 100 x 100, the lower part and some of the right-hand part of my GUI is cut off because the frame doesn't compensate for its border. When I run it as an applet, I don't have this problem, but I don't want to do that. How can I either get rid of that border around my frame window so I can just have the plain GUI, or have the coordinates set themselves up properly?
sscce:
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
public class class1 extends JFrame{
public class1(){
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent evt){
System.out.print(evt.getPoint());
}
});
}
public static void main(String[] args){
class1 c = new class1();
c.setTitle("Test");
c.setSize(320, 320);
c.setLocationRelativeTo(null);
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setVisible(true);
}
}

It's hard to know what is wrong with your code without the code, but I do know that if you go the easy way by using various layout managers, and let these managers do the laying out of components for you and the sizing of things as well, including calling pack() on the JFrame, usually things fall easily and well into place. So again, don't set the size of anything, but rather let the components' preferred sizes and the layout managers do this for you.
If this advice doesn't help, please give us more information and code, preferably an sscce, a small compilable and runnable program that doesn't do anything other than demonstrate your problem.
Edit: I am assuming that this is a Swing GUI. Please verify if this is so.
Edit 2: One problem you're having is that you're setting the size of a JFrame not taking into account its "decorations" including the menu bar, the resize/maximize/close icon. Again, you shouldn't be setting sizes directly, but if you must better override the getPreferredSize() method of the JPanel that holds your grid.
Edit 3: For example:
import java.awt.*;
import javax.swing.*;
public class Grid extends JPanel {
public static final Color DARK_COLOR = Color.red.darker().darker().darker();
public static final Color LIGHT_COLOR = Color.lightGray.brighter();
public static final int SQUARE_SIDE = 60;
private static final int ROW_COUNT = 8;
#Override
public Dimension getPreferredSize() {
return new Dimension(ROW_COUNT * SQUARE_SIDE, ROW_COUNT * SQUARE_SIDE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < ROW_COUNT; i++) {
for (int j = 0; j < ROW_COUNT; j++) {
Color c = (i % 2 == j % 2) ? LIGHT_COLOR : DARK_COLOR;
g.setColor(c);
int x = i * SQUARE_SIDE;
int y = j * SQUARE_SIDE;
g.fillRect(x, y, SQUARE_SIDE, SQUARE_SIDE);
}
}
}
public Grid() {
// TODO Auto-generated constructor stub
}
private static void createAndShowGui() {
Grid mainPanel = new Grid();
JFrame frame = new JFrame("Grid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Related

When i interchange the commented lines , i receive different output.

I am learning java abstract window toolkit and i am stuck in this code.When i interchange the commented line, the output changes.Any Explanation for both the cases will be appreciated.
import java.awt.*;
public class guibutton
{
public guibutton()
{
Frame f = new Frame("Panel Example");
Panel panel = new Panel();
panel.setBounds(40,80,200,200);
panel.setBackground(Color.gray);
f.add(panel);
f.setVisible(true); ////////////////this line
f.setLayout(null); /////////////////this line
f.setResizable(true);
f.setSize(400,400);
}
public static void main(String args[])
{
new guibutton();
}
}
This line:
f.setVisible(true);
renders your GUI in its current state, one where the JFrame's default BorderLayout is in force. Note that BorderLayout ignores the setBounds(...) method.
This line:
f.setLayout(null);
removes the JFrame contentPane's BorderLayout, and so your GUI is rendered without the layout, changing the positioning of the added JPanel -- the setBounds(...) method call here is respected.
If you call this after the GUI has been rendered, it won't have an effect, unless you do something that triggers the layout managers to re-layout the components, such as re-size the GUI.
Myself, I wouldn't use AWT but would use Swing, I'd draw the rectangle within the paintComponent method of a JPanel, using a Rectangle object. This way, I could monitor the mouse in relation to the rectangle, and change its state. For instance, try out this program, and see what happens to the rectangle when the mouse hovers over it:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class GuiButton2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int RECT_X = 40;
private static final int RECT_Y = 80;
private static final int RECT_W = 200;
private static final Color DEFAULT_RECT_COLOR = Color.GRAY;
private static final Color HOVER_RECT_COLOR = Color.PINK;
private Rectangle rectangle = new Rectangle(RECT_X, RECT_Y, RECT_W, RECT_W);
private boolean hover = false;
public GuiButton2() {
setPreferredSize(new Dimension(PREF_W, PREF_H));
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
// hover true if mouse is hovering over the rectangle
hover = rectangle.contains(e.getPoint());
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
// if hover true -- use hover color, otherwise use default color
Color c = hover ? HOVER_RECT_COLOR : DEFAULT_RECT_COLOR;
g2.setColor(c);
g2.fill(rectangle); // draw rectangle
}
private static void createAndShowGui() {
GuiButton2 mainPanel = new GuiButton2();
JFrame frame = new JFrame("GUI Button");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Side note 1:
While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Side note 2:
You will want to learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others.

FlowLayout without linebreaks and/or in vertical direction

This is a follow-up to this question. Any viable answer will also answer that one.
What layout may be used with as little modification as possible to replicate the aligning nature of a FlowLayout, but never linebreak and also be available in a from-top-to-bottom flavour?
The obvious candidate, BoxLayout, does not work nicely with JPanels. Consider the following two examples:
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class App
{
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS);
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
box.add(label);
}
box.add(Box.createVerticalGlue());
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
This will properly display a vertical line of labels, beginning at the top and stretching as far towards the bottom as the labels take space. Good.
Modifying this, however, just a tiny bit:
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS);
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
box.add(Box.createVerticalGlue());
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
This will stretch all components of the Box to the same height, placing the labels far away from each other. Bad.
Overriding the JPanel's getPreferredSize and getMaximumSize methods (with getMinimumSize) has no effect and would be a bad way to fix it, because it relied on the components rather than the container and its layout.
Addendum:
Here is an already pretty successful attempt using GroupLayout. Unfortunately it did not seem to occur to the designer that among DEFAULT_SIZE and PREFERRED_SIZE a choice MINIMUM_SIZE would have been a good idea.
Furthermore if it is possible to invert the sequence of GroupLayout.SequentialGroup, the API is no help to figure out how. I for one certainly have no clue how to even extend that class.
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class LineLayout extends GroupLayout
{
public LineLayout(Container owner, int axis)
{
super(owner);
this.direction = axis;
this.direction |= owner.getComponentOrientation() != ComponentOrientation.LEFT_TO_RIGHT
? LineLayout.RIGHT_TO_LEFT : LineLayout.LEFT_TO_RIGHT;
this.setupGroups();
}
public LineLayout(Container owner, int axis, int orientation)
{
super(owner);
this.direction = axis;
this.direction |= orientation;
this.setupGroups();
}
#Override // to replicate FlowLayout functionality : this method is called from owner.add
public void addLayoutComponent(Component component, Object constraints)
{
if(constraints == null)
{
// REALLY surprised that this works, considering that overriding the JPanel's
// getMaximumSize method with getPreferredSize had no effect
this.horizontal.addComponent(component, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE);
this.vertical.addComponent (component, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE);
}
// TODO: else
}
protected void setupGroups()
{
super.setAutoCreateGaps(false); // does nothing
if((this.direction & LineLayout.AXIS) == LineLayout.Y_AXIS)
{
this.horizontal = super.createParallelGroup();
this.vertical = (this.direction & LineLayout.ORIENTATION) == LineLayout.RIGHT_TO_LEFT
? this.createSequentialInvertedGroup() : super.createSequentialGroup();
}
else
{
this.horizontal = (this.direction & LineLayout.ORIENTATION) == LineLayout.RIGHT_TO_LEFT
? this.createSequentialInvertedGroup() : super.createSequentialGroup();
this.vertical = super.createParallelGroup();
}
super.setHorizontalGroup(this.horizontal);
super.setVerticalGroup (this.vertical);
}
// How!?
// protected LineLayout.SequentialInvertedGroup createSequentialInvertedGroup() { return new LineLayout.SequentialInvertedGroup(); }
protected GroupLayout.SequentialGroup createSequentialInvertedGroup() { return super.createSequentialGroup(); } // placeholder
protected int direction;
protected GroupLayout.Group horizontal;
protected GroupLayout.Group vertical;
// not sure how reliable the constant field values of BoxLayout are, whether it's smart to assume them unchanging over the ages
public static final int AXIS = 0b1;
public static final int X_AXIS = 0b0; // = BoxLayout.X_AXIS;
public static final int Y_AXIS = 0b1; // = BoxLayout.Y_AXIS;
public static final int ORIENTATION = 0b10;
public static final int LEFT_TO_RIGHT = 0b00; // also top to bottom
public static final int RIGHT_TO_LEFT = 0b10; // also bottom to top
// No idea how; only has "add" methods; cannot actually do anything with the added components!?
//protected static class SequentialInvertedGroup extends GroupLayout.SequentialGroup
//{}
}
class Applikation
{
public static void main(String[] args)
{
JFrame window = new JFrame();
JPanel box = new JPanel();
box.setLayout(new LineLayout(box, LineLayout.Y_AXIS));
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
If you try this out, you will note that there are still notable border spaces between the "XX" labels, taking up about 2/3 of an extra label per gap. While already much better than in the BoxLayout example, I do not think there is a good way to improve this spacing further.
private static int MAX_HEIGHT = 40;
private static final Dimension DIMENSION = new Dimension(Integer.MAX_VALUE, MAX_HEIGHT);
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS){
private static final long serialVersionUID = 1L;
#Override
public Component add(Component comp) {
comp.setMaximumSize(DIMENSION);
return super.add(comp);
}
};
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
}
You are using a Box for adding your components into. And the Documentation says:
a Box can use only a BoxLayout.
Now lets look into the Documentation for BoxLayout. It says:
BoxLayout pays attention to a component's requested minimum, preferred, and maximum sizes.
Now we have found the reason for the different outputs of your two examples. In your first example you are adding JLabels directly to your Box. Since they have a default maximumSize depending on their content they are not scaled by the Box.
In your second example you are adding JPanels to the Box that have your JLabels in it. A JPanel does not have a default maximumSize and so it is scaled by the Box.
So if you want to get the same output with JPanels as without you need your JPanels to have a maximumSize depending on their content means the JLabels.
So you could set a maximumSize manually. Something like that:
panel.setMaximumSize(new Dimension(100,20));
Or you use a different LayoutManager with your JPanels. One that calculates its size depending on its components. One that pays attention to a component's requested minimum, preferred, and maximum sizes.
Does this sound familiar to you? Right its from the Documentation of BoxLayout. So try to use a BoxLayout on your JPanels and you will get exactly the same result as your first example.
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

How to have a class create its own instance of Graphics

I have a class called CharMove,in it are the paint(Graphics g) method, and some custom methods. The class should create a square,then move that square randomly around the screen. However,when I create two instances of this class in my World Class,only one square appears. First the square doesn't move but the new coord.'s are displayed,then after 5 runs the square begins to move randomly. I think the program is getting caught on the Graphics method because only one square is being created,when the CharMove class should be creating another instance of Graphics.I have searched online but can't find a way to create different instances of Graphics.Thanks in advance.
CharMove Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class CharMove extends JPanel {
int x = 250;
int y = 250;
public void paint(Graphics g) {
Graphics pane = (Graphics2D) g;
pane.setColor(Color.blue);
pane.fillRect(x, y, 10, 10);
}
public void movement(JFrame frame) {
for (int i=0;i<5;i++) {
try {
TimeUnit.SECONDS.sleep(1);
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public int Getx(int a, JFrame frame) {
Random rn = new Random();
int xnum = rn.nextInt(10)-5;
a += xnum;
System.out.println("x:" + a);
return a;
}
public int Gety(int b, JFrame frame){
Random rn = new Random();
int ynum = rn.nextInt(10)-5;
b += ynum;
System.out.println("y:" + b);
return b;
}
}
World Class
import java.awt.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import java.util.Random;
public class World {
public static void main(String[] args) {
JFrame game = new JFrame();
game.setTitle("Matrix");
game.setSize(500, 500);;
game.getContentPane().setBackground(Color.white);
game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.setVisible(true);
CharMove char1 = new CharMove();
CharMove char2 = new CharMove();
game.add(char1);
game.add(char2);
char1.movement(game);
char2.movement(game);
}
}
In swing, all of your painting should be down in paintComponent(Graphics g) (rename your method)
To do animation, you should use a Swing Timer (w/ an ActionListener) to update the positions of your animated items. Once that's done, the timer should call repaint();
public void actionPerformed(ActionEvent ae) {
this.x = Getx(this.x,frame);
this.y = Gety(this.y,frame);
frame.repaint();
}
However,when I create two instances of this class in my World Class,only one square appears.
The default layout manager for a JFrame is a BorderLayout.
game.add(char1);
game.add(char2);
When you add components without specifying a constraint then both components are added to the CENTER. However, only one component can be added to the CENTER so only the last one added is displayed.
Try:
game.add(char1, BorderLayout.PAGE_START);
game.add(char2, BorderLayout.PAGE_END);
However when you do this the componens won't be displayed because they have a (0, 0) preferredSize. So you will also need to override the getPreferredSize() method of your CharMove class.
#Override
public Dimension getPreferredSize()
{
return new Dimension(300, 200);
}
Also, custom painting should be done in the paintComponent(...) method and you need to invoke super.paintComponent(...) at the start to clear the background.
The repaint() method in your movement() method should be on the panel, not the frame, since you are changing properties of the panel.
Each CharMove is essentially a JPanel which draws a single square of size 10 somewhere on itself when it is painted. You are adding two CharMove panels to the game JFrame (which in fact adds them to the default content pane, which has a subclassed BorderLayout). As you are not providing a layout constraints object, in fact both panels are being added to the BorderLayout.CENTER of the content pane, and the second is completely covering the first.
To correct this you should modify CharMove so that it paints all of the squares (eg by maintaining an array or some sort of collection of squares, and painting all of them in the paint method) and just add that one panel to the JFrame.
Apart from this issue, while you are animating the squares in the movement method you are blocking the Event Dispatch Thread, meaning that during the animation you won't be able to move any other windows or respond to any mouse clicks or other inputs. Take ControlAltDel's advice about using the Swing Timer for animation to correct this problem.

Unresizable JFrame pack error

JFrame's pack() method won't work every time when the window is not resizable it seems. Try it for yourself (it may need a few retries) please:
import javax.swing.*;
import java.awt.*;
public class FramePackBug {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
JFrame window = new JFrame("Buggy");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setBackground(Color.RED);
JPanel jp = new JPanel() {
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 100);
}
};
jp.setPreferredSize(new Dimension(200, 100));
window.add(jp);
window.pack();
window.setLocation((i % 5) * 250, (i / 5) * 150);
window.setVisible(true);
}
}
});
}
}
(You can fix it by making the frame visible directly after making it unresizable.)
Why is that so?
For reference, here's the appearance of a variation on Mac OS X. Note,
A one-pixel disparity suggests the possibility of space intended for a focus indicator; but as #Marco13 comments, the occasional appearance suggests a (non-obvious) threading issue.
As the appearance seems platform dependent, a label displays the OS and version system properties.
The example overrides getPreferredSize() to establish the enclosed panel's geometry, as suggested here.
The call to super.paintComponent() is not strictly necessary if the implementation honors the opacity property by filling every pixel.
The irregular locations seen in #AyCe's Windows screenshot are consisted with a threading issue suggested by #Marco13.
Mac OS X:
Windows 7 (#AyCe):
Ubuntu 14:
import javax.swing.*;
import java.awt.*;
public class FramePackBug {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
for (int i = 0; i < 20; i++) {
JFrame window = new JFrame("Buggy");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setBackground(Color.RED);
JPanel jp = new JPanel() {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, getWidth(), getHeight());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
};
window.add(jp);
window.add(new JLabel(System.getProperty("os.name") + ", "
+ System.getProperty("os.version")), BorderLayout.NORTH);
window.pack();
window.setLocation((i % 5) * (window.getWidth() + 5),
(i / 5) * (window.getHeight() + 5) + 20);
window.setVisible(true);
}
}
});
}
}
EDIT: See below for updates
Sorry, this is not (yet?) the final solution, but I'm still investigating this and wanted to share the first insight, maybe it's helpful for others as well, for a further analysis:
The odd behavior is caused by the getInsets() method of the frame. When overriding the method, one can see that it returns insets where left, right and bottom are 3 in most cases, but they are 4 when the error appears:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FramePackBug {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
final int ii = i;
JFrame window = new JFrame("Buggy")
{
#Override
public Insets getInsets()
{
Insets insets = super.getInsets();
if (insets.left == 4)
{
System.out.printf(
"Wrong insets in %d : %s\n", ii, insets);
}
return insets;
}
};
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setBackground(Color.RED);
JPanel jp = new JPanel() {
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 100);
}
};
jp.setPreferredSize(new Dimension(200, 100));
window.add(jp);
window.pack();
window.setLocation((i % 5) * 250, (i / 5) * 150);
window.setVisible(true);
}
}
});
}
}
I've already scanned the underlying implementation, which eventually delegates to the WWindowPeer and some native methods that do some odd computations based on the window style (that is, depending on whether the window is resizable or not), and I have identified some candidate reasons for the error, but still no final conclusion. (The fact that this seems to happen randomly does not make debugging easier, of course...)
A side note: Simply calling the pack() method twice caused the frames to always appear properly for me, but of course, this could at best only be considered as a hacky workaround, as long as the root cause for the odd behavior is not identified.
Update
I dug one level deeper, but still did not find a final solution.
The initialization method of the sun.awt.windows.WFramePeer class is implemented as follows:
void initialize() {
super.initialize();
Frame target = (Frame)this.target;
if (target.getTitle() != null) {
setTitle(target.getTitle());
}
setResizable(target.isResizable());
setState(target.getExtendedState());
}
The super call goes into sun.awt.windows.WWindowPeer, and there it does the following:
void initialize() {
super.initialize();
updateInsets(insets_);
...
}
The updateInsets call ends at a native method. The native implementation of updateInsets does some suspicious calls, querying the "style" of the window, and adjusting the insets based on a WS_THICKFRAME property, e.g.
if (style & WS_THICKFRAME) {
m_insets.left = m_insets.right =
::GetSystemMetrics(SM_CXSIZEFRAME);
What I can say for sure is that the result of these updateInsets calls is wrong. They are too large for the unresizable frames, which eventually causes the errors. What I can not say for sure is: Where the wrong insets are (sometimes) updated (for some frames) to properly reflect the size for the unresizable frame.
A potential fix?
As it can be seen in the first of the updated snippets, the initialization of the WFramePeer eventually calls
setResizable(target.isResizable());
The setResizable once more delegates to a native method eventually, and in the native implementation of the setResizable method, there is this style and the WS_THICKFRAME again.
So changing the sun.awt.windows.WFramePeer#initialize() method to first set the "resizable" property, and then passing the call upwards (where it will cause updateInsets to be called) resolved the issue for me:
void initialize() {
Frame target = (Frame)this.target;
setResizable(target.isResizable());
super.initialize();
if (target.getTitle() != null) {
setTitle(target.getTitle());
}
setState(target.getExtendedState());
}
With this modification, the insets are properly computed, and the frames are always displayed correctly.
I am not sure if this is right but you could try putting the setResizable() method after the pack method since the setResizeable(false) might be disabling the pack function.

Maintain component size by centring with buffers

I am building a small chess board for tactics, it would be a fun way to reflect upon on interests (programming and chess).
One problem I have currently face, although solved, is maintaining the board aspect ratio of 1:1.
The Board extends JPanel. Due to a problem with constraints, I have opted towards maintaining the board's physical size rather than it's rendered size. This would lead to faster operations when actually being used.
What I want it to look like, and have achieved:
The way I achieved this though seemed very hackish and is poor code by Skeet standards (thank you based Skeet).
public Frame() {
final JFrame frame = new JFrame("Chess");
final JPanel content = new JPanel(new BorderLayout());
final JPanel boardConfine = new JPanel(new BorderLayout());
final Board board = new Board();
boardConfine.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int min = Math.min(boardConfine.getWidth(), boardConfine.getHeight());
int xBuffer = (boardConfine.getWidth() - min) / 2;
int yBuffer = (boardConfine.getHeight() - min) / 2;
board.setBounds(xBuffer, yBuffer, min, min);
}
});
boardConfine.add(board, BorderLayout.CENTER);
content.setBackground(new Color(205, 205, 205));
content.add(boardConfine, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(content);
frame.pack();
frame.setVisible(true);
}
As seen above, I manually set the board's size and location. Even I have stated exhaustively that this shouldn't ever be done, but I couldn't find a solution to work. I need the board to fill the maximum possible area, yet maintain the aspect ratio.
If there are any suggestions (either code or concepts) you can provide, I really thank you for taking the time to help me with this elitist conundrum.
Although not a complete solution, the example below scales the board to fill the smallest dimension of the enclosing container. Resize the frame to see the effect.
Addendum: The ideal solution would be Creating a Custom Layout Manager, where you have access to the enclosing container's geometry, and setBounds() can maintain the desired 1:1 aspect ratio. A variation of GridLayout may be suitable. Grid coordinates can be calculated directly, as shown here.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/19531648/230513
*/
public class Test {
private static class MyPanel extends JPanel {
private static final int N = 8;
private static final int TILE = 48;
#Override
public Dimension getPreferredSize() {
return new Dimension(N * TILE, N * TILE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.gray);
int w = this.getWidth();
int h = this.getHeight();
int tile = Math.min(w, h) / N;
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++) {
if ((row + col) % 2 == 0) {
g.fillRect(col * tile, row * tile, tile, tile);
}
}
}
}
}
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}

Categories

Resources