Why isn't componentHidden called for my JPopupMenu? - java

I want to be notified when my JPopupMenu is hidden — whether because an item was selected, the menu was dismissed, or setVisible(false) was called on it. Here is my test code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class A extends ComponentAdapter implements Runnable, ActionListener {
private JButton b;
public static void main(String[] args) {
EventQueue.invokeLater(new A());
}
public void run() {
JFrame f = new JFrame("Test");
b = new JButton("Click me");
b.addActionListener(this);
f.add(b);
f.pack();
f.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
JPopupMenu pm = new JPopupMenu();
pm.addComponentListener(this);
pm.add("Popup...");
pm.add("...menu!");
pm.show(b, 10, 10);
}
public void componentShown(ComponentEvent e) { System.out.println("componentShown"); }
public void componentHidden(ComponentEvent e) { System.out.println("componentHidden"); }
}
Regardless of how I interact with the menu, neither of the two ComponentListener methods are being called. Why is that? Is there different/better/correct way of finding out when my JPopupMenu is hidden?
Thanks,
Cameron

JPopupMenu has a special listener for visibility change events:
pm.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
System.out.println("cancelled");
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
System.out.println("vanishing");
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
System.out.println("appearing");
}
});
Note, however, as method names hint, they are called before visibility changes, so if you're calling isVisible() somewhere in the event handlers, you should be aware of that, for example:
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
updateMenu();
}
private void updateMenu() {
if (!menu.isVisible()) { // this won't work!
// perform some updates
}
}
With regards to why ComponentListener isn't sending you events on the menu disappearing, this might explain:
The component-hidden and component-shown events occur only as the result of calls to a Component 's setVisible method. For example, a window might be miniaturized into an icon (iconified) without a component-hidden event being fired.
Source: ComponentListener tutorial (non-canonical perhaps, but from the horse's mouth.)
Consider that in conjunction with JPopupMenu's implementation of setVisible:
public void setVisible(boolean b) {
// Not supported for MenuComponents
}
And you might know how it so happens, but not why it happens (what is the justification and where is that properly documented?)

Related

How can i wait for a frame to perform an action before the code execute the next line?

I want to initialize a Graphical User Interface (GUI) for the user to input a form. After this is accomplished i want to open a new GUI, but as soon as the first GUI pops-up the next one is initialized to.
Is there any way to solve this without using waits and notifies?
here is an example of my code:
public static void main(String[] args) {
new GUIForm();
// wait until the user inputs the complete form
new GUIWelcome();
}
It is really simple I woild like to keep it that way.
Create an Interface OnActionListener
public interface OnActionListener {
public void onAction();
}
Add these code in GUIForm class
private OnActionListener listener;
private JButton action;
public GUIForm(OnActionListener listener) {
this.listener = listener;
action = new JButton("Action");
action.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
GUIForm.this.listener.onAction();
}
});
}
Now you can achieve that
new GUIForm(new OnActionListener() {
#Override
public void onAction() {
new GUIWelcome();
}
});
You need to use some sort pub/sub mechanism. This in a nutshell is what you need:
public class PubSub {
public static void main(String[] args) {
JFrame frame1 = new JFrame("GUIForm");
frame1.setSize(640, 480);
JButton button = new JButton("User Input");
JFrame frame2 = new JFrame("Welcome");
frame2.setSize(320, 240);
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
button.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
#Override
public void mouseExited(MouseEvent e) {
button.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
#Override
public void mouseClicked(MouseEvent e) {
frame2.setVisible(true);
}
});
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.add(button);
frame1.setVisible(true);
}
}
This version uses JFrame's listeners, but you could implement your on callback mechanism to accomplish the same

Make a JTextField recieve input even with windows not focused

