tooltipText bug causes JMenuItem to lose focus - java

I am working on a Java Swing GUI and am having a minor issue with tool tip text on popup menu items.
Basically when you hover over a JMenuItem it is supposed to leave that JMenuItem selected and display the desired tool tip text.
What actually happens is when the tool tip text is made visible a StateChange event seems to cause the relevant JMenuItem to lose selection status and so the tool tip text very quickly disappears. Note that this only happens the first time, if you move your mouse around you can get the JMenuItem to select again and it will also display the tool tip text properly. I could leave this but I was hoping to set a delay through the ToolTipManager's sharedInstance() which at this point would hurt the user-friendly side of things since the user would have to wait twice as long after realizing the issue themselves.
I built a very simple demo that reflects the problem I am seeing, am I missing something or is this just a Java 8 with Mac issue?
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class TestFrame {
static JFrame jf = new JFrame();
public static void main(String[] args){
jf = new JFrame();
JPanel jp = new JPanel();
jp.setBackground(Color.white);
jp.setForeground(Color.black);
JPopupMenu p = new JPopupMenu();
JMenuItem jmi = new JMenuItem("An option");
jmi.setToolTipText("mouse over text");
jmi.addChangeListener(new ChangeListener(){
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("CHANGED by: "+e.getSource().toString());
}});
p.add(jmi);
jp.setComponentPopupMenu(p);
jf.add(jp);
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jf.setSize(1000, 500);
jf.setPreferredSize(jf.getSize());
jf.setVisible(true);
}
}

For reference, I tried this modified version that runs on the event dispatch thread. It's seems improved, but it still fails intermittently. It looks like a regression.
Console:
$ javac TestFrame.java ; java TestFrame
1.8.0_31
10.9.5
…
Code:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.event.ChangeEvent;
/** #see http://stackoverflow.com/a/28160300/230513 */
public class TestFrame {
public static void main(String[] args) {
System.out.println(System.getProperty("java.version"));
System.out.println(System.getProperty("os.version"));
EventQueue.invokeLater(() -> {
JFrame jf = new JFrame();
JPanel jp = new JPanel();
JPopupMenu p = new JPopupMenu();
JMenuItem jmi = new JMenuItem("An option");
jmi.setToolTipText("Mouse over text");
jmi.addChangeListener((ChangeEvent e) -> {
System.out.println("Changed by: " + e.getSource().toString());
});
p.add(jmi);
jp.setComponentPopupMenu(p);
jf.add(jp);
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jf.pack();
jf.setSize(320, 240);
jf.setVisible(true);
});
}
}

Related

Attempting to set the layout to BoxLayout

