Adding onResizeListener to JButton - java

I have a window(JFrame) which contains special types of JButtons (my own class of buttons which extend JButton). That window suppose to resize itself when the button size is changing(in example when I click on the button its font size increases and the whole size of JButton too).
I do not want make JFrame fit the buttons, but let me decide how big the window should be, so pack() is no solution.
I was thinking about a kind of component listner which would resize window on button size change but I could not find anything.
Code example(working, click the button):
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JOptionPane;
public class ResizingButton extends JButton {
public ResizingButton(String txt) {
super();
setText(txt);
addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
int newFontSize = getFont().getSize() + 1;
setFont(new Font(Font.SANS_SERIF, Font.PLAIN, newFontSize));
FontMetrics metrics = getFontMetrics(getFont());
int width = metrics.stringWidth(getText());
int height = metrics.getHeight();
Dimension newDimension = new Dimension(width + 40, height + 10);
setPreferredSize(newDimension);
setBounds(new Rectangle(getLocation(), getPreferredSize()));
}
});
}
}
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Zadanie2 extends JFrame {
JButton jb1, jb2;
public Zadanie2() {
createGUI();
}
private void createGUI() {
setSize(200, 80);
setLayout(new FlowLayout());
add(new ResizingButton("tekst"));
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new Zadanie2();
}
}

That window suppose to resize itself when the button size is changing(in example when I click on the button its font size increases and the whole size of JButton too).
Override the getPreferredSize() of your custom button.
I do not want make JFrame fit the buttons, but let me decide how big the window should be, so pack() is no solution.
Now when you invoke pack() the frame will be resized taking into account the new preferred size of the button.
I was thinking about a kind of component listner which would resize window on button size change but I could not find anything.
The ComponentListener has a componentResized() event. So you would add the listener to the button. You would only use this approach if your automatic resizing code is different then just using pack().

see ComponentListener
ComponentListener firing one event per one pixel on resize
add Swing Timer if event to the Swing GUI will be done only once time, if resize ended
Swing Timer with small dealy 350-500ms, if resize still continue then to test Timer.isRunning, if return true then only Timer#restart(),

Related

Swing JDialog width too wide

I am trying to make a modeless dialog menu in Swing that is displayed upon the press of a button. The dialog contains several menu items. My problem is that the dialog window is much wider than necessary. I am looking for help on setting the window width.
Here's what the output looks like. Notice that the window containing the menu items is much wider than the items themselves. That's what I want to fix.
Here's minimal code that shows this problem:
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test().run();
}
TestDialog testDialog;
private void run() {
JFrame jframe = new JFrame();
JButton jbutton = new JButton("test");
jframe.add(jbutton);
jbutton.setBounds(130, 100, 100, 40);
jframe.setSize(400, 500);
jframe.setLayout(null);
jframe.setVisible(true);
testDialog = new TestDialog(SwingUtilities.windowForComponent(jframe));
jbutton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
testDialog.show();
}
});
}
private class TestDialog {
JDialog jdialog;
public TestDialog(Window parent) {
jdialog = new JDialog(parent, "Test", Dialog.ModalityType.MODELESS);
jdialog.setPreferredSize(new Dimension(100, 0));
jdialog.setLayout(new BoxLayout(jdialog.getContentPane(), BoxLayout.Y_AXIS));
JMenuItem jmenuItem1 = new JMenuItem("MenuItem 1");
Dimension jmiDimension = jmenuItem1.getPreferredSize();
System.out.printf("jmenuItem1 is %f x %f%n", jmiDimension.getWidth(), jmiDimension.getHeight());
jdialog.add(jmenuItem1);
jdialog.add(new JMenuItem("MenuItem 2"));
jdialog.pack();
Dimension d = jdialog.getSize();
System.out.printf("jdialog is %f x %f%n", d.getWidth(), d.getHeight());
}
public void show() {
jdialog.setVisible(true);
}
}
}
The program prints this output, showing that the dialog is 324 pixels wide but the menu items are 87:
jmenuItem1 is 87.000000 x 21.000000
jdialog is 324.000000 x 88.000000
I have also tried using the jdialog.setSize() and jdialog.setMaximumSize() methods. And I've tried setting the maximum size of the menu items. None of them seem to have any affect upon the dialog's window size.
I also tried a GridLayout, rather than a BoxLayout - that also made no difference.
I also tried setting the width of the dialog's content pane and layered pane. Still no difference.
I noted that the dialog has no owner nor parent.
testDialog = new TestDialog(SwingUtilities.windowForComponent(jframe));
You don't need to use the windowForComponent(...) method. You already have a reference to the parent frame:
testDialog = new TestDialog( jframe );
Don't attempt to hard code sizes. Each component will determine its own preferred size and the then layout manager will use this information to determine the overall size.
//jdialog.setPreferredSize(new Dimension(100, 0));
A JMenuItem was designed to be used on a JMenu. Instead you should be using a JButton to add to a regular panel.
I don't have a problem with the size. The width/height of the dialog is as expected.
Note I use JDK 11 on Windows 10. When I ran your original code, the dialog had no size since the setPreferredSize() statement caused the height to be 0. So I'm not sure how you get the display shown in your image.
And yes the dialog width is wider than the component added to the frame because there is a minimum width to the dialog to be able to be able to display the title bar even if no components are added to the dialog.

