JButtons in GridLayout get resized from a certain aspect ratio - java

I am building a Minesweeper clone, and my startNewGame() method works well. In that method, I also change the field size if the difficulty has been changed.
Here is how it looks on
easy (9x9):
And here is how it looks on intermediate (16x16).
It also works well if I have 20x20, 30x30, etc. The problem is that if I change the board to 30x16 (expert), it looks like this. It seems like it starts stretching the buttons after a certain aspect ratio for some reason. I set the minefield JPanel's preferred size to [16 * x, 16 * y] (each cell is 16x16).
Why are my buttons stretching and how can I prevent that from happening?
Here is my startNewGame() method (keep in mind that most things are static so their declarations aren't here):
public static void startNewGame()
// Resets and starts a new Game.
{
timer.stop();
timerLabel.setText("00:00");
clicks = 0;
int newGridLength, newGridHeight, newNumOfMines;
if (difficulties[0].isSelected())
{
newGridLength = 9;
newGridHeight = 9;
newNumOfMines = 10;
}
else if (difficulties[1].isSelected())
{
newGridLength = 16;
newGridHeight = 16;
newNumOfMines = 40;
}
else if (difficulties[2].isSelected())
{
newGridLength = 30;
newGridHeight = 16;
newNumOfMines = 99;
}
else
{
newGridLength = 9;
newGridHeight = 9;
newNumOfMines = 10;
}
GRID_LENGTH = newGridLength;
GRID_HEIGHT = newGridHeight;
NUM_OF_MINES = newNumOfMines;
remainingMines = NUM_OF_MINES;
remainingMinesLabel.setText("" + remainingMines);
buttonGrid.clear();
cellGrid.clear();
frame.remove(gridPane);
gridPane = new JPanel();
gridPane.setLayout(new GridLayout(GRID_LENGTH, GRID_HEIGHT));
initializeGrid(gridPane);
gridPane.setPreferredSize(new Dimension(CELL_SIZE * GRID_LENGTH, CELL_SIZE * GRID_HEIGHT));
frame.add(gridPane, BorderLayout.CENTER);
frame.pack();
}

I read more into other layouts and converted my program to GridBagLayout, and it's working as intended now.
Result:
Expert (30x16)

gridPane.setLayout(new GridLayout(GRID_LENGTH, GRID_HEIGHT));
The GridLayout uses
new GridLayout(rows, columns)
as the parameters.
You are passing the parameters in the wrong order.
Note the easiest to create a GridLayout is to only specify a single non-zero value.
So I would use:
gridPane.setLayout(new GridLayout(0, GRID_LENGTH));
This will specify 30 columns.
Now as you add components to the grid it will wrap every 30 components.

Related

Make Text Bigger and put breaks in the code (java Applet)

I wanted to know how I can make the text of my labels bigger and put breaks in between of the labels. So basically for the code I want it to be:
(Big) Ohms Law
(A bit smaller) Voltage (textbox + button here)
(Same size as Voltage) Resistance (textbox + button here)
So far I have this:
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
public class OhmsLawApplet extends Applet implements ActionListener
{
// declare variables
int RESISTANCE;
int VOLTAGE;
int OHMS;
int V = 0;
int R = 0;
int I = 0;
//construct components
Label OhmsLabel = new Label("Ohms Law"); // Label at the top
Label VOLTAGELabel = new Label("Voltage:"); // Label for Voltage
TextField VOLTAGEField = new TextField(); //Textfield to input Voltage
Label RESISTANCELabel = new Label("Resistance:"); // Label for Resistance
TextField RESISTANCEField = new TextField(); //Textfield to input Resistance
Button CALCULATEButton = new Button("Calculate");
public void init(){
// Add the components to the Applet
setForeground(Color.black);
add(OhmsLabel);
add(VOLTAGELabel);
add(VOLTAGEField);
add(RESISTANCELabel);
add(RESISTANCEField);
add(CALCULATEButton);
CALCULATEButton.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
// UNFINISHED/ DON'T KNOW HOW TO DO YET
VOLTAGE = Integer.parseInt(VOLTAGEField.getText());
RESISTANCE = Integer.parseInt(RESISTANCEField.getText());
VOLTAGE = I*R;
RESISTANCE = V/I;
}
}
You can use Component#setFont(Font) to set the font and thus update the textsize:
Label label = ...
int newSize = ...
label.setFont(label.getFont().getName() , label.getFont().getStyle() , newSize);
This piece of code will create a new font with the style-attributes but a different fontsize.
As for changing the distance of the labels: that depends upon the used LayoutManager. You can set the size of each component manually using Component#setPreferredSize(Dimension), which will work for most LayoutManagers. But the distance of the components solely depends upon the used LayoutManager. You might want to have a look at the GridBagLayout

gridbag layout GUI

I am having problems with the gridbag layout, I am trying to get the images to be on one line horizontally, however when I add images to the left of the original one, it will go down vertically instead of going horizontally to the left.
I have tried to specify the gridY to be the same, however this did not work, also I have tried the GridBayConstarints.Horizontal however that was irrelevant.
So far I have
import java.awt.GridBagConstraints;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class main {
public static void main(String[] args)
{
GUI g = new GUI();
int turnCounter = 1;
for(int i = 0; i<6; i++)
{
String image = "src/resources/bone"+0+i+".png";
GridBagConstraints c = new GridBagConstraints();
//ADDS IMAGE TO THE LEFT
if(turnCounter%2 == 0)
{
c.gridy = c.gridy;
c.gridx = c.gridx-1;
ImageIcon icon1 = new ImageIcon(image);
JLabel label1 = new JLabel((ImageIcon) icon1);
g.add(label1,c);
g.revalidate();
turnCounter++;
}
//ADD IMAGES TO THE RIGHT
else if(turnCounter%2 == 1)
{
c.gridy = c.gridy;
c.gridx = c.gridx+1;
ImageIcon icon1 = new ImageIcon(image);
JLabel label1 = new JLabel((ImageIcon) icon1);
g.add(label1,c);
g.revalidate();
turnCounter++;
}
}
}
}
What the image looks like at the moment.
The [2|0] and [3|0] should be to the left of [0|0] in that order so it will look like this
[4|0][2|0][0|0][1|0][3|0][5|0] all in one line.
The code you used for putting things to the right is correct, but as the default value of gridx is 0, what you are doing when you try to add a component to the left is giving a value to gridx of -1, an incorrect value for gridx, seems like the problem is there.
The solution I can give right now, is making an array with a lenght of how many images you want to display, in this case it would be a:
int yourLenght = 5; //Sounds dirty lol
JLabel[] yourArray;
yourArray = new JLabel[myLenght]
After this, only select the place of the array where you want to place it, I would recommend it in the middle.
Why does this work?
Now your images can only be on the 5th or 1st column (gridx = 0), but not on -1, altough we haven't finished yet, if you leave it like this, and you add or quit one to "yourLenght" your images could only be placed on the 2nd column or the 4th one, cause you can only add or quit one to 3. To avoid these what we are going to do is declare another int.
int magicInt = 1;
And then after each component is added we will add 1 to the "magicInt" value.
//You already added the object rather on the left or on the right
magicInt++;
And that way each time you add an object you can advance one place to the left or to the right more.
Notes:
1-Anchors won't work if you don't give a value to weighty. You can read more about these variables here.
2-I discourage using
c.gridy = c.gridy;
If you are only going to use that loop for adding things on one row, better use:
c.gridy = 0;
That way your code is easier to read.
3- Solution listed above only works if objects are added consitently with the following pattern : left, right, left, right... or right, left, right, left...
(It was a quick solution, but you will find your solution just think...)
Hope, this helped you, if you have any doubts don't mind commenting that way I can help :)

Getting Each Monitor Inset In Java

Don't mind the use of the Window Insets, but pay more attention to the use of the ScreenInsets, which is saved locally as Insets insets; I print the insets.bottom, and for every monitor the taskbar height shows up, even though the taskbar is only located on the first monitor.
The monitor insets on my second monitor should all be zero, but yet it acts as if the taskbar is located on both monitors. Setting the window to full size in the monitor the window is currently located on works, except it leaves room for the taskbar regardless which monitor are use it in.
From my understanding of the Toolkit.getScreenInsets(GraphicsConfiguration), it should return the correct insets for the specific GraphicsConfiguration you pass in, yet I'm passing in each GraphicsDevice's GraphicsConfiguration and getting the same results back.
JFrame window;
public void setSizeToFullScreen()
{
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] screenDevices=ge.getScreenDevices();
Point p=window.getLocationOnScreen();
for(int i=0;i<screenDevices.length;i++)
{
Rectangle2D b=screenDevices[i].getDefaultConfiguration().getBounds();
if(SMath.getMath().doesRectangleContainPoint(b.getX(), b.getY(), b.getWidth(), b.getHeight(), p.getX(),p.getY()))
{
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(screenDevices[i].getDefaultConfiguration());
System.out.println("Monitor: "+i+": task bar height: "+insets.bottom);
this.setSize(b.getWidth()+1 -(insets.right+insets.left)-(this.window.getInsets().left+this.window.getInsets().right), b.getHeight()+1-(insets.top+insets.bottom)-(this.window.getInsets().top+this.window.getInsets().bottom));
this.setLocation(b.getX()+insets.left+window.getInsets().left, b.getY()+insets.top+window.getInsets().top);
return;
}
}
}
My question is, in Java, how can we figure out which monitor actually has the taskbar, or the better question, how can we get the correct monitor insets for each monitor in Java.
Re: "..., yet it acts as if the taskbar is located on both monitors".
I found the following:
Referring to Windows->Control Panel->Appearance and Personalization->
Display->Screen Resolution:
When gs[0] (= the display shown with a "1" inside a circle in the Control Panel window above) has the Toolbar, the reported Insets are correct.
I.e., they are reported to be = 0 for the no-Toolbar screen and = 49 for the screen that has the Toolbar.
When any other gs[x] has the Toolbar, the reported Insets are wrong :-(. I.e., they are reported to be = 49 for all screens.
In my application, I want "JDialog dialog0" to always appear 500 to the right of the lower-left corner of my "big" display, and "JFrame frameBalloonHerderGui" to always appear in the upper-left corner of my "small" display.
I want the JDialog to have a fixed size in the lower-left corner of my "big" display, and the JFrame should pretty much fill the "small" display it's in.
I did give the graphicsConfiguration for the display I wanted each JDialog/JFrame to appear in to their constructors. Alas, that is not enough to allow the Insets to be correct.
To accomplish the positioning above, I wrote a function that I call for my JDialog and JFrame as follows:
// I'll omit the creation of the grapicsConfiguration for now.
JDialog dialog0 = new JDialog( gc);
ScreenAndTaskBarHeights h = getThisComponentsScreensTaskBarHeight( dialog0);
final int myWidth = 1170;
final int myHeight = 800;
final int myScreenXInset = 500;
final int myScreenYInset = 10;
dialog0.setBounds(
h.screenOriginX + myScreenXInset,
h.screenOriginY + h.screenHeight - myHeight - h.taskBarHeight - myScreenYInset,
myWidth, myHeight);
dialog0.setVisible( true);
// I'll omit the creation of the grapicsConfiguration for now.
JFrame frameBalloonHerderGui = new JFrame( gc);
ScreenAndTaskBarHeights h =
getThisComponentsScreensTaskBarHeight( frameBalloonHerderGui);
final int myWidth = 1695;
final int myScreenInset = 10;
frameBalloonHerderGui.setBounds(
h.screenOriginX + myScreenInset,
h.screenOriginY + myScreenInset,
myWidth, h.screenHeight - (myScreenInset * 2) - h.taskBarHeight);
frameBalloonHerderGui.setVisible( true);
The workaround I found is that the screenSize.x & .y = (0,0) for the display that has the TaskBar, and some big positive or negative numbers for the other displays.
The function below successfully implements this workaround. I also made a simple class so I could pass multiple values back to the caller as shown above.
// Add additional data members here to your liking.
static class ScreenAndTaskBarHeights
{
int screenOriginX = -1;
int screenOriginY = -1;
int screenHeight = -1;
int taskBarHeight = -1;
ScreenAndTaskBarHeights()
{
}
void setValues(
int newScreenOriginX, int newScreenOriginY,
int newScreenHeight, int newTaskBarHeight)
{
screenOriginX = newScreenOriginX;
screenOriginY = newScreenOriginY;
screenHeight = newScreenHeight;
taskBarHeight = newTaskBarHeight;
}
}
static ScreenAndTaskBarHeights
getThisComponentsScreensTaskBarHeight( Component c)
{
ScreenAndTaskBarHeights screenAndTaskBarHeights =
new ScreenAndTaskBarHeights();
GraphicsConfiguration gc = c.getGraphicsConfiguration();
Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets( gc);
// This should be the TaskBar height specific to the gc that we
// passed in, but it's not :-(.
//
// int taskBarHeight = scnMax.bottom;
//
// However, this seems to be a successful workaround:
//
Rectangle screenSize = gc.getBounds();
boolean thisScreenHasTheToolbar =
(screenSize.x == 0 && screenSize.y == 0);
// Change scnMax.bottom to catch wherever you're worried that the
// TaskBar may be lurking.
//
screenAndTaskBarHeights.setValues(
screenSize.x, screenSize.y, screenSize.height,
(thisScreenHasTheToolbar) ? scnMax.bottom : 0);
return screenAndTaskBarHeights;
}

How to change string length (calculating its width in pixel) when changing window size and strange behaviour of JLabel

I have JLabel which I would like to change its size while I resize the window. When JLabel contains String which is too big, the String should be shortened, with right part visible and adds dots on the left hand side of the String.
My JLabel is inside innerPanel which is a header in middlePanel which is added to outerPanel. So when I resize window I use listener on outerPanel in that way:
outerPanel.addComponentListener(new ComponentListener() {
#Override
public void componentResized(ComponentEvent evt) {
int width = ((JPanel) evt.getSource()).getWidth();
windowSize = width;
refresh();
}
// [...] other not used override methods
});
refresh() repaints view and creates new middlePanel where is called class which creates innerPanel where is located my JLabel:
Public class InnerPanel extends JPanel {
private int maxSize;
String string = "<VERY_LONG_STRING>";
private static final int DEFAULT_INDEND_PIXEL = 70;
public InnerPanel(int windowSize) {
maxSize = windowSize - DEFAULT_INDENT_PIXEL;
createPanel();
}
private createPanel() {
// [...] gridbag and GridBagConstraints implementation
String shortString = countString();
JLabel label = new JLabel(shortString);
add(label,gc);
}
private String countString() {
int index = 0;
boolean toBig = true;
StringBuilder sb = new StringBuilder(string);
while(toBig) {
Rectangle2d rect = // [...] code which creates rectangle around text from sb.toString()
// I have no access to repo at home but if it's important I can paste it tomorrow
if(rect.getWidth() > maxSize)
sb.deleteCharAt(0);
else
toBig = false;
}
return sb.toString();
}
}
That's works fine in general, bacause it do resize JLabel in one step when I enlarge window in width. But the problem is appear when I try to reduce the window in width. In this case componentResized() calculate width step by step (and it's called multiple times), gradually decreases width by some amount of pixels till it reach real window size. It's behave in that way even thow I change window size in one step from maximum size to 800. Whole process is so slow, that it takes around a second to fit string to window size. So it looks bit like an animation.
The problem is very rare to me, bacause width in componentResized() method is calculeted step by step only when I assign windowSize variable.
When I give windowSize fixed size like for example 500 - componentResized() is called only onces - with correct width indicated real window size (!!) - and there's no its step by step decrease!
It's look like width variable which is assigned by ((JPanel) evt.getSource()).getWidth() knows that windowSize is used to dynamically change size of JLabel component even before first call of refresh() method.
If anyone have an idea what is going on here - I will be very appreciate for help.
You may be able to adapt one of the approaches shown here to better effect. As shown here, the ellipsis is supplied by the label's UI delegate via a call to SwingUtilities2.clipString(), which appends the clipString. Rather than re-invent the label UI, use TextLayout to determine the required geometry, prepend the ellipsis, and handle the alignment in a table or list renderer, as shown here.

