JComboBox setPopupVisible not called on clicking arrow - java

When clicking the arrow to open the popup on a JComboBox the overridden setPopupVisible is not called, see minimal example below. Am i just missing something here or doing something wrong? Any hints appreciated :)
The goal here is that i want to control its visibility behaviour, especially only hiding it under certain conditions, for example input checking (mind that the combobox is editable).
Using Java 8.
Building a Frame with a custom JComboBox:
import java.awt.Frame;
import javax.swing.BoxLayout;
public class Test {
public static void main(String[] args) {
Frame frame = new Frame();
frame.setLayout(new BoxLayout(frame, BoxLayout.Y_AXIS));
MyComboBox combo = new MyComboBox();
combo.setEditable(true);
combo.addItem("bli");
combo.addItem("bla");
combo.addItem("blu");
combo.addItem("ble");
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
}
The custom JComboBox:
import javax.swing.JComboBox;
public class MyComboBox extends JComboBox {
#Override
public void setPopupVisible(boolean v) {
if(!v) {
System.out.println("HIDING COMBOBOX");
super.setPopupVisible(v);
} else {
System.out.println("SHOWING COMBOBOX");
super.setPopupVisible(v);
}
}
}

In JComboBox setPopupVisible(boolean) API is NOT there to notify when the popup is opened/closed. It is there to programmatically show the popup or hide the popup.
If you want to be notified when the popup is opened/closed, you can use addPopupMenuListener() like in below code:
import javax.swing.BoxLayout;
import javax.swing.JComboBox;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.Frame;
public class Test {
public static void main(String[] args) {
Frame frame = new Frame();
frame.setLayout(new BoxLayout(frame, BoxLayout.Y_AXIS));
MyComboBox combo = new MyComboBox();
combo.setEditable(true);
combo.addItem("bli");
combo.addItem("bla");
combo.addItem("blu");
combo.addItem("ble");
combo.addPopupMenuListener(new PopupMenuListener()
{
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e)
{
System.out.println("Popup Menu Will Become Visible");
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
{
System.out.println("Popup Menu Will Become Invisible");
}
#Override
public void popupMenuCanceled(PopupMenuEvent e)
{
System.out.println("Popup Menu Canceled");
}
});
frame.add(combo);
frame.pack();
frame.setVisible(true);
}
}
class MyComboBox extends JComboBox
{
#Override
public void setPopupVisible(boolean v) {
if(!v) {
System.out.println("HIDING COMBOBOX");
super.setPopupVisible(v);
} else {
System.out.println("SHOWING COMBOBOX");
super.setPopupVisible(v);
}
}
}

Related

Java Swing Main Window comes to the front automatically when opening a dialog

I'm trying to make a Java Swing Application where a JDialog is opened upon clicking on a JMenu in the Menubar, but for some reason, the main JFrame automatically comes to the front as soon as the JDialog opens.
I simply open the Dialog in the MenuListener of the JMenu.
#Override
public void menuSelected(MenuEvent e) {
Dialog dialog = new Dialog();
dialog.setVisible(true);
}
The main JFrame is started here:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Fenster hauptFenster = new Fenster();
hauptFenster.setVisible(true);
}
});
}
I already tried calling toFront() on the JDialog upon creation, but that doesn't help it.
Here's a minimal reproducible example where the problem occurs:
import java.awt.EventQueue;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
public class Test extends JFrame {
JFrame thisFrame;
public Test() {
//GUI
setTitle("Behaviour Test");
setSize(350,200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
thisFrame = this;
//Menübar
JMenuBar menu = new JMenuBar();
JMenu widerstand = new JMenu("Start Dialog");
widerstand.addMenuListener(new MenuListener() {
#Override
public void menuSelected(MenuEvent e) {
TestDialog dialog = new TestDialog(thisFrame);
dialog.setVisible(true);
}
#Override
public void menuDeselected(MenuEvent e) {
}
#Override
public void menuCanceled(MenuEvent e) {
}
});
menu.add(widerstand);
setJMenuBar(menu);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Test testFrame = new Test();
testFrame.setVisible(true);
}
});
}
}
class TestDialog extends JDialog {
public TestDialog(JFrame parent) {
setTitle("Test Dialog");
setSize(450,300);
super.setLocationRelativeTo(parent);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setResizable(false);
}
}
The easiest solution is to set the parent of the dialog as I originally suggested in my comment (since this is the normal way to keep the parent/child relationship between the frame/dialog).
However, even if you don't set the parent, I have not seen this behaviour. Maybe that is because I have always displayed a child dialog from within an ActionListener.
So, it is interesting that the behaviour is different between the MenuListener and an ActionListener if you don't set the parent.
When using the MenuListener you need to wrap the code in an invokeLater():
import java.awt.*;
import java.awt.EventQueue;
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
public class Test extends JFrame {
JFrame thisFrame;
public Test() {
//GUI
setTitle("Behaviour Test");
setSize(350,200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setResizable(false);
thisFrame = this;
JMenuBar menu = new JMenuBar();
JMenu widerstand = new JMenu("Start Dialog");
widerstand.addMenuListener(new MenuListener() {
#Override
public void menuSelected(MenuEvent e) {
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
TestDialog dialog2 = new TestDialog(thisFrame);
dialog2.setVisible(true);
}
});
}
#Override
public void menuDeselected(MenuEvent e) {
}
#Override
public void menuCanceled(MenuEvent e) {
}
});
menu.add(widerstand);
setJMenuBar(menu);
JButton button = new JButton("Display Dialog");
add(button);
button.addActionListener((e) ->
{
TestDialog dialog2 = new TestDialog(thisFrame);
dialog2.setVisible(true);
});
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Test testFrame = new Test();
testFrame.setVisible(true);
}
});
}
}
class TestDialog extends JDialog {
public TestDialog(JFrame parent) {
setTitle("Test Dialog");
setSize(450,300);
setLocationRelativeTo(parent);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setResizable(false);
}
}
Works as expected with the ActionListener.

