How to mimic the behavior of JPopupMenu with JDialog? - java

I'm implementing an "in app" search engine with Swing and I want it to behave exactly like Windows 10's search box.
The search box should:
Open above and to the right of the search button, touching the button's edge.
Have the focus when open.
Close (if open) on a press of the search button.
Close (if open) when pressing with the mouse anywhere out of the search box.
It was perfect if JPopUpMenu could have JDialog as it's child but since it can't I need to implement the behaviors from scratch (or do I?).
This is my first time using Swing and I'm having difficulties implementing everything by myself.
I tried looking for examples online but I couldn't find much helpful information.
Is there a workaround to the fact that JPopUpMenu can't host JDialog?
Are there examples of implementing the behaviors I described?
Thanks
===============================Edit============================
Thanks for the comments so far. I've managed to get the behavior I wanted except one issue.
The following code creates a frame with a button:
public static void main(String[] args){
JFrame mainWindow = new JFrame();
mainWindow.setSize(420,420);
mainWindow.setVisible(true);
JFrame popUp = new JFrame();
popUp.setSize(210, 210);
JButton button = new JButton("button");
mainWindow.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!button.isSelected()){
button.setSelected(true);
popUp.setVisible(true);
}
else{
button.setSelected(false);
popUp.setVisible(false);
}
}
});
popUp.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
popUp.setVisible(false);
}
});
}
When I click the button, a pop-up window appears and if I click out of the main window the pop up disappear but then when I want to re-open the pop-up I need to press the button twice.
How can I get the button to operate correctly when the pop-up was closed due to lose of focus?

Your "solution" is very brittle. Try moving the main JFrame before left-clicking on the JButton.
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section. Study the rest of the tutorial.
I created a main JFrame that pops up a JDialog. I put the close JButton on the JDialog. The JDialog is modal, meaning you cannot access the main JFrame while the JDialog is visible.
You can place the JDialog anywhere you wish on the screen. Normally, you have a JDialog appear in the center of the parent JFrame. That's where users expect a dialog to appear. I placed the JDialog towards the upper left, just to show you how it's done.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PopupExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new PopupExample().createAndShowGUI());
}
private JFrame mainWindow;
public void createAndShowGUI() {
mainWindow = new JFrame("Main Window");
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.add(createMainPanel(), BorderLayout.CENTER);
mainWindow.pack();
mainWindow.setLocationByPlatform(true);
mainWindow.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(200, 200, 200, 200));
JButton button = new JButton("button");
panel.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
createAndShowDialog(mainWindow);
}
});
return panel;
}
private void createAndShowDialog(JFrame frame) {
JDialog dialog = new JDialog(frame, "Dialog", true);
dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
dialog.add(createDialogPanel(dialog), BorderLayout.CENTER);
dialog.pack();
// Here's where you set the location of the JDialog relative
// to the main JFrame
Point origin = frame.getLocation();
dialog.setLocation(new Point(origin.x + 30, origin.y + 30));
dialog.setVisible(true);
}
private JPanel createDialogPanel(JDialog dialog) {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));
JButton button = new JButton("Close");
panel.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dialog.dispose();
}
});
return panel;
}
}

Related

Weird Issue with JComponent Visibility

I am working on a GUI project with Swing in Java and the program is generally working fine. However, under each screen, I have a back button that calls the method of the screen before it and goes through the ArrayList containing all of the elements on the current screen and calls setVisible(false) on them. Upon running the program, the back button works correctly if you click it once but if you go back on the screen, and click it again, it takes two clicks for it to correctly work and then four clicks and then eight clicks and so on. I have no idea what is going on or why it is behaving this way as nothing in my code seems to do it. Also, sometimes, the button correctly returns to the previous screen but then keeps the components on the current screen active as if setVisible(false) was never called. The following code represents the general structure of my project. Is there anything that it is doing that is generating this problem?
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class MAIN {
static JFrame frame;
static JPanel panel;
public static void main(String [] args) {
mainScreen();
}
public static void mainScreen() {
JButton newScreen = new JButton("Next Screen");
frame = new JFrame();
panel = new JPanel();
panel.setBounds(0,0,1920,1080);
panel.setBackground(Color.cyan);
newScreen.setBounds(50, 500, 100, 500);
newScreen.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
newScreen.setVisible(false);
JButton returnButton = new JButton("return");
returnButton.setBounds(50, 50, 100, 100);
panel.add(returnButton);
returnButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
returnButton.setVisible(false);
mainScreen();
}
});
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.add(newScreen);
frame.add(panel);
frame.setSize(1920,1080);
frame.setLayout(null);
frame.setVisible(true);
}
}