I can't seem to find a solution online for why I'm getting this error on attempted run
I'm working on making a simple test system for a different program when are button press will yield value in a text box. I would like them to be on different lines to make it cleaner, so I looked into layouts. I decided a Box Layout would fit me best. I looked at different examples before attempting this and my code ended up looking like this, (apologies for the messy code)
Update
Got the box layout error to disappear but the code will not center them on the panel/frame. The label and button align left while the textfield becomes very large. I don't need it todo that
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import static javax.swing.BoxLayout.Y_AXIS;
import static javax.swing.SwingConstants.CENTER;
public class button extends JFrame {
static JFrame f;
static JButton b;
static JLabel l;
// main class
public static void main(String[] args)
{
// create a new frame to stor text field and button
f = new JFrame("panel");
BoxLayout layout = new BoxLayout(f, BoxLayout.Y_AXIS);
f.setLayout(layout);
// create a label to display text
l = new JLabel("panel label");
b = new JButton("button1");
JTextField textArea = new JTextField(5);
textArea.setEditable(false);
//textArea.append("Hello World");
// create a panel to add buttons
JPanel p = new JPanel();
// add buttons and textfield to panel
f.add(p);
f.setSize(300, 300);
p.add(l);
p.add(b);
p.setBackground(Color.white);
p.add(textArea);
f.show();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textArea.setText(String.valueOf(r));
}
});
}
}
Error
Exception in thread "main" java.awt.AWTError: BoxLayout can't be shared
at java.desktop/javax.swing.BoxLayout.checkContainer(BoxLayout.java:461)
at java.desktop/javax.swing.BoxLayout.invalidateLayout(BoxLayout.java:245)
at java.desktop/javax.swing.BoxLayout.addLayoutComponent(BoxLayout.java:278)
at java.desktop/java.awt.Container.addImpl(Container.java:1152)
at java.desktop/java.awt.Container.add(Container.java:1029)
at java.desktop/javax.swing.JFrame.addImpl(JFrame.java:553)
at java.desktop/java.awt.Container.add(Container.java:436)
at button.main(button.java:36)
I would like the three items to all to be stacked one on top of another with a space between them. The order doesn't matter right now.
Swing was first added to the JDK in 1998 and has undergone a lot of changes since. Unfortunately, when you read Web pages about Swing, it is not obvious when that page was last updated. Consequently you may be learning outdated techniques for writing Swing code.
First of all, according to the code you posted, class button does not need to extend class JFrame since you use a static variable as your application's JFrame. Also, JFrame is a top-level container which makes it a special kind of container and not the same kind of continer as a JPanel. You need to set the layout manager for your JPanel and then add the JLabel, JTextField and JButton to that JPanel. And then add the JPanel to the JFrame.
Calling method pack() of class JFrame will automatically set the preferred sizes for the components inside the JFrame. It appears in the code below.
Please also look at Java coding conventions which allows others to more easily read and understand your code. And note that, according to these conventions, I renamed your class from button to Buttons and also because there are already several class in the JDK named Button.
Here is my rewrite of your code...
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
public class Buttons implements Runnable {
public void run() {
createAndShowGui();
}
private void createAndShowGui() {
JFrame f = new JFrame("Box");
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel p = new JPanel();
BoxLayout layout = new BoxLayout(p, BoxLayout.Y_AXIS);
p.setLayout(layout);
JLabel l = new JLabel("panel label");
JTextField textField = new JTextField(5);
JButton b = new JButton("button1");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Random r = new Random();
textField.setText(String.valueOf(r.nextBoolean()));
}
});
p.add(l);
p.add(textField);
p.add(b);
f.add(p);
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
public static void main(String[] args) {
Buttons instance = new Buttons();
EventQueue.invokeLater(instance);
}
}

JavaFX/Swing threading issues when typing in TextField after parent JFrame loses focus

I have a combined Swing/JavaFX application using JDesktopPane and JInternalFrame for MDI and JFXPanel to embed JavaFX fields in the internal frames. This has been working fine up until Java 8u161. With the update, there are a lot of uncaught exceptions getting thrown when users try to type into text fields, most noticeably after the parent JFrame loses focus. Any suggestions for workarounds would be greatly appreciated.
Sample program:
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
public class SwingFXTest {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new SwingFXTest().initAndShowGUI();
});
}
private void initAndShowGUI() {
JFrame frame = new JFrame("Test 8u161");
JDesktopPane desktopPane = new JDesktopPane();
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("Test Menu");
menuBar.add(menu);
JMenuItem menuItem = new JMenuItem("Test Item");
menuItem.addActionListener(e -> {
desktopPane.add(createInternalFrame());
});
menu.add(menuItem);
frame.setJMenuBar(menuBar);
frame.setContentPane(desktopPane);
frame.setSize(600, 600);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private JInternalFrame createInternalFrame() {
JInternalFrame internalFrame = new JInternalFrame();
internalFrame.setVisible(true);
internalFrame.setResizable(true);
internalFrame.setSize(200, 200);
final JFXPanel fxPanel = new JFXPanel();
internalFrame.setContentPane(fxPanel);
Platform.runLater(() -> initFX(fxPanel));
return internalFrame;
}
private void initFX(JFXPanel fxPanel) {
TextField field = new TextField();
Group root = new Group(field);
Scene scene = new Scene(root);
fxPanel.setScene(scene);
}
}
To reproduce the issue:
Select Test Menu -> Test Item
Click the text field
Click outside the application window
Click the text field again
Start typing
Sometimes the text doubles (i.e. typing "test" results in "testtest") and eventually I start getting a number of exceptions. Example stack trace here.
Edit: I am running Windows 10.
This issue is not reproducible in JDK 8 early-access build (8u172) which is yet to be released. So please wait for same.
Also not reproducible in JDK 9 and JDK 10 early-access build.
This issue has already been reported and you can track here:
https://bugs.openjdk.java.net/browse/JDK-8195739
Thanks

Can't dispose of jframe window?