Swing - Remove selected border and change arrow color of JComboBox

I'm trying to remove the selected border of a JComboBox (top arrow) and change the arrow color (bottom arrow)
If possible, how do I remove the outer border? (the darker gray one)
How do I go about doing this?
So you can do this by implementing a ComboBoxUI, or, actually, subclassing BasicComboBoxUI for example. The second option is better, because you only need to tweak some code in some places and you are ready (instead of implementing your own ComboBoxUI from scratch). So follows the code which does what you requested (hopefully):
import java.awt.Color;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox); //Uninstalls the LAF border for both button and label of combo box.
}
#Override
protected JButton createArrowButton() {
//Feel free to play with the colors:
final Color background = Color.CYAN.darker(); //Default is UIManager.getColor("ComboBox.buttonBackground").
final Color pressedButtonBorderColor = Color.RED; //Default is UIManager.getColor("ComboBox.buttonShadow"). The color of the border of the button, while it is pressed.
final Color triangle = Color.BLACK; //Default is UIManager.getColor("ComboBox.buttonDarkShadow"). The color of the triangle.
final Color highlight = background; //Default is UIManager.getColor("ComboBox.buttonHighlight"). Another color to show the button as highlighted.
final JButton button = new BasicArrowButton(BasicArrowButton.SOUTH, background, pressedButtonBorderColor, triangle, highlight);
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
}
public static void main(final String[] args) {
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
First of, we remove the border from the combo box inside installDefaults() method. That will remove the border from both the button and the label. Then we mimic the createArrowButton() method of BasicComboBoxUI to create our own custom button. The default implementation of BasicComboBoxUI.createArrowButton() creates a BasicArrowButton with some construction-time colors. Those colors change the button's triangle's color for example and the background color.
That is for creating a BasicArrowButton. Although, if you want better control over the button (for example instead of a triangle you need an icon and/or some text on the button) then you can simply create a plain JButton inside the createArrowButton() instead of a BasicArrowButton. Then you can initialize it inside the same method, or, preferably modify it inside the configureArrowButton(). Like the following code does:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI2 {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox);
}
#Override
protected JButton createArrowButton() {
final JButton button = new JButton("V");
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
#Override
public void configureArrowButton() {
super.configureArrowButton(); //Do not forget this!
arrowButton.setBackground(Color.CYAN.darker());
arrowButton.setForeground(Color.BLUE);
}
}
public static void main(final String[] args) {
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
combo.setPreferredSize(new Dimension(150, 45)); //Needed to be able to show the button's text.
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
The only problem with the custom plain JButton approach (ie the second approach) is that the LayoutManager of the JComboBox will not take into account the preferred size of the arrow button if a protected flag inside the UI (named squareButton) is true. If that flag is true, then the button's width is set equal to the button's height, which in turn is set equal to the combo box height. That's why in the second approach, I set the preferred size of the combo box manually to give it a height of 45 pixels which is enough to be able to display the text inside the button (in that specific case, where the text is equal to "V").
You can set the value of this flag for a single UI instance manually by overriding installDefaults() method in the BasicComboBoxUI and setting it to false for example. Otherwise, you can set its value for all combo boxes via calling UIManager.put("ComboBox.squareButton", Boolean.FALSE); in your code early enough (ie before the instantiation of each combo box). But as it turns out, because I tested it, when setting this value to false the size of the button overlaps the display area's size (the display area is where the selected value of the combo box is visible).
After some more digging, I found out that the cause of this problem is that the UI's getMinimumSize(...) method does not take into account the button's insets... That means the default border of the JButton around the text/icon is not accounted for. So after setting the squareButton flag to false, you will also need to override this method.
Putting them all together, you get the following final approach/code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicComboBoxUI;
public class MainComboBoxUI3 {
public static class MyComboBoxUI extends BasicComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox);
}
#Override
protected JButton createArrowButton() {
final JButton button = new JButton("Drop");
button.setName("ComboBox.arrowButton"); //Mandatory, as per BasicComboBoxUI#createArrowButton().
return button;
}
#Override
public void configureArrowButton() {
super.configureArrowButton(); //Do not forget this!
arrowButton.setBackground(Color.CYAN.darker());
arrowButton.setForeground(Color.BLUE);
//arrowButton.setBorder(javax.swing.BorderFactory.createEmptyBorder());
}
//Overrided getMinimumSize to take into account the button's insets for both width and height:
#Override
public Dimension getMinimumSize(final JComponent c) {
final Dimension mindim = super.getMinimumSize(c);
final Insets buttonInsets = arrowButton.getInsets();
return new Dimension(mindim.width + buttonInsets.left + buttonInsets.right, mindim.height + buttonInsets.top + buttonInsets.bottom);
}
}
public static void main(final String[] args) {
UIManager.put("ComboBox.squareButton", Boolean.FALSE); //Set all the combo boxes' button to non-square...
final JComboBox<String> combo = new JComboBox<>(new String[]{"A string", "B string 2", "C string 3"});
combo.setUI(new MyComboBoxUI());
final JPanel panel = new JPanel(new GridBagLayout());
panel.add(combo);
panel.setBackground(Color.RED.darker());
final JFrame frame = new JFrame("MainComboBoxUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
And the result looks like this:
Finally, if you want smaller border for the drop down button (ie the one with text "Drop" in the above picture), you can set the border of the arrowButton to (for example) javax.swing.BorderFactory.createEmptyBorder() inside the configureArrowButton() method...

Re-adding WorldWindowGLJPanel to JFrame causes GL viewport miscalculation

If I add an instance of WorldWindowGLJPanel to my JFrame then remove it and add it again, the panel's GL viewport gets recalculated to a Rectangle that is much smaller than the available space. The dimension appears to consistently be 116x26. What this means is the frame becomes mostly blank with just a small piece of the WorldWind panel displaying in the bottom left corner of the frame. Resizing the frame appears to reset the viewport but is there a way to reset the viewport programmatically?
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import gov.nasa.worldwind.Model;
import gov.nasa.worldwind.WorldWind;
import gov.nasa.worldwind.avlist.AVKey;
import gov.nasa.worldwind.awt.WorldWindowGLJPanel;
public class WorldWindTest {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final WorldWindowGLJPanel wwPanel = new WorldWindowGLJPanel();
Model wwModel = (Model) WorldWind.createConfigurationComponent(AVKey.MODEL_CLASS_NAME);
wwPanel.setModel(wwModel);
JPanel buttons = new JPanel(new FlowLayout());
buttons.add(new JButton(new AbstractAction("Re-add WorldWind panel") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
frame.getContentPane().remove(wwPanel);
frame.getContentPane().add(wwPanel);
frame.getContentPane().repaint();
}
}));
frame.getContentPane().add(buttons, BorderLayout.NORTH);
frame.getContentPane().add(wwPanel);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
}
}
4 and half years late to the party, but posting my solution because I couldn't find one when I ran into this issue.
This problem seems to have nothing to do with WorldWindowGLJPanel, I believe it's a problem with any GLJPanel.
When you remove the GLJPanel from its parent, it resets its internal pixel scaling to some sentinel values. That's why it becomes tiny, but continues to (sorta) work.
The following 3 lines of code fixed it for me (I add them to a hierarchy listener and call them when the parent changes, but you can add them to the SSCCE above in the button handler)
wwPanel.initializeBackend(false);
wwPanel.reshape(0, 0, 0, 0);
wwPanel.revalidate();
initializeBackend tells OpenGL to initialize the backend, if it isn't initialized (Not 100% sure what causes it, but it seems to be tied to moving between parent windows).
Reshape will overwrite those pixel scale sentinel values, and so it'll actually calculate the proper size to display.
Revalidate causes it to recalculate the size of the panel, which I believe kicks off the size calculations now that those sentinel values aren't in the way.