How to hide a JPopupMenu by pressing a button?

I'm making a program that has a popup menu with two buttons, one of which should close the popup menu, but I have no idea how to do that and googling hasn't gone too well.
I've tried using popup.hide() but then the menu wouldn't come back, despite doing so when I tried just moving the popup. It also required me to put a SuppressWarning in that case and it took a few seconds for it to close at all. Is there any better way of doing it?
I'm not sure what kind of code is relevant, but here's the relevant buttons and their roles in this(I skipped all the creating the GUI parts that didn't seem relevant, everything looks good and I know that the buttons are working):
package test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
interface CustomButton {
JButton create();
void react(JPopupMenu popup, JFrame frame);
}
class ErrandsButton implements CustomButton {
private JButton errands = new JButton("Errands");
public JButton create() {
return errands;
}
public void react(JPopupMenu popup, JFrame frame) {
errands.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
popup.show(frame, 120, 65);
}
});
}
}
class Test {
static JFrame frame = new JFrame("List");
static CustomButton errands = new ErrandsButton();
static JButton cancelTask = new JButton("Cancel");
static JPopupMenu popup = new JPopupMenu();
static void cancelTask() {
cancelTask.addActionListener(new ActionListener() {
#SuppressWarnings("deprecation")
public void actionPerformed(ActionEvent e) {
popup.hide();
}
});
}
public static void main(String args[]) {
createInterface();
cancelTask();
errands.react(popup, frame);
}
static void createInterface() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JPanel popup1 = new JPanel();
JPanel button = new JPanel();
popup1.add(cancelTask);
popup.add(popup1);
frame.add(popup);
button.add(errands.create());
frame.getContentPane().add(BorderLayout.CENTER, button);
frame.setVisible(true);
}
}
Use popup.setVisible(true) and popup.setVisible(false).
frame.add(popup); is the problem. Do not add a JPopupMenu to a Container. Instead, use setComponentPopupMenu.
Alternatively, you could do the work yourself by adding a MouseListener whose mousePressed, mouseReleased and mouseClicked methods call isPopupTrigger and show. (It is vital that you do this in all three of those methods—different platforms have different conditions for showing popup menus.)
But really, using setComponentPopupMenu is easier.

How to make one class react to a button pressed in another?

