Clicking JPanel with inner components - java

I have a main JPanel class (not exact code):
class Panel extends JPanel {
public void initGUI() {
setLayout(...);
JTabbedPane tabbedPane = new JTabbedPane();
JPanel boxPanel = new JPanel(...);
tabbedPane.addTab("test", boxPanel);
JLabel label = new JLabel("Label")
boxPanel.add(label);
add(tabbedPane);
}
}
I want to be able to click anywhere on the Panel or its inner components and return the Panel.
public class PanelMouseAdapter extends MouseAdapter {
public void mouseReleased(MouseEvent e) {
Panel panel = (Panel)e.getSource();
//do other stuff
}
}
And for each Panel I'm adding this mouse listener.
But it only works around the edges of the Panel, any inner components are ignored. I need it to be able to click anywhere in that Panel.
I need to maintain that anywhere I click it will return the Panel object (as in the mouse listener).
Thanks for any feedback.

Not sure I understand the question. Your demo code only shows a single panel, so why do you care what parent panel is clicked on? The better your explanation of the requirement the better the solution we can provide.
Anyway, check out Global Event Listeners. This will allow you to listen for a mousePressed event (which is a better then listening for a mouseClicked).
Next you need to create a custom panel (MyCustomPanel) that you use for the top level panel.
Now, whenever a mousePressed is generated you can get the source of the event and then use:
MyCustomPanel panel = SwingUtilties.getAncestorOfClass(MyCustomPanel.class, (Component)event.getSource());