I'm trying to dispose of the difficulty window after any one of the difficulty button's are clicked but it won't happen. I've tried .dispose and frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); but i can't get it. Is it just placement or more?
import java.awt.FlowLayout;
import java.awt.event.*;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.GridLayout;
public class Game extends JFrame{
public static JFrame frame = new JFrame();
private JLabel lab;
public static void main(String[] args) {
Game difficulty = new Game();
difficulty.setSize(350,105);
difficulty.setTitle("Difficulty.");
difficulty.setVisible(true);
difficulty.setLocationRelativeTo(null);
/**Game sudoku = new Game();
sudoku.setSize(900, 900);
sudoku.setVisible(false);*/
}
public Game(){
setLayout(new FlowLayout());
lab = new JLabel("Please select your difficulty.");
add(lab);
JButton easy;
easy = new JButton("Easy");
add(easy);
easy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
System.out.println("You clicked the button");
JFrame.dispose();
}
});
JButton medium;
medium = new JButton("Medium");
add(medium);
JButton hard;
hard = new JButton("Hard");
add(hard);
JButton evil;
evil = new JButton("Evil!");
add(evil);
}
}
First of all you're extending JFrame and creating an object of JFrame, if I'm not wrong, this shouldn't be done.
public class Game extends JFrame{
public static JFrame frame = new JFrame();
And as #Salah said, JFrame is not static, so it should be:
public JFrame frame = new JFrame();
To solve your problem, you're disposing a new JFrame (yes, you have 3 JFrames in one class, instead of 1, which is what you want), with: JFrame.dispose(); if you already created an object or you're extending JFrame, you can:
this.dispose(); //For the extended JFrame
or
frame.dispose(); //For the object you created
dispose() method is not a static, so it'll not work by calling it directly from JFrame class
JFrame.dispose();
try to do :
dispose();
Or to dispose the frame object you have created
frame.dispose();
Read more about JFrame
I had the same problem:
this.dispose();
solved my problem.
Try setting the jFrame to invisible before disposing it:
public void disposeJFrame(JFrame frame){
frame.setVisible(false);
frame.dispose();
}
If you're wanting to close the whole program, you can use System.exit(0);
Instead JFrame.dispose();, use frame.dispose() or JFrame.this.dispose();

Why Mainframe JButton appear foreground of JInternalFrame?

I am creating a Swing Application. I have one main JFrame and a JDesktopPane. I added one button and one label on main frame. But if I open any JInternalFrame on Main Frame button and label covers the internal frame.
(JButtonand JLabel appear foreground of JInternalFrame). If I click internal frame button go to background.
Can you help to solve this?
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Rectangle;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
public class MainFrame {
JFrame frame1 ;
JDesktopPane desktop ;
public MainFrame () {
frame1 = new JFrame("EMPLOYEE LEAVE TRACKER");
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setExtendedState(Frame.MAXIMIZED_BOTH);
frame1.repaint();
desktop = new JDesktopPane(); //Creates a new JDesktopPane.
frame1.setContentPane(desktop);
frame1.setSize(900,700);
frame1.setVisible(true);
desktop.setBackground(Color.DARK_GRAY );
//Creates a JLabel on JDesktopPane.
JLabel label1 = new JLabel("EMPLOYEE LEAVE TRACKER", SwingConstants.CENTER);
label1.setFont(new Font("SansSerif",Font.ITALIC + Font.BOLD,54));
label1.setBounds(new Rectangle(new Point(275, 100),label1.getPreferredSize()));
//Creates a JButon on JDesktopPane.
JButton Leave = new JButton("Leave Management");
Leave.setHorizontalTextPosition(JButton.CENTER);
Leave.setBounds(new Rectangle(new Point(700,200),Leave.getPreferredSize()));
Leave.setSize(300, 300);
Leave.addActionListener(new java.awt.event.ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
frame1.add(LeaveManagment());
}
});
//Look and Feel
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch(Exception e) {
System.out.println("Error setting native LAF: " + e);
}
desktop.add(Leave);
desktop.add(label1);
}
//Creating JInternalFrame
public JInternalFrame LeaveManagment(){
final JInternalFrame employeeFrame = new JInternalFrame("LEAVE M" +
"ANAGEMNT", true, true, true, true);
employeeFrame.getContentPane().setBackground(Color.white);
employeeFrame.setSize(900,700);
employeeFrame.setVisible(true);
employeeFrame.setMaximizable(true);
employeeFrame.setResizable(true);
JComponent c = (JComponent)
employeeFrame.getContentPane();
c.setLayout(new FlowLayout());
return employeeFrame;
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MainFrame ();
}
});
}}
From the sound of it, you've added the buttons to the JDesktopPane.
The desktop pane is a type of JLayeredPane, this allows you to place components on, we'll, layers.
While this is a neat feature, IMHO, only JInternalFrames and the occasional special "window" should appear on the desktop (although the desktop manager does use a type of button to represent minimised windows)
Personally, I'd place the buttons also where, not on the desktop.
Take a look at How to Use Internal Frames and How to use Layered Pane
MadProgrammer has it right. One solution is to add your components to the appropriate JLayeredPane layer. i.e.,
// Note name change of JButton to adhere to Java naming standards:
desktop.add(leaveManagementBtn, JLayeredPane.DEFAULT_LAYER);
desktop.add(label1, JLayeredPane.DEFAULT_LAYER); // note layer component being added to
and,
public void actionPerformed(java.awt.event.ActionEvent e) {
// again method name capitalization changed to adhere to standard
// again, component added to appropriate layer
frame1.add(leaveManagment(), JLayeredPane.PALETTE_LAYER);
}
Again, MadProgrammer is right, the buttons should not be on the JDesktopFrame but on a button bar off of the desktop. I recommend that you accept his answer.