Hi I am new to programming and trying to figure things out as I go. Thanks in advance for the help.
I am trying to make a button in one class that when pressed, the other class knows.
Here is the first class that contains the testWindow method that I want to call in my other class.
import javax.swing.*;
import java.awt.event.*;
public class TestWindow {
public static void testWindow() {
JFrame frame = new JFrame("test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel text = new JLabel("this is a test!",SwingConstants.CENTER);
text.setBounds(0,30,300,50);
JButton button = new JButton("Start");
button.setBounds(100,100,100,40);
frame.add(text);
frame.add(button);
frame.setSize(300,200);
frame.setLayout(null);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//I don't know what to put here
}
});
}
}
And here is the second class where I want to use my testWindow method.
public class MainTest extends TestWindow {
public static void main(String[] arg){
testWindow();
//other stuff that happens when "start" is pressed
}
}
When I run the MainTest class, the testWindow appears as it should. But when the "start" button is pressed, I want to close that frame then do other actions in the main method. How would I go about that?
When I run the MainTest class, the testWindow appears as it should. But when the "start" button is pressed, I want to close that frame then do other actions in the main method. How would I go about that?
You're desiring the functionality of a modal dialog, a window that halts program flow until it has been dealt with. And in this situation you shouldn't be using a JFrame which does not allow for this type of modality, but rather a Swing modal dialog such as a JOptionPane or a JDialog that you create, make modal, and display. Then the GUI program flow halt until the dialog window is no longer visible.
If you do this, all the button's action listener has to do is to close the dialog window that holds it, that's it.
Side note: You're misusing inheritance here, as your MainTest class should most definitely not extend from the TestWindow class. While it may not matter in this simple code, it can and will cause problems in future code.
e.g.,
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.event.*;
public class TestWindow {
public static void testWindow() {
// JFrame frame = new JFrame("test");
final JDialog frame = new JDialog((JFrame) null, "Test", ModalityType.APPLICATION_MODAL);
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel text = new JLabel("this is a test!", SwingConstants.CENTER);
// text.setBounds(0, 30, 300, 50);
JButton button = new JButton("Start");
// button.setBounds(100, 100, 100, 40);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.dispose();
}
});
int eb = 15;
JPanel panel = new JPanel(new BorderLayout(eb, eb));
panel.setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
panel.add(text, BorderLayout.PAGE_START);
panel.add(button, BorderLayout.CENTER);
frame.add(panel);
frame.pack();
// frame.setSize(300, 200);
// frame.setLayout(null);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
and
import javax.swing.SwingUtilities;
public class TestTestWindow {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TestWindow.testWindow();
System.out.println("Called after test window no longer visible");
});
}
}

How I enable parent JFrame after close child?