You can use Container#getComponents() for this case. For example consider the code snippet given below (look for addListeners(Container) method):
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
class SPanel extends JPanel
{
public void init()
{
this.add(new JButton("Button"));
this.add(new JLabel("Click"));
JPanel pan = new JPanel();
pan.add(new JButton("PanButton"));
pan.add(new JTextField(29));
add(pan);
addListeners(this);
}
public void addListeners(Container comp)
{
Component[] components = comp.getComponents();
for (Component component : components)
{
if (component instanceof Container)
{
Component[] child = ((Container)component).getComponents();
if (child != null && child.length > 0)
{
addListeners((Container)component);
}
}
component.addMouseListener(new MouseAdapter()
{
public void mousePressed(MouseEvent evt)
{
System.out.println(evt.getSource().getClass());
}
});
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater( new Runnable()
{
#Override
public void run()
{
SPanel sp = new SPanel();
sp.init();
JFrame frame = new JFrame("Frame");
frame.getContentPane().add(sp);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

Solved issue.
Initially I was going to open a popup menu once I had the mouse listener in place.
But now I added JComponent.setComponentPopupMenu as Polum suggested, not to the Panel but to the tabbedPane.
Then I added a listener to the popup menu, got the source object via event in popupMenuWillBecomeVisible method, then the component via source.getInvoker(), then get the parent of the invoker component and check if its an instance of PairBox.

Related

OK button and String not showing up on JPanel

I am trying to make a panel showing a printed statement "Hello World!" and an OK button. Neither will show up on the panel and I have no idea why. I started with a block of code that was supposed to create just a blank popup. The blank popup worked great. I can't add the string or button and see them. I have tried calling paintComponent. I have tried adding the content to the panel. Does anyone know what I am missing?
Here is my code
package painting;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SwingPaintDemo1 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static class SwingPaintDemo extends JPanel{
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("Hello World!", 20,30);
}
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(250,250);
f.setVisible(true);
JButton okbutton = new JButton("OK");
ButtonHandler listener = new ButtonHandler();
okbutton.addActionListener(listener);
SwingPaintDemo displayPanel = new SwingPaintDemo();
JPanel content = new JPanel();
content.setLayout(new BorderLayout());
content.add(displayPanel, BorderLayout.CENTER);
content.add(okbutton, BorderLayout.SOUTH);
}
private static class ButtonHandler implements ActionListener{
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
}
You forgot to add the JPanel to the JFrame. Just add the following line at the bottom of your createAndShowGUI() method:
f.add(content);
I would also recommend moving your f.setVisible(true); line to the bottom of the method just to be safe. When you make the frame visible, the component tree is set up to take into account all the components added to the JFrame. If you add more components after that, you will need to do either manually revalidate the tree or do something that triggers an automatic revalidation. I'm assuming you're not revalidating your tree anywhere, so you should move f.setVisible(true); to after all the components are added.

How to cancel a mouseclicker event in Java

I have a mouseclicker event added to some JLabels and, after one of them will be clicked, I want to remove the link between that JLabel and the mouseclicker event.
To add the mouseclicker event to JLabel I use this code:
JLabel.addMouseListener(this);
There is a way to remove the JLabel from being clicked after the effect is solved? How can I do this?
I searched something but I'm not sure to how I can describe the problem and search about it, so i didn't found results.
This may seem trivial, but you could simply do:
myLabel.removeMouseListener(this);
Option two is to leave the MouseListener in place, but make it smarter -- i.e., give it logic that allows it to ignore input if need be. This could be a simple if block such as
#Override
public void mousePressed(MouseEvent me) {
if (someBoolean) {
return;
}
// here have your usual code
}
and then in your code, when you want to de-activate the MouseListener, simply change the listener's someBoolean field to false. This use of a boolean switch or flag is useful for when you need to turn the listener on and off repeatedly.
As a side note, you're usually better off not using this for your listeners as that is giving the main GUI class a bit too much responsibility. Instead use anonymous inner classes for simple few line code or named class for more involved listener code.
For example:
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
import javax.swing.border.Border;
public class TurnListenerOnAndOff extends JPanel {
private JLabel myLabel = new JLabel("My Label");
private JCheckBox listenerEnabledCheckBox = new JCheckBox("Listener Enabled", true);
public TurnListenerOnAndOff() {
// make label bigger with a border
Border outsideBorder = BorderFactory.createLineBorder(Color.black);
Border insideBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5);
myLabel.setBorder(BorderFactory.createCompoundBorder(outsideBorder, insideBorder));
// create and add MyMouseListener to my label
myLabel.addMouseListener(new MyMouseListener());
// add components to the GUI's main JPanel
add(myLabel);
add(listenerEnabledCheckBox);
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
// if the JCheckBox isn't checked...
if (!listenerEnabledCheckBox.isSelected()) {
return; // let's get out of here
}
// otherwise if the check box is checked, do following code
System.out.println("myLabel pressed!");
}
}
private static void createAndShowGui() {
TurnListenerOnAndOff mainPanel = new TurnListenerOnAndOff();
JFrame frame = new JFrame("On and Off");
frame.setDefaultCloseOperation(JFrame.DISPOSE_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();
}
});
}
}

JPanel repaint doesn't work

I have a simple task.
There is a frame. There are two panel in that frame. In second panel there is a button. When user click that button first panel must change its content.
Here is a code:
package test;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
class MyJPanel1 extends JPanel {
MyJPanel1() {
this.add(new JButton("MyJPanel1"));
}
}
class MyJPanel2 extends JPanel {
MyJPanel2() {
this.add(new JButton("MyJPanel2"));
}
}
class MyFrame extends JFrame {
JPanel topPanel = null;
MyFrame() {
super("Test");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new GridLayout(0, 1, 20, 20));
topPanel = new MyJPanel1();
this.add(topPanel);
JPanel bottomPanel = new JPanel();
this.add(bottomPanel);
JButton button = new JButton("switch");
button.addMouseListener(new MouseClickListener());
bottomPanel.add(button);
this.pack();
this.setVisible(true);
}
class MouseClickListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
topPanel = new MyJPanel2();
System.out.println("switch");
topPanel.invalidate();
topPanel.validate();
topPanel.repaint();
MyFrame.this.invalidate();
MyFrame.this.validate();
MyFrame.this.repaint();
}
}
}
public class Test {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyFrame();
}
});
}
}
But that don't work. After I click on button I see text in console, but first panel remain the same. I read that I must use invalidate() validate() and repaint() methods and I did, but it isn't help.
Any help would be appreciated.
If you want to "switch" panels then you should be using a CardLayout. The CardLayout allows 2 (or more) components to share the same space in a container but only one is ever visible at a time.
Read the section from the Swing tutorial on How to Use CardLayout for more information and working examples.
In your mouseClicked() method you create a new topPanel, but you don't do anything with it. Perhaps you meant to remove the original topPanel from myFrame, create a new topPanel, and then add the new toipPanel to myFrame.
Note that this may not be the best strategy (creating a new topPanel).

MouseEvent lost in JScrollPane