I'm working on a small personal project.
I have two JFrame windows, Viewer and Console.
Viewer only contains a subclass of JPanel, and it should respond to mouse input (clicking, mostly).
Console contains a button, a JTextField (the input), and a JTextArea (the output).
I need to make sure than when I press keys on my keyboard, the corresponding text appears in the Console JTextField, not only when the focus is held by the JTextField, but also when it's held any other component in my app.
In other words, I want the JTextField to accept input even right after I clicked on the Viewer frame.
Is this feasible?
In case that matters, I'm running win 8 x64 and I don't really care about portability (I'm the only one that will ever look at or use this code).
EDIT: here is an example of my code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class main {
public static Viewer v;
public static Console c;
public static void main(String[] args) {
v=new Viewer();
c=new Console();
}
static class Viewer extends JFrame {
public Viewer(){
setSize(600,600);
getContentPane().add(new myPanel());
addMouseListener(new mouse());
setVisible(true);
}
}
static class myPanel extends JPanel{
public void paintComponent(Graphics g){
g.setColor(new Color((int)(Math.random()*255),(int)(Math.random()*255),(int)(Math.random()*255)));
g.fillRect(0, 0, 600, 600);
}
}
static class Console extends JFrame {
public Console(){
setSize(600,200);
JTextField text=new JTextField();
getContentPane().add(text);
setVisible(true);
}
}
static class mouse implements MouseListener{
#Override
public void mouseClicked(MouseEvent arg0) {
v.repaint();
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
#Override
public void mousePressed(MouseEvent arg0) {
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
}
In this example, after I click on the big window to change its color, if I want to write stuff in the other window I have to click on it first.
Can I suggest the KeyboardFocusManager? I've found this to be the easiest way to achieve what I believe you are trying to achieve
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
public void dispatchKeyEvent(KeyEvent ke) {
//TODO: fill this in
}
});
One inelegant solution is to create a KeyListener, which feeds typed characters to your console, although how exactly this happens depends on how you create your components. For the sake of this example I'll pretend to do it through a static method in your Console class (preferably you'd have access to an instance of Console):
public class ApplicationKeyListener implements KeyListener {
public ApplicationKeyListener() {
}
// Other necessary methods...
public void keyPressed(KeyEvent e) {
char pressed = e.getKeyChar();
Console.feedChar(pressed);
}
}
And then in Console, make sure your JTextField object is global and static, then set up the feedChar() method.
public class Console extends JFrame {
private static JTextField consoleTextField;
public Console() {
consoleTextField = new JTextField();
// ...
}
public static feedChar(char c) {
consoleTextField.setText(consoleTextField.getText() + c.toString());
}
}
Then finally you'd have to add this listener to all JFrames and their children.
public class Viewer extends JFrame {
public Viewer() {
ApplicationKeyListener kl = new ApplicationKeyListener();
addKeyListener(kl);
for (Component child : this.getComponents()) {
child.addKeyListener(kl);
}
}
}

Java: check if the mouse has clicked

i am in trouble with checking if the mouse has clicked with a JFrame. When i use public void mousePressed(MouseEvent e) to print something and i click with the mouse it doesn't print anything. It doesn't gives a error, it just prints nothing out. Here is my code :
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class tuna extends JFrame
{
private JTextArea textArea;
public static void main(String[] args)
{
//Run the program
tuna run = new tuna();
run.setDefaultCloseOperation(3);
run.setSize(1200, 1000);
run.setVisible(true);
}
public tuna()
{
super("Simple JFrame");
//add a simple JScrollPane
textArea = new JTextArea(10,10);
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setEditable(true);
add(scrollPane);
}
//This doesn't print anything when i am clicking in the JFrame
public void mousePressed(MouseEvent e)
{
if(e.getButton() == MouseEvent.BUTTON1)
{
System.out.println("left");
}
else if(e.getButton() == MouseEvent.BUTTON3)
{
System.out.println("right");
}
}
}
Thank you in advance.
You can make you own Mouse Listener and add it to textArea or another component.
For example like this:
public class tuna extends JFrame
{
private JTextArea textArea;
public static void main(String[] args)
{
//Run the program
tuna run = new tuna();
run.setDefaultCloseOperation(3);
run.setSize(1200, 1000);
run.setVisible(true);
}
public tuna()
{
super("Simple JFrame");
//add a simple JScrollPane
textArea = new JTextArea(10,10);
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setEditable(true);
textArea.addMouseListener(new CustomListener());
add(scrollPane);
}
//This doesn't print anything when i am clicking in the JFrame
public class CustomListener implements MouseListener {
#Override
public void mouseClicked(MouseEvent mouseEvent) {
if(mouseEvent.getButton() == MouseEvent.BUTTON1)
{
System.out.println("left");
}
else if(mouseEvent.getButton() == MouseEvent.BUTTON3)
{
System.out.println("right");
}
}
#Override
public void mousePressed(MouseEvent mouseEvent) {
}
#Override
public void mouseReleased(MouseEvent mouseEvent) {
}
#Override
public void mouseEntered(MouseEvent mouseEvent) {
}
#Override
public void mouseExited(MouseEvent mouseEvent) {
}
}
}
Well there are few issues, in your concept and source code
First you need a MouseListener, so in your case either you can
create a separate MouseListener or use the current JFrame class
itself, like this
public class Tuna extends JFrame implements MouseListener
Also I would recommend to follow proper naming convention and use Tuna instead of tuna.
Then the element you want to respond on the MouseEvents should be
register with the MouseListener created in first step, in your constructor.
textArea.addMouseListener(this);
Also make sure on what element you want to register your
MouseListener, currently your entire Frame is covered by TextArea,
so registering listener on JFrame won't help, instead add it on
JTextArea
Try using #Override annotation where ever possible, editor shows
appropriate compiler errors then, in your case you just wrote,
public void mousePressed(MouseEvent e)
instead of
#Override
public void mousePressed(MouseEvent e)
As this methods works only if you implement an MouseListener
Refer this link for more understanding,
https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html

Make a class that have "addActionListener" method

i'm doing a class that does a sort of "file explorer",
in the constructor i create the frame ,panel ecc.. but than i want to say to the main program that calls this class that the user has finish the selection, i know i can call a static method that is in the main from this class,but i want to make a action listener because i want to use this class for different programs
For Example if FileEx is my class:
public class FileEx()
{
public FileEx()
{
//program that do something
if(done == true)
//here i want to call the action
}
public void addActionListener(ActionListener ac) //i don't know if it's correct
//but i want something like this
{
}
}
public static void main(String[] args)
{
FileEx fileex = new FileEx();
fileex.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e)
{
//when done is true i want this block of code to be called
}
});
}
ActionListeners will only work when added to components that allow them to be added and that notify listeners with them such as JButtons, JMenuItems, JComboBoxes and such. We have no idea what type of class FileEx is or why it should accept an ActionListener and a little more information would be qutie helpful. If you want to notify another object that an event occurs, such as that a calculation is done, use another type of listener such as a PropertyChangeListener. Alternatively you could do the processing in a modal JDialog window, which will notify the calling window that it is done performing its duties by returning code flow to the calling window.
For example, please look at my answers to similar questions:
Drawing with paintComponent after value of Jbutton changed in another class
JTextField data in different frames, with data stored in global variable?
Loop making program freeze
EDIT
For example, if you wanted your FileEx to allow other classes to listen for changes to a String called selection (the so-called "bound" property) you could create it to look something like:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class TestFileEx {
public static void main(String[] args) {
final FileEx fileEx = new FileEx();
fileEx.addPropertyChangeListener(FileEx.SELECTION, new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
// TODO code to call when fileEx has changed selections
String fileExSelection = evt.getNewValue().toString();
// or
String fileExSelection2 = fileEx.getSelection();
}
});
}
}
and
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
publicclass FileEx {
public static final String SELECTION = "selection";
private SwingPropertyChangeSupport propertyChangeSupport = new SwingPropertyChangeSupport(
this);
private String selection;
public void someMethodThatChangesSelection() {
}
public String getSelection() {
return selection;
}
public void setSelection(String selection) {
String oldValue = this.selection;
String newValue = selection;
this.selection = selection;
// notify the listeners of change
propertyChangeSupport.firePropertyChange(SELECTION, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public void rem(String propertyName, PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
}
}
Here is the code based on your example which adds actionlisteners and calls them:
public class FileEx()
{
private final List<ActionListener> listeners = new ArrayList<>();
public FileEx()
{
//program that do something
if(done == true) {
notifyListeners();
}
}
public void addActionListener(ActionListener ac)
{
listeners.add(ac);
}
private void notifyListeners()
{
for (final ActionListener listener: listeners)
{
listener.actionPerformed(null);//You can create event if you want.
}
}
}
public static void main(String[] args)
{
FileEx fileex = new FileEx();
fileex.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e)
{
//when done is true i want this block of code to be called
}
});
}