Get the in-focus tab after closing a tab

In a JTabbedPane, I associated a custom-made Data object to each added tab. I also have a corresponding Metadata object that shows up in another panel when the tab is selected. The problem I have now is when a tab is closed, the metadata panel shows the metadata of the Data object in the tab that just gets closed. Ideally, I want the panel to show the metadata for the in-focus tab that the user sees. However, the act of closing a tab means the “selected tab” is the tab being closed, so tabpane.getSelectedIndex() would not work. How can I get the tab that is in focus after closing a tab? Thank you in advance!
Devil is in the detail, which you provided none.
I did a quick test and discovered that, ChangeListener is called before ContainerListener, which is a real pain, but, it was always reporting the correct index.
So, what you need to do is marry the two together, so that, both will update the meta data pane when they are called.
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane(tabbedPane));
tabbedPane.addTab("Two", new TabPane(tabbedPane));
tabbedPane.addTab("Three", new TabPane(tabbedPane));
tabbedPane.addTab("Four", new TabPane(tabbedPane));
tabbedPane.addContainerListener(new ContainerListener() {
#Override
public void componentAdded(ContainerEvent e) {
}
#Override
public void componentRemoved(ContainerEvent e) {
System.out.println("Removed " + e.getChild());
}
});
tabbedPane.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println(tabbedPane.getSelectedIndex());
}
});
JFrame frame = new JFrame();
frame.add(tabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TabPane extends JPanel {
private JTabbedPane parent;
public TabPane(JTabbedPane parent) {
this.parent = parent;
setLayout(new GridBagLayout());
JButton btn = new JButton("Close");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
parent.remove(TabPane.this);
}
});
}
}
}

Update a JPopupMenu menu items while it is open

In my UI i have a JPopMenu with values as ,
for e.g A,B,C
The scenario is,
I opened the JPopupMenu and kept it open.
At back end with a timer running , it updates the content B to some other alphabet at frequent interval.
3.I want the JPopupMenu to get updated while it is kept open.
In current behavior if i close and open JPopupMenu the updated value shows up.
I tried repaint()but it doesn't do anything.
What is the best way to do this?? Am new to swings please help.
Menu items can change their content at run time just fine. Without seeing your code it's hard to tell what you're doing wrong, but here's a working example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupTest {
private static final String[] messages = {
"You are today's 1000th user!",
"You have won an internet!",
"Claim your prize!"
};
private PopupTest() {
JFrame frame = new JFrame("You have won");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel lbl = new JLabel("Check your prize!");
frame.setLocationByPlatform(true);
frame.add(lbl);
frame.pack();
final JPopupMenu menu = new JPopupMenu();
final JMenuItem item = new JMenuItem(messages[0]);
menu.add(item);
menu.add(new JMenuItem("Another item that does not work"));
final Timer timer = new Timer(1000, new ActionListener() {
int count;
#Override
public void actionPerformed(ActionEvent e) {
count++;
count %= messages.length;
item.setText(messages[count]);
}
});
menu.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
timer.stop();
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
timer.stop();
}
});
lbl.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
menu.show(e.getComponent(), e.getX(), e.getY());
timer.start();
}
}
});
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PopupTest();
}
});
}
}
Try to use .revalidate() with .repaint() it might help.
The docs suggest that the revalidate method is called every time something like size changes and manually calling it with repaint seems to solve problems like these.

Click j/ToggleButton then set Icon/Image