This is the code I am using to show the issue which I am facing in another project.
I am not getting any line like this if I use JScrollPane as a wrapper for panel2. Why?
I want to click on JscrollPane and got event printed as following.
java.awt.event.MouseEvent[MOUSE_CLICKED,(800,469),absolute(808,499),button=1,modifiers=Button1,clickCount=1] on javax.swing.JPanel[,0,0,934x612,layout=java.awt.FlowLayout,alignmentX=0.0,alignmentY=0.0,border=javax.swing.border.LineBorder#cc0e01,flags=9,maximumSize=,minimumSize=,preferredSize=java.awt.Dimension[width=880,height=630]]
If now I change
panel1.add(pane);
to
panel1.add(panel2);
Then the message above got printed.
public class LostMouseEvent {
public static void main(String[] args) {
new LostMouseEvent();
}
public LostMouseEvent() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel panel1 = new JPanel();
JPanel panel2 = new JPanel();
JScrollPane pane = new JScrollPane(panel2);
panel1.setPreferredSize(new Dimension(880, 630));
panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
panel2.setPreferredSize(new Dimension(840, 610));
panel2.setBorder(BorderFactory.createLineBorder(Color.green));
panel1.add(pane);
frame.add(panel1);
frame.pack();
frame.setVisible(true);
frame.setSize(950, 650);
panel1.addMouseListener(new MyMouseListener());
}
});
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mouseClicked (MouseEvent me) {
System.out.println(me);
}
}
}
UPD: In fact in my project there is more than just one panel2. Originally, I had panel1 and many panel2 inside. Then I wanted to wrap each panel2 with JScrollPane and started to face this problem.
I need to have only one MouseListener to minimize changes to the code.
Use EDT for creation and manipulation of Swing components
Dont call setSize() rather call pack() before setting JFrame visible.
Dont call setPrefferedSize() rather override getPrefferedSize()
Your code works as expected, it will only print the message if panel1 is clicked, note panel1 is behind JScrollPane, thus anything outside the green border is panel1. To make it work for both the JScrollpane/panel2 and JPanel/panel1 simply add the MouseListener to BOTH of the required components:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class LostMouseEvent {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LostMouseEvent();
}
});
}
public LostMouseEvent() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
JPanel panel1 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(880, 630);
}
};
JPanel panel2 = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(840, 610);
}
};
JScrollPane pane = new JScrollPane(panel2);
panel1.setBorder(BorderFactory.createLineBorder(Color.blue));
panel2.setBorder(BorderFactory.createLineBorder(Color.green));
panel1.add(pane);
frame.add(panel1);
MouseListener ml=new MyMouseListener();
//add mouse listener to panel1 and panel2
panel1.addMouseListener(ml);
panel2.addMouseListener(ml);
//alternatively add to pane
//pane.addMouseListener(ml);
frame.pack();
frame.setVisible(true);
}
});
}
private class MyMouseListener extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent me) {
System.out.println(me);
}
}
}
EDIT:
I personally would not recommend this, however,
To add a single listener to the JFrame that will capture all MouseEvents use Toolkit class and call addAWTEventListener like so:
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent awte) {//all mouse events will be processed here you will have to check for the mouse events you are interested in
System.out.println(awte);
}
}, AWTEvent.MOUSE_EVENT_MASK);//for Mouse events only
UPDATE 1:
You could also add the MouseListener to your JFrames glasspane via JFrame.getGlassPane().addMouseListener(ml) dont forget to set the glasspane visible after setting JFrame visible. This will allow you to only have to add a single Listener. See here:
...
MouseListener ml = new MyMouseListener();
//add mouse listener to panel1 and panel2
//panel1.addMouseListener(ml);
//panel2.addMouseListener(ml);
//alternatively add to pane
//pane.addMouseListener(ml);
frame.getGlassPane().addMouseListener(ml);
frame.pack();
frame.setVisible(true);
frame.getGlassPane().setVisible(true);
...
UPADTE 2:
The main reason for you having the problem of the MouseEvent getting lost in JScrollPane is because its a bug. See here.
The work around shown is:
public Test()
{
setUI(new javax.swing.plaf.metal.MetalScrollPaneUI(){
public void installListeners(JScrollPane scrollPane){}
});
JPanel canvas = new JPanel();
canvas.add( new JLabel("Test") );
setViewportView( canvas );
setVisible(true);
}
MouseEvent lost in JScrollPane
answer is very / quite simple, be sure that there isn't something about lost events, nor with JScrollPane,
Swing JComponent can firing event only if is there added proper Listener
you not added MouseListener to second JPanel,
this JPanel is placed into parent JPanel, this parent has added MouseListener then firing mouseEvent, sure in your case only outside of Bounds of 2nd JPanel added to this container
then 2nd JPanel is deepestComponentAt, and not possible fire event without redispatch coordinates from parent to child
you can to redispatch event programatically too,

One JFrame opening another

