Swing - Get new component under mouseReleased - java

This question probably belies underlying lack of Swing knowledge, but I can't seem to pin down what I'm doing wrong. Essentially I want to detect mousePressed in one component, and then receive the mouseReleased for whatever component is under the mouse when it is released, rather than the original component.
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
import net.miginfocom.swing.MigLayout;
public class TestMouseListener extends MouseInputAdapter {
public void mouseEntered(MouseEvent arg0) {System.out.println("Entered " + arg0.getComponent());}
public void mouseExited(MouseEvent arg0) {System.out.println("Exited " + arg0.getComponent());}
public void mousePressed(MouseEvent arg0) {System.out.println("Pressed " + arg0.getComponent());}
public void mouseReleased(MouseEvent arg0) {System.out.println("Released " + arg0.getComponent());}
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setLayout(new MigLayout());
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TestPanel panel1 = new TestPanel("Panel 1");
TestPanel panel2 = new TestPanel("Panel 2");
panel1.add(new JLabel("Text1"));
panel2.add(new JLabel("Text2"));
frame.add(panel1);
frame.add(panel2);
TestMouseListener listener = new TestMouseListener();
panel1.addMouseListener(listener);
panel1.addMouseMotionListener(listener);
panel2.addMouseListener(listener);
panel2.addMouseMotionListener(listener);
frame.setVisible(true);
}
}
class TestPanel extends JPanel {
String name;
TestPanel(String name){ this.name = name; }
public String toString(){ return name; }
}
So when I mouse over panel 1, click, drag to panel 2, release, I get the following:
Entered Panel 1
Pressed Panel 1
Exited Panel 1
Entered Panel 2
Released Panel 1
How do I get the panel I released over? I was hoping the mouseReleased event would fire for Panel 2 but obviously it does not.

Store a variable that holds your most recently entered component. Keep overwriting it every time you trigger a MouseEntered event. Then have a method, so that when you release the mouse button, you can use whatever your most recently entered component is.
Component lastEntered;
private void MouseEntered(MouseEvent e) {
lastEntered = e.getComponent;
}

Related

JButton KeyPressed - Nothing Happens

I'm trying to make it so that pressing the right arrow key does the same thing as pressing a JButton. I can bind the right arrow key to the button itself - but that means I have to have pressed the button before the right key works. Now I'm trying to see if binding to the actual JFrame is what I want, but I can't get anything to happen when I bind to the frame at all:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
onButtonPress();
}
private void formKeyPressed(java.awt.event.KeyEvent evt) {
if (evt.getKeyCode() == KeyEvent.VK_RIGHT){
onButtonPress();
}
}
private void onButtonPress() {
pressNum++;
jLabel1.setText("Button has been pressed " + pressNum + " times.");
}
As a general rule of thumb, you should avoid KeyListener. The main reason is, in order for a KeyListener to generate key events, the component it is registered to must be focusable AND have keyboard focus. In your case, this would probably mean adding a KeyListener to every component in your UI which "might" gain keyboard focus, not something which is practical in the real world.
Instead, you should make use of the Key Bindings API, which provides you the means to define the level of focus required in order for it to trigger the associated actions.
The Key Bindings API and the example make use of the Actions API, which allows me to define a single unit of work which can be applied to a number of "actionable" controls
The example also makes use of a delegate/callback/listener (namely CounterListener) which allows me to decouple the "side effects" from the action itself.
This basically means that the Action can do what it needs to do, but "other" interested parties can perform some other action when it changes. You could, equally attach an ActionListener to the Action, but this was just simpler and quicker to implement
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JLabel label = new JLabel("...");
MyAwesomeAction action = new MyAwesomeAction(new CounterListener() {
#Override
public void counterChanged(int count) {
label.setText("Button has been pressed " + count + " times");
}
});
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
JButton button = new JButton(action);
add(button, gbc);
add(label, gbc);
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "MakeItSo");
am.put("MakeItSo", action);
}
}
public interface CounterListener {
public void counterChanged(int count);
}
public class MyAwesomeAction extends AbstractAction {
private int count;
private CounterListener listener;
public MyAwesomeAction(CounterListener listener) {
putValue(NAME, "Make it so");
this.listener = listener;
}
#Override
public void actionPerformed(ActionEvent e) {
count++;
listener.counterChanged(count);
}
}
}
This example has a JButton with two listeners: an ActionListener and and a KeyListener. The key listener is implemented with the KeyAdapter abstract class. From the API documentation:
KeyAdapter is for receiving keyboard events. The methods in this class
are empty. This class exists as convenience for creating listener
objects.
Create a listener object using the extended class and then register it
with a component using the component's addKeyListener method. When a
key is pressed, released, or typed, the relevant method in the
listener object is invoked, and the KeyEvent is passed to it.
The example code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ButtonListeners {
private JLabel label;
private int counter;
public static void main(String [] args) {
new ButtonListeners().gui();
}
private void gui() {
JFrame frame = new JFrame();
frame.setTitle("JButton Listeners");
JButton button = new JButton("jButton1");
button.addActionListener(actionEvent -> displayLabel());
button.addKeyListener(new ButtonKeyPressListener());
label = new JLabel("Press button or -> key");
frame.add(button, BorderLayout.SOUTH);
frame.add(label, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(300, 150);
frame.setVisible(true);
}
private void displayLabel() {
label.setText("Action count: " + ++counter);
}
private class ButtonKeyPressListener extends KeyAdapter {
#Override public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
displayLabel();
}
}
}
}