Java GTK+ native look and feel looks bad and bold

I'm facing a problem when applying a native Look & Feel to my JFrame, all text (except HTML formatted JLabel) have an ugly bold font.
The very simple UI in the following code sum up all the differences I can see with this L&F :
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
public class HelloWorld extends JFrame {
public HelloWorld() {
Box b = Box.createVerticalBox();
// Labels
JLabel bold = new JLabel("I'm bold !");
JLabel notBold = new JLabel("<html><em>I'm not bold !</em></html>");
b.add(bold);
b.add(notBold);
// Scrollbars example
JPanel scrollViewPort = new JPanel();
scrollViewPort.setPreferredSize(new Dimension(400, 400));
JScrollPane scroll = new JScrollPane(scrollViewPort);
b.add(scroll);
add(b, BorderLayout.CENTER);
// Bold menu
JMenuBar menubar = new JMenuBar();
JMenu menu = new JMenu("Menu");
JMenuItem item = new JMenuItem("Item");
menu.add(item);
menubar.add(menu);
setJMenuBar(menubar);
setPreferredSize(new Dimension(200, 200));
pack();
}
public static void setNativeLAF() {
// Native L&F
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
System.out.println("Unable to set native look and feel: " + e);
}
}
public static void main(String[] args) {
// setNativeLAF();
HelloWorld app = new HelloWorld();
app.setVisible(true);
}
}
See the difference :
with native L&F (GTK+)
with Metal L&F
by commenting out the setNativeLAF() call.
I'm applying the native look and feel right when my application starts, before any window appears. The UIManager gives me GTK+ (com.sun.java.swing.plaf.gtk.GTKLookAndFeel) as the native look and feel, which is ok since i'm using a Gnome 3 desktop.
I have three problems with this right now :
The GTK+ look and feel doesn't looks like the GTK theme (see the scrollbars)
The JMenuBar seems disabled (not the case with the Metal L&F)
The font is bold ! (same problem with Metal L&F, but fixable)
Any help or explanation about why the GTK+ L&F does that on my system whould be appreciated.
Edit: Here is what a "classic" application looks like on my system in eclipse.
I see several things worth mentioning:
You can apply a derived font to a label, as shown below.
The relevant specification for HTML in a component sys "EM basic emphasis typically rendered in an italic font".
See also Initial Threads.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class HelloWorld extends JFrame {
public HelloWorld() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
Box b = Box.createVerticalBox();
// Labels
JLabel bold = new JLabel("I'm bold !");
bold.setFont(bold.getFont().deriveFont(Font.BOLD));
JLabel notBold = new JLabel("<html><em>I'm not bold !</em></html>");
b.add(bold);
b.add(notBold);
// Scrollbars example
JPanel scrollViewPort = new JPanel();
scrollViewPort.setPreferredSize(new Dimension(400, 200));
JScrollPane scroll = new JScrollPane(scrollViewPort);
b.add(scroll);
add(b, BorderLayout.CENTER);
// Bold menu
JMenuBar menubar = new JMenuBar();
JMenu menu = new JMenu("Menu");
JMenuItem item = new JMenuItem("Item");
menu.add(item);
menubar.add(menu);
setJMenuBar(menubar);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
HelloWorld app = new HelloWorld();
app.setVisible(true);
}
});
}
}

Categories