I have a JFrame and JPanel full of Jsomethings with an actionlistener. When the user clicks an object I want to open another JFrame. Here is what I did:
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == rejectionbutton){
RejectApp ra = new RejectApp();
ra.main(null);
}
}
(RejectApp calls a new JFrame.) So another JFrame opens on the screen with more options. It works OK (so far), but I want to know is this standard? I mean calling the main method like this?
Another question is, without using a cardlayout (which I don't want to use), is the best way to handle multiple panels, by doing this sort of thing?
I would change a few things. First off, usually an application has one JFrame and then if it needs to show another window does so as a modal or non-modal dialog such as can be obtained with a JDialog or JOptionPane. Having said that, it's even more common to have one JFrame and swap "views" in the JFrame -- swap contentPanes or other large panels via a CardLayout as this would mimic the behavior of many gui programs we all currently use.
Personally, I also try to gear my GUI creation towards creating a JPanel or JComponent rather than towards creating a top-level window. This way if I want to display the GUI as a stand alone app, a dialog, or an applet I can pop it into the contentPane of a JFrame or JDialog or JApplet respectively, or if as an inner panel of a more complex GUI, then insert it there, or in an application with a swapping view, then as a card in a CardLayout as noted above. The bottom line is I feel that this structure gives you the developer a lot more options in how you can use this GUI.
Also, I would avoid calling another class's main as you're doing (assuming this is the public static void main method) as you lose all benefits of OOPs. You also seem to be trying to call a static method in a non-static way (assuming I understand your program structure correctly).
For your second question, it begs a question of my own: why do you not want to use CardLayout?
edit: an example of what I meant is as follows:
import java.awt.Dimension;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SwingEg {
private static void createAndShowUI() {
JFrame frame = new JFrame("Main JFrame");
frame.getContentPane().add(new MainGUI().getMainPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class MainGUI {
private static final Dimension MAIN_PANEL_SIZE = new Dimension(450, 300);
private JPanel mainPanel = new JPanel();
private JDialog modalDialog;
private JDialog nonModalDialog;
public MainGUI() {
JButton openModalDialogBtn = new JButton("Open Modal Dialog Window");
openModalDialogBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openModalDialogBtnActionPerformed(e);
}
});
JButton openNonModalDialogBtn = new JButton("Open Non-Modal Dialog Window");
openNonModalDialogBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openNonModalDialogBtnActionPerformed(e);
}
});
mainPanel.setPreferredSize(MAIN_PANEL_SIZE);
mainPanel.add(openModalDialogBtn);
mainPanel.add(openNonModalDialogBtn);
}
private void openModalDialogBtnActionPerformed(ActionEvent e) {
if (modalDialog == null) {
Window topWindow = SwingUtilities.getWindowAncestor(mainPanel);
modalDialog = new JDialog(topWindow, "Modal Dialog", ModalityType.APPLICATION_MODAL);
modalDialog.getContentPane().add(new DialogPanel().getMainPanel());
modalDialog.pack();
modalDialog.setLocationRelativeTo(topWindow);
modalDialog.setVisible(true);
} else {
modalDialog.setVisible(true);
}
}
private void openNonModalDialogBtnActionPerformed(ActionEvent e) {
if (nonModalDialog == null) {
Window topWindow = SwingUtilities.getWindowAncestor(mainPanel);
nonModalDialog = new JDialog(topWindow, "Non-Modal Dialog", ModalityType.MODELESS);
nonModalDialog.getContentPane().add(new DialogPanel().getMainPanel());
nonModalDialog.pack();
nonModalDialog.setLocationRelativeTo(topWindow);
nonModalDialog.setVisible(true);
} else {
nonModalDialog.setVisible(true);
}
}
public JPanel getMainPanel() {
return mainPanel;
}
}
class DialogPanel {
private static final Dimension DIALOG_SIZE = new Dimension(300, 200);
private JPanel dialogPanel = new JPanel();
public DialogPanel() {
dialogPanel.add(new JLabel("Hello from a dialog", SwingConstants.CENTER));
dialogPanel.setPreferredSize(DIALOG_SIZE);
}
public JPanel getMainPanel() {
return dialogPanel;
}
}
I would rather make a new instance of JFrame or a subclass, or call a new method who makes a new JFrame:
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == rejectionbutton){
JFrame frame = new JFrame("New Frame");
//or
makeNewFrame();
}
}
Another simple Layout-Manager is the BorderLayout, it´s the default Layout-Manager of the JFrame class.
new YourJFrameNameHere().setVisible(true);
Replace YourJFrameNameHere with the JFrame name.
Simple, no?

Categories