Swing mouseEntered strange behavior

I have a jframe with some JComponents, with some mouseListener. My aim is to show a little jframe with a specified text, when the mouse enter on a jlabel, and to show off it when the mouse is exited.
Jframe is supposed to be shown near the mouse.
Anyway that does not happen, and the programm behaves in a very strange way.
Why?
the bug
That's my code
package finestrina;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class finestra implements MouseListener{
private JFrame finestra = new JFrame();
private JFrame pagina = new JFrame();
private JButton submit1 = new JButton("press");
private JTextField text = new JTextField();
finestra(){
pagina.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pagina.setSize(500, 500);
JPanel cont = new JPanel();
cont.setLayout(new GridLayout(3,4));
JLabel label = new JLabel();
label.setText("ON MOUSEROVER THIS");
cont.add(label);
label.addMouseListener(this);
submit1.addMouseListener(this);
text.addMouseListener(this);
cont.add(submit1);
cont.add(text);
pagina.add(cont);
pagina.setVisible(true);
finestra.setUndecorated(true);
}
#Override
public void mouseClicked(MouseEvent arg0) {}
#Override
public void mouseEntered(MouseEvent event) {
if(event.getSource() instanceof JLabel){
JLabel event_casted = (JLabel)event.getSource();
if(event_casted.getText().equals("ON MOUSEROVER THIS")){
Point punto = event.getLocationOnScreen();
punto.setLocation(punto.getX()+20, punto.getY()+20);
JLabel littlelabel = new JLabel();
littlelabel.setText("your mouse is on the jlabel");
finestra.add(littlelabel);
finestra.setLocation(punto);
finestra.setSize(100,100);
finestra.setVisible(true);
}
}
}
#Override
public void mouseExited(MouseEvent event) {
if(event.getSource() instanceof JLabel){
JLabel event_casted = (JLabel)event.getSource();
if(event_casted.getText().equals("ON MOUSEROVER THIS")){
finestra.setVisible(false);
}
}
}
#Override
public void mousePressed(MouseEvent event) {
}
#Override
public void mouseReleased(MouseEvent arg0) {}
public static void main(String[] args0){
new finestra();
};
}
There are a number of (possible) issues
The GridLayout will cause the component to occupy most of the space of the container, which might cause the window to "appear" to popup earlier then you expect
finestra.add(event_casted); is causing the label to be removed from it's current parent container (the main window), as a component can only belong to a single container
It would, generally be better to use the tooltip support provided by the API. Maybe have a look at How to Use Tool Tips, remember, they can also support HTML.
If that's not functionality what you want, then maybe a JPopupMenu might be better