How to repaint out of focus dialog without gaining its focus?

I made some menu and it is to update conmmon variables (for text on grid) then the out-of-focus dialog must repaint the grid. Here is the screenshot:
The main control panel is always at top position and 'Data Display' panel is always sitting behind it. When press a button on front panel, Data Display must update its grid. Currently, the common variable 0.4 on the grid is updated by adding listener and works fine. But the grid itself is not repainting anymore. How can I repaint the out-of-focus dialog in real time?
Here is the code of the front panel:
public class MainDisplayForm extends javax.swing.JFrame {
Storage st = new Storage();
DisplayForm dF = new DisplayForm();
....
public MainDisplayForm() {
initComponents();
Btn_IncreaseGain.addActionListener(new ButtonListener_IncreaseGain());
}
....
} //MainDisplayForm ends here.
class ButtonListener_IncreaseGain implements ActionListener {
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
ButtonListener_IncreaseGain()
{
}
public void actionPerformed(ActionEvent e) {
st.iGain = 20;
dF.revalidate();
dF.repaint();
System.out.println("Testing");
}
}//Listener ends here.
Here is code of Data Display:
public void paint(Graphics g)
{
g2 = (Graphics2D) g;
paintComponents(g2);
//added numbers are for adjustment.
int x = this.jPanel1.getX()+8;
int y = this.jPanel1.getY()+30;
int width = this.jPanel1.getWidth()+19;
int height = this.jPanel1.getHeight()+40;
//labelling voltages
label0.setText(st.zero);
label1.setText(st.v1);
label2.setText(st.v2);
label3.setText(st.v3);
label4.setText(st.v4);
label5.setText(st.v3);
label6.setText(st.v4);
g2.setColor(Color.darkGray);
for(int i=x; i<width; i=i+80)
{
g2.drawLine(i, y, i, height);
}
int j = 0;
for(int i=y; i<height; i=i+80)
{
j++;
//st.iGain
g2.setColor(Color.orange);
if(j==1)
{
double k1 = st.iGain * 0.4;
st.v1 = Double.toString(k1);
g2.drawString(st.v1, x+5, y+10);
}
if(j==2)
{
double k2 = st.iGain * 0.3;
st.v2 = Double.toString(k2);
g2.drawString(st.v2, x+5, y+90);
}
g2.setColor(Color.DARK_GRAY);
g2.drawLine(x, i, width, i);
....
} //grid info is not completed yet.
Thanks,
Focus isn't the issue and has nothing to do with your current problem. The solution is to change the properties of the data grid by updating fields it contains via setter methods and calling repaint on the JComponent (perhaps a JPanel, or some other component that derives ultimately from JComponent) held by the data grid. The paintComponent method of this component should use its class fields to update what it draws.
You almost never paint in the paint method of a JComponent and certainly you don't want to draw directly into a top-level window. You also probably don't want to set text of JLabels, JTextFields, or any other JTextComponent. from within paint/paintComponent.
I can't see why your code is not working and can only guess that the likely cause of your problem is in code not shown.
Edit 1:
Just guessing, but you may have a problem of references. I notice that your listener class creates new DisplayForm and Storage objects:
DisplayForm dF = new DisplayForm();
Storage st = new Storage();
There's a good possibility that these objects are not the ones being displayed, especially if you create these objects elsewhere and display them. Again I'm just guessing since I don't see the rest of your code, but perhaps you should to pass references for these objects into the DisplayForm via constructor or setter method parameters.
Edit 2:
e.g.,
public void setDisplayForm(DisplayForm dF) {
this.dF = dF;
}
// same for Storage
And in the main program:
public MainDisplayForm() {
initComponents();
ButtonListener_IncreaseGain btnListenerIncreaseGain = new ButtonListener_IncreaseGain();
btnListenerIncreaseGain.setDisplayForm(....);
btnListenerIncreaseGain.setStorage(....);
Btn_IncreaseGain.addActionListener(btnListenerIncreaseGain);
}

Categories

Resources