Creating a mouselistener over a single Jpane

it's my first post so I hope it'll not be too cringeworthy. So I am trying to create a hex-based strategy game, not quite there yet but anyways.
To achieve a hex-based game I would like to create a field made of hexes which the user should be able to click, and receive the coordinates of that pixel. At the moment I can produce either a field of hexes or a mouselistener/mouseadapter but not both. The last one executed replaces the other on the screen.
If the pane.add(New HexMap()); is switched with pane.add(new MouseListener()); the listener works but the line is not printed
I've looked around for quite some time but the posts that I've encountered had either dealt with changing the background color which the mouselistener can do, because background is independent of the mousesensorhttp://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html? The other examples I've come by have been too advanced for me, because they're using multiple panes, and I have not been able to comprehend themhttp://docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html.
So what I'm looking for is a way to add a mouselistener over a single pane, displaying the hexes. Would this be possible?
E.G adding the hexMap after the mouselistener would not overwrite the mouselistener but rather act as an addition
A single line has been created acting as a placeholder for the hexes.
The code:
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
public class GraphicsSetup extends JPanel{
public static final int FRAME_WIDTH = 600;
public static final int FRAME_HEIGHT= 400;
private static JFrame frame;
public static void main(String[] args){
GraphicsSetup draw = new GraphicsSetup();
}
public GraphicsSetup(){
HexMap hexMap = new HexMap();
JPanel panel = new JPanel();
frame = new JFrame("HexExample");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(FRAME_WIDTH,FRAME_HEIGHT);
Container pane = frame.getContentPane();
pane.setBackground(new Color(20, 100, 30));
pane.add(new MouseListener());
pane.add(new HexMap());
frame.setVisible(true);
}
public class HexMap extends JComponent{
public void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.drawLine(0,0, FRAME_WIDTH, FRAME_HEIGHT);
}
}
class MouseListener extends JComponent{
public MouseListener(){
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent me) {
System.out.println("Mouse Event" + me);
}
});
}
}
}
Yours Sincerely
I'm not entirely sure what you're after, but try adding your components to your panel object. Such as:
panel.add(new MouseListener());
panel.add(new HexMap());
And then add this to the content pane of your frame:
pane.add(panel);
If you're wondering how to arrange your interface differently, read about layout managers here:
http://docs.oracle.com/javase/tutorial/uiswing/layout/visual.html
Edit
Try the following:
Set the layout manager to use a BorderLayout:
JPanel panel = new JPanel(new BorderLayout());
Add your components to the panel and set their location:
panel.add(new MouseListener(), BorderLayout.NORTH);
panel.add(new HexMap(), BorderLayout.CENTER);
Add the panel to the frame content pane:
pane.add(panel);
This will work but the size of the MouseListener panel is quite small...you'll need to figure that out next...