KeyListener doesn't detect key presses

In my JFrame I have this listener:
this.addKeyListener(new KeyAdapter(){
#Override
public void keyPressed(KeyEvent arg0) {
//do stuff
}
});
This has been working fine until about 10 minutes ago. Now when I press a key, keyPressed() never even gets called. I tested this with eclipse debugger.
I have no idea what happened. Any ideas?
EDIT #1: This is also happening with a button that I have set up. the action listener does not recognize when the button is clicked.
EDIT #2: Ok So I was able to narrow it down. I have a JFrame, this frame has a main panel and also 2 action listeners (keyListener, the problematic one, and a mouse click listener, working fine). The main panel has two subpanels a and b. panel a has 2 buttons, one is not set up yet. It seems that these buttons are somehow conflicting with the keylistener making it so the key listener and buttons do not work. either way the mouse click listener still works.
Edit #3: Ok here is some simplified code:
The button is working, but the keyListener is not. I am hearing about focus a lot, if this is the problem how can I fix it?
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ListenerTest extends JFrame {
private JPanel mainPanel;
private JPanel panelA;
private JPanel panelB;
private JButton buttonA;
private JButton buttonB;
public ListenerTest() {
mainPanel = new JPanel();
panelA = new JPanel();
panelB = new JPanel();
buttonA = new JButton("Button A");
buttonB = new JButton("Button B");
this.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent arg0) {
System.out.println(arg0.getKeyChar());
}
});
this.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e) {
System.out.println(e.getX() + ", " + e.getY());
}
});
buttonA.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Button A pressed!");
}
});
panelA.add(buttonA);
panelA.add(buttonB);
mainPanel.add(panelA);
mainPanel.add(panelB);
this.add(mainPanel);
this.setSize(300, 300);
this.setVisible(true);
}
public static void main(String args[]) {
new ListenerTest();
}
}
When you add an action for a specific key (for example: F2) to panelA, this seems to work:
public ListenerTest() {
// Create components...
panelA.getActionMap().put("saveAction", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("F2");
}
});
//panelA.getInputMap().put(KeyStroke.getKeyStroke("F2"), "saveAction");
panelA.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke("F2"), "saveAction");
//this.addKeyListener(new KeyAdapter() {
//mainPanel.addKeyListener(new KeyAdapter() {
panelA.addKeyListener(new KeyAdapter() {
//buttonA.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent arg0) {
System.out.println("Panel A: " + arg0.getKeyChar());
}
});
// Rest of the code...
}
Adding the action also seems to have a side effect: the KeyListener seems to work again for panelA. The focus no longer goes to one of the buttons by default.
Note: when using panelA.getInputMap(), the key action only works when the buttons do not have the focus. Use panelA.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW) to make sure the action works when one of the buttons has the focus.

Multiple Panes with MouseListener on top of each other