MouseMotionListener in child component disables MouseListener in parent component

I need help to understand the event propagation in Swing. I know that each event is handled by only one component. Thus, when I have a panel outside with some child panel inside and I add mouseListeners to both of them, the one of inside will be called. That's nice and that's the expected behavior.
But I don't understand the behavior in the following situation:
inside registers a MouseMotionListener and outside registers a MouseListener. I expect inside to consume all MouseMotionEvents and outside to receive the MouseEvents, because there is no listener for normal MouseEvents on inside. But that's not the case, inside somehow consumes all MouseEvents not only the MouseMotionEvents.
The following code illustrates the problem:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventTest {
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
JComponent inside = new JPanel();
inside.setBackground(Color.red);
inside.setPreferredSize(new Dimension(200,200));
MouseMotionListener mm = new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent arg0) {
System.err.println("dragged");
}
#Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved");
}
};
// next line disables handling of mouse clicked events in outside
inside.addMouseMotionListener(mm);
JComponent outside = new JPanel();
outside.add(inside);
outside.setPreferredSize(new Dimension(300,300));
outside.addMouseListener( new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
System.err.println("clicked");
}
});
JFrame frame = new JFrame();
frame.add(outside);
frame.pack();
frame.setVisible(true);
}
});
}
}
I could work around the problem by registering a listeners on inside for all events the parent component might be interested in and then calling dispatchEvent to forward the event to the parent.
a) can someone point me to some docs, where this behavior is described? The javadocs of MouseEvent made me think that my expectations were right. So, I need a different description to understand it.
b) is there a better solution than the one sketched above?
Thanks,
Kathrin
Edit: It is still unclear, why Swing behaves this way. But as it looks, the only way to get the stuff working is to manually forward the events, I will do it.
a) By design, Java mouse events "bubble up" only if there in no mouse listener on the child component.
b) You can forward events to another component, as shown here and below.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EventTest {
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JComponent outside = new JPanel();
JComponent inside = new JPanel();
inside.setBackground(Color.red);
inside.setPreferredSize(new Dimension(200, 200));
inside.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
System.err.println("dragged");
}
#Override
public void mouseMoved(MouseEvent e) {
System.err.println("moved inside");
outside.dispatchEvent(e);
}
});
outside.add(inside);
outside.setPreferredSize(new Dimension(300, 300));
outside.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved outside");
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(outside);
frame.pack();
frame.setVisible(true);
}
});
}
}
Very similar to trashgod's answer - you can use a MouseAdapter as your motion listener, and override it to forward any events you want to be handled by the parent. This should only add a minimal amount to your code.
MouseAdapter mm = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent arg0) {
System.err.println("dragged");
}
#Override
public void mouseMoved(MouseEvent arg0) {
System.err.println("moved");
}
#Override
public void mouseClicked(MouseEvent e) {
outside.dispatchEvent(e);
}
};
// For forwarding events
inside.addMouseListener(mm);
// For consuming events you care about
inside.addMouseMotionListener(mm);
I too couldn't find any way around using the dispatchEvent(e) method. I think you're stuck with that route.
This worked out for me:
Ellipse2D mCircle = new Ellipse2D.Double(x,y,size,size);
void PassMouseEvent(MouseEvent e) {
getParent().dispatchEvent(e);
}
public void mousePressed(MouseEvent arg0) {
if(mCircle.contains(arg0.getX(), arg0.getY())) {
// Do stuff if we click on this object
} else {
// Pass to the underlying object to deal with the mouse event
PassMouseEvent(arg0);
}
}

Categories