I am making a 4x4 board kinda like minesweeper. Each button has a bomb or another image.
Here's my code:
private void jButton1MouseClicked(java.awt.event.MouseEvent evt) {
this.jToggleButton1.setIcon(new javax.swing.ImageIcon("bombaa.png"));
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
this.jToggleButton1.setIcon(new javax.swing.ImageIcon("bombaa.png"));
}
also tried this way...
private void setIcon1(){
setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("bombaa.png")));
}
and call setIcon() in the jButton1ActionPerformed and jButton1MouseClicked BUT this sets my image as the main Icon for the program.
Basically what I need is: Click a button and set the image/icon one time only.
Start by creating your own button, one which you can control the selected state...
public class StickyModel extends JToggleButton.ToggleButtonModel {
public void reset() {
super.setSelected(false);
}
#Override
public void setSelected(boolean b) {
if (!isSelected()) {
super.setSelected(b);
}
}
}
This will prevent the button from becoming "unselected" once it has been set selected (it also includes a reset method which will make it "unselected" for you)
Create your buttons with a "blank" or empty "default" icon and a set the selectedIcon property to what you want shown when the button is selected...
JToggleButton btn = new JToggleButton();
btn.setModel(new StickyModel());
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Blank.png"))));
btn.setSelectedIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Bomb.png"))));
So, when the button is clicked, it will use the selectedIcon
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
try {
add(createButton());
add(createButton());
add(createButton());
} catch (IOException exp) {
exp.printStackTrace();
}
}
protected JToggleButton createButton() throws IOException {
JToggleButton btn = new JToggleButton();
btn.setModel(new StickyModel());
btn.setIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Blank.png"))));
btn.setSelectedIcon(new ImageIcon(ImageIO.read(getClass().getResource("/Bomb.png"))));
return btn;
}
}
public class StickyModel extends JToggleButton.ToggleButtonModel {
public void reset() {
super.setSelected(false);
}
#Override
public void setSelected(boolean b) {
if (!isSelected()) {
super.setSelected(b);
}
}
}
}

Clear JTextField Contents on Click

So I've built a very basic Web browser - I'm trying desperately to remove the contents of the address bar when a user clicks on it (JTextField) this appears with some text in as default. Any advice is appreciated.
Have a great day!
MY CODE
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class Web_Browser extends JFrame {
private final JTextField addressBar;
private final JEditorPane display;
// Constructor
public Web_Browser() {
super("Web Browser");
addressBar = new JTextField("Click & Type Web Address e.g. http://www.google.com");
addressBar.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
loadGo(event.getActionCommand());
}
}
);
add(addressBar, BorderLayout.NORTH);
display = new JEditorPane();
display.setEditable(false);
display.addHyperlinkListener(
new HyperlinkListener(){
#Override
public void hyperlinkUpdate(HyperlinkEvent event){
if(event.getEventType()==HyperlinkEvent.EventType.ACTIVATED){
loadGo(event.getURL().toString());
}
}
}
);
add(new JScrollPane(display), BorderLayout.CENTER);
setSize(500,300);
setVisible(true);
}
// loadGo to sisplay on the screen
private void loadGo(String userText) {
try{
display.setPage(userText);
addressBar.setText(userText);
}catch(IOException e){
System.out.println("Invalid URL, try again");
}
}
}
Use a FocusListener. On focusGained, select all.
addressBar.addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
JTextComponent textComponent = (JTextComponent) e.getSource();
textComponent.selectAll();
}
});
For example:
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.*;
import javax.swing.text.JTextComponent;
#SuppressWarnings("serial")
public class FocusExample extends JPanel {
private static final int TF_COUNT = 5;
private JTextField[] textFields = new JTextField[TF_COUNT];
public FocusExample() {
for (int i = 0; i < textFields.length; i++) {
textFields[i] = new JTextField("Foo " + (i + 1), 10);
textFields[i].addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent e) {
JTextComponent textComponent = (JTextComponent) e.getSource();
textComponent.selectAll();
}
});
add(textFields[i]);
}
}
private static void createAndShowGui() {
FocusExample mainPanel = new FocusExample();
JFrame frame = new JFrame("FocusExample");
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();
}
});
}
}
This gives the user the option of leaving the previous text in place, of adding to the previous text, or of simply over-writing it by typing.
new JTextField("Click & Type Web Address e.g. http://www.google.com");
Maybe you want the Text Prompt, which doesn't actually store any text in the text field. It just gives the user a hint what the text field is for.
This is beneficial so that you don't generate DocumentEvents etc., since you are not actually changing the Document.
Add a mouseListener instead of your actionListener method.
addressBar.addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent e){
addressBar.setText("");
}

Categories

Resources