I have an issue with mouse listeners added to 2 different panels one on top of the other. Both of them make use of the mouseEntered() and mouseExited() methods.
The expected outcome of the test code bellow is:
When I hover on the frame a red rectangle should be made visible
When I hover on the red rectangle in the frame it should turn blue.
When my mouse exits the now blue rectangle (but still inside the frame) it should turn red
When my mouse exits the frame entirely the now red rectangle should not be visible
If I try to move my mouse over the colored rectangle it keeps flashing from visible to not visible. The system print shows what I mean, it keeps activating Mouse Entered and Mouse Exited every time I move or click with my mouse. Is there anyway to prevent the mouseExited() method from being called every time I move or click my mouse?
Here is the test code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Sandbox extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.add(new Sandbox());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public Sandbox() {
super(new BorderLayout());
final JPanel panelA = new JPanel();
panelA.setBackground(Color.red);
panelA.setPreferredSize(new Dimension(155, 155));
panelA.setVisible(false);
this.add(panelA, BorderLayout.WEST);
this.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
System.out.println(" - MOUSE ENTERED ");
panelA.setVisible(true);
}
#Override
public void mouseExited(MouseEvent e) {
System.out.println(" - MOUSE EXITED ");
panelA.setVisible(false);
}
});
panelA.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
System.out.println(" # MOUSE ENTERED ");
panelA.setBackground(Color.blue);
}
#Override
public void mouseExited(MouseEvent e) {
panelA.setBackground(Color.red);
System.out.println(" # MOUSE EXITED ");
}
});
}
}
You have that behavior, because you have 2 listeners. When you try to change color to blue it changed, but from first listener executed panelA.setVisible(false); and you don't see that.
You can fix that in next way: change code of mouseExited() method in first listener:
#Override
public void mouseExited(MouseEvent e) {
if(!panelA.contains(e.getPoint())){
panelA.setVisible(false);
}
System.out.println(" - MOUSE EXITED ");
}
EDIT: one more fix for exiting from frame when mouse on panelA : change mouseExited for second listener :
#Override
public void mouseExited(MouseEvent e) {
panelA.setBackground(Color.red);
if (!Sandbox.this.contains(e.getPoint())) {
panelA.setVisible(false);
}
System.out.println(" # MOUSE EXITED ");
}

JFrame stops receiving mouse events

I have a JFrame, which represents the GUI for my Java application.
I have a custom button, derived from JComponent and created and placed on this JFrame. On pressing this button a modal dialog appears.
The problem is, that after modal dialog appears, the JFrame stops receiving mouse events from mouse. I opened Spy++ and found out that MouseEvents from Windows are passed to the JFrame. So they are switched off somewhere in Java.
I need to receive button events for one thing - when mouse enters my custom button area, button changes color. And I need to know when the mouse exits the button area, to change the button to it's original color. The same problem is with standard JButton - it remains hovered after modal dialog appears.
Of course I can track the opening of modal dialog myself, and make my button unhovered, but just curious, if there is standard solution.
You see, that the button on the JFrame remains hovered as the dialog appears.
package quixote.sscce;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class FrameTop extends JFrame implements MouseListener, ActionListener {
private JButton button;
public FrameTop() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(200, 200);
setLocation(100, 100);
setLayout(new BorderLayout());
button = new JButton("Click me");
add(button, BorderLayout.CENTER);
button.addActionListener(this);
addMouseListener(this);
}
#Override
public void actionPerformed(ActionEvent arg0) {
JDialog dialog = new JDialog(this, "dialog");
dialog.setModal(true);
dialog.setLocation(150, 150);
dialog.setSize(100, 100);
dialog.setVisible(true);
}
private int aaa = 0;
#Override
public void mouseClicked(MouseEvent arg0) {
System.out.println("XXX " + aaa);
}
#Override
public void mouseEntered(MouseEvent arg0) {
System.out.println("XXX " + aaa);
}
#Override
public void mouseExited(MouseEvent arg0) {
System.out.println("XXX " + aaa);
}
#Override
public void mousePressed(MouseEvent arg0) {
System.out.println("XXX " + aaa);
}
#Override
public void mouseReleased(MouseEvent arg0) {
System.out.println("XXX " + aaa);
}
public static void main(String args[]) {
final FrameTop top = new FrameTop();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
top.setVisible(true);
}
});
}
}
I think you might have forgot set opaque ie. setOpaque(true).
This will allow you to change background colour of frame and for button button.setOpaque(true).
Try it, it may help you.
Use non modal dialog, and call dialog.setAlwaysOnTop(true);
hope this helps

Categories