I have a frame, on this frame I have a Menu with About MenuItem. When we select it the program opens a new JPanel with texts and with OK button and the enabled status of parent panel is set to false.
And now comes a problem. When we click on OK, then I want to close this About panel, and I want to turn to parent panel, and I want to enable it!
Please tell me, how?
Consider using a WindowListener that reacts to the closing event of the about-dialog. You can add this in your frame or in the constructor of your dialog, just set the variables accordingly.
myDialog.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
parentFrame.setEnabled(true);
}
});
If you really only have a switching JPanel, use a ButtonListener.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
frame.setEnabled(true);
}
});
As mentioned in the comments, using a modal JDialog would be a more elegant way of solving the problem of disabling a parent frame while a dialog is active. Here is a tutorial.
Why don't you use simply a JOptionPane (particularly the showMessageDialog method)? You can specify there an Object (for example a JPanel) which will be presented in a modal dialog. Take a look at this sample code I've written for you (I've used a JButton, but it will be the same for JMenuItem):
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class AboutDialogDemo extends JFrame {
private final JButton btnAbout = new JButton("About...");
public AboutDialogDemo() {
final JFrame thisFrame = this;
btnAbout.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(thisFrame, new AboutPanel());
}
});
getContentPane().setLayout(new BorderLayout());
getContentPane().add(btnAbout, BorderLayout.PAGE_END);
pack();
}
public static void main(String[] args) {
AboutDialogDemo frame = new AboutDialogDemo();
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class AboutPanel extends JPanel {
private final JLabel lblAbout = new JLabel("Sample about text");
public AboutPanel() {
setLayout(new BorderLayout());
add(lblAbout, BorderLayout.PAGE_START);
}
}
I hope you'll find it useful

Java Swing. Opening a new JPanel from a JButton and making the buttons pretty

I am trying to build a little program that has a main GUI with 2 buttons. One button closes the program, the other I want to open a new JPanel that will have text fields etc.
I would like to be able to make the buttons so they look like normal application buttons I guess, nice and square, equal size etc. etc., I am not sure how to do this though.
Also, I am unsure how to open a new JFrame from a button click.
GUI Code:
package practice;
public class UserInterface extends JFrame {
private JButton openReportSelection = new JButton("Open new Window");
private JButton closeButton = new JButton("Close Program");
private JButton getCloseButton() {
return closeButton;
}
private JButton getOpenReportSelection() {
return openReportSelection;
}
public UserInterface() {
mainInterface();
}
private void mainInterface() {
setTitle("Program Information Application");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel centerPanel = new JPanel(new GridLayout(0, 3));
centerPanel.add(openReportSelection);
centerPanel.add(closeButton);
getCloseButton().addActionListener(new Listener());
add(centerPanel, BorderLayout.CENTER);
setSize(1000, 200);
setVisible(true);
}
private void addReportPanel() {
JPanel reportPanel = createNewPanel();
getContentPane().add(reportPanel, BorderLayout.CENTER);
}
private JPanel createNewPanel() {
JPanel localJPanel = new JPanel();
localJPanel.setLayout(new FlowLayout());
return localJPanel;
}
}
ActionListener Class code:
package practice;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Listener implements ActionListener {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
}
EDIT: I think opening a new JPanel would be the way to go rather than a JFrame. What would be the best way to do this from a Jbutton click?
Start by using a different layout manager, FlowLayout or GridBagLayout might work better
JPanel centerPanel = new JPanel(new FlowLayout());
centerPanel.add(openReportSelection);
centerPanel.add(closeButton);
These layouts will honour the preferred sizes of your buttons
As for opening another window, well, you've already create one, so the process is pretty much the same. Having said that, you might consider having a look at The Use of Multiple JFrames: Good or Bad Practice? before you commit yourself to far.
A better approach might be to use a JMenuBar and JMenuItems to act as the "open" and "exit" actions. Take a look at How to Use Menus then you could use a CardLayout to switch between views instead, for example
From a pure design perspective (I know it's only practice, but perfect practice makes perfect), I wouldn't extend anything from JFrame and instead would rely on building your main GUIs around something like JPanel instead.
This affords you the flexibility to decide how to use these components, as you could add them to frames, applets or other components...
If you want your buttons to have the native Look and Feel (L&F), add the following to your program:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
Instead of opening another JFrame, you'll want to instead use a JDialog, typically with modality set.
In Java, you can only extend one class and therefore you should consider carefully whether it is appropriate or not to extend another class. You could ask yourself, "Am I actually extending the functionality of JFrame?" If the answer is no, then you actually want to use an instance variable.
Below is an example program from the above recommendations:
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
public class MyApplication {
private JFrame myframe; // instance variable of a JFrame
private JDialog mydialog;
public MyApplication() {
super();
myframe = new JFrame(); // instantiation
myframe.setSize(new Dimension(400, 75));
myframe.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
JButton btnNewWindow = new JButton("Open New Window");
btnNewWindow.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
mydialog = new JDialog();
mydialog.setSize(new Dimension(400,100));
mydialog.setTitle("I got you! You can't click on your JFrame now!");
mydialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); // prevent user from doing something else
mydialog.setVisible(true);
}
});
myframe.getContentPane().add(btnNewWindow);
JButton btnCloseProgram = new JButton("Close Program :(");
btnCloseProgram.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
myframe.dispose();
}
});
myframe.getContentPane().add(btnCloseProgram);
myframe.setVisible(true);
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e1) {
e1.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
new MyApplication();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
I am not sure from your question what do wou want to do. Do you want to open a new JFrame or do you want to add a JPanel to the existing frame.
To open a new JFrame using a button, create an instance of the JFrame in the actionPerformed method of the button. In your case it would look similar to this:
openReportSelection.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
JFrame frame = new JFrame();
// Do something with the frame
}
}
});
You're probably looking up to create and open a new JFrame. For this purpose, first you need to instantiate an object from JFrame Class. As an example, Let's instantiate a new JFrame with specific boundries.
JFrame testFrame = new testFrame();
verificationFrame.setBounds(400, 100, 250, 250);
Then you need to create your components like JButtons, Jlabels and so on, and next you should add them to your new testFrame object.
for example, let's create a Jlabel and add it testFrame:
JLabel testLbl = new JLabel("Ok");
testLbl.setBounds(319, 49, 200, 30);
testFrame.getContentPane().add(testLbl);
Now let's suppose you have a Jbutton which is named "jbutton" and by clicking it, a new JFrame object will be created and the Jlabel component will be added to it:
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
JFrame testFrame = new testFrame();
verificationFrame.setBounds(400, 100, 250, 250);
Label testLbl = new JLabel("Ok");
testLbl.setBounds(319, 49, 200, 30);
testFrame.getContentPane().add(testLbl);
}}});

Categories

Resources