Re-position and change buttons to images?

I am trying to re-position the button so that it is in the bottom right corner of the frame, but everything I try such as setLocation and setBounds, don't seem to do anything. Also, how would I change the button to an image? So that it is still a button, but an image is displayed.
package TrainCounselor;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Start extends JPanel {
public Start() {
// Game Properties
setOpaque(false);
setLayout(new FlowLayout());
}
public void paint(Graphics g) {
Image a = Toolkit
.getDefaultToolkit()
.getImage(
"C:/Users/Mel/workspace/camptycoon/javagame/src/javagame/background1.png");
g.drawImage(a, 0, 0, this);
super.paint(g);
}
public static void main(String[] args) {
JFrame myFrame = new JFrame("Put Image");
JButton startButton = new JButton("Start");
startButton.setLayout(null);
startButton.setLocation(50, 50);
Start c = new Start();
c.add(startButton);
myFrame.add(c);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setTitle("Counselor Training");
myFrame.setSize(755, 600);
myFrame.setResizable(false);
myFrame.setVisible(true);
}
}
I am trying to re-position the button so that it is in the bottom right corner of the frame
Use the appropriate Layout Managers.
I would start by using a JPanel with a FlowLayout that is right aligned. Then you add this panel to the "SOUTH" of the BoderLayout which is used by the JFrame.
See A Visual Guide to Layout Managers for more information.
Note when you add the Start class to the frame you are adding it to the "CENTER", not the south. Also, custom painting is done by override the paintComponent() method, not the paint() method and don't forget to invoke super.paintComponent() before you draw the image, not after.
Also, how would I change the button to an image? So that it is still a button, but an image is displayed.
Read the section from the Swing tutorial on How to Use Buttons.

Categories

Resources