I am trying to achieve:
1. User performs a mouse-press on parent JFrame
2. Child JFrame becomes visible at mouse location
3. While mouse-button remains pressed, the user can drag the child JFrame across the screen by moving the mouse
The problem:
I can mimic a mouse-press but it does not 'grab' the child JFrame--hence the child JFrame is not being dragged unless the user manually clicks the child JFrame again. I want the process to be smooth without any interruptions: i.e. steps 1-3 (above) should all execute with a single mouse-press.
Failed attempts:
1. I have tried using Robot's mousePressed() to simulate an additional mouse-press on the child. This works, however, it's not clean and can be quite buggy--especially if the PC/device is slow or the user moves the mouse too quickly. This is not a good solution.
2. Using the Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new MouseEvent()); results in the exact same issue as depicted by the current code.
3. When adding a KeyListener (for testing) to both the child and the parent, when the mouse-press is made, the child window is focused and responds to the implemented KeyListener--the parent's KeyListener is not activated..
The Code:
final JFrame parent = new JFrame(), child = new JFrame();
parent.setSize(256, 256);
child.setSize(128,128);
parent.setVisible(true);
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
int id = MouseEvent.MOUSE_PRESSED;
long time = System.currentTimeMillis();
int x = 48;
int y = 48;
int button = MouseEvent.BUTTON1_MASK;
child.dispatchEvent(new MouseEvent(child, id, time, button, x, y, 1, false));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
child.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed!");
}
});
Even though the child JFrame has the focus, the MousePress remains active on the parent. To resolve the issue in a stable manner, the parent should 'forward' its MouseDragged event to the child as shown below:
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
parent.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.dispatchEvent(new MouseEvent(child, e.getID(), e.getWhen(), e.getButton(), e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(parent.getX()+e.getX()-48, parent.getY()+e.getY()-48);
}
});
Even this is not fully 'clean' because the code is relying on the parent's frame visibility. However, it is a stable solution (unlike Robot).
Related
my swing application one panel have 6 button. when cursor goes on the button i want to change default cursor to hand cursor and cursor is exit then it want to change default cursor. Now i am doing this thing using below code.
private void btnRegisterReceiptMouseEntered(java.awt.event.MouseEvent evt) {
btnRegisterReceipt.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
private void btnRegisterReceiptMouseExited(java.awt.event.MouseEvent evt) {
btnRegisterReceipt.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
Now i want to write this code to each and every button.
But I want to write common method to do this one. I already try to use MouseListener do this thing, but I can not get which is the mouse point component.
I don't know it is possible or not. if it is possible please anyone tell me how to do this things.
private void changeCursor() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered( MouseEvent e ) {
/*if ( mouse Entered compornent is button ) {
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
} else {
button.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}*/
}
});
}
Write a generic MouseListener (as an annonymouse class):
MouseListener ml = new MouseAdapter()
{
#Override
public void mouseEntered(MouseEvent e)
{
e.getComponent( setCursor(...) );
}
#Override
public void mouseExited(MouseEvent e)
{
e.getComponent( setCursor(...) );
}
};
Then you can just add the MouseListener to any component you want with:
btnRegisterReceipt.addMouseListener( ml );
anotherButton.addMouseListener( ml );
You can also make this as a reusable class:
public MousePointerListener extends MouseAdapter
{
#Override
public void mouseEntered(MouseEvent e)
{
e.getComponent( setCursor(...) );
}
#Override
public void mouseExited(MouseEvent e)
{
e.getComponent( setCursor(...) );
}
}
Then you use:
MouseListener ml = new MousePointerListener();
btnRegisterReceipt.addMouseListener( ml );
anotherButton.addMouseListener( ml );
The key point in both examples is that you can get the source of the event from the MouseEvent, which allows you to write generic code. You should look at this approach for all you listeners, instead of letting your IDE generate the listener code.
Problem
I have two MouseEvent handlers (mouseClicked and mouseMoved), and they both work, but only separately.
If I click the mouse, the action gets processed fine. (bullet gets fired)
If I move the mouse, the action gets processed fine. (the sprite moves)
The problem occurs when I do both actions at the same time (moving mouse whilst clicking). The mouseMoved event goes through fine, but the mouseClicked event doesn't get called.
The below code is in my Game class constructor, which is extending JPanel.
this.addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent e){
//This creates a new bullet, and adds to an array to get drawn.
bullet = new Bullet(105, e.getY()+5, bulletTexture);
bulletsOnScreen.add(bullet);
e.consume();
}
});
this.addMouseMotionListener(new MouseAdapter(){
#Override
public void mouseMoved(MouseEvent e){
//This increments the sprites position on the screen.
player.setYPosition(e.getY()-50);
e.consume();
};
});
What I've tried
I have tried using SwingWorker objects to run the mouseMoved in a background thread, but the results are the same (bullet doesn't fire).
#Override
public void mouseMoved(MouseEvent e){
SwingWorker myWorker = new SwingWorker<Void, Void>(){
#Override
protected Void doInBackground() throws Exception {
player.setYPosition(e.getY()-50);
e.consume();
return null;
}
};
myWorker.execute();
};
I have also tried to check for a mouseClick within the mouseMoved method, again to no success.
public void mouseMoved(MouseEvent e){
if(e.getButton() == MouseEvent.MOUSE_CLICKED){
//Create bullet and add to array (but this never gets called)
}
player.setYPosition(e.getY()-50);
e.consume();
};
If anyone has any ideas or pointers, it would be great thanks.
Here's some code I cooked to have full working example of behavior you described.
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed");
}
#Override
public void mouseClicked(MouseEvent e) {
System.exit(1);
}
});
frame.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
System.out.println("Moved");
}
});
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(100, 100);
frame.setVisible(true);
}
});
}
}
As you will see when moving around frame you will get lots of "Moved" events.
Most interesting for you will be part that when you press mouse button down you will see "Pressed". If you release without moving there will be clicked event ( app will exit ). Closing app might be a bit extreme but we have to be sure that we don't miss that event.
But when you press down mouse button, hold it and move, you won't see any move event triggered. Then, when you release, you won't get clicked event fired either.
I'm not sure about this part but it looks like mouseClicked will be triggered only if mousePressed and mouseReleased event occur one after another.
In your game, when you click and move your mouse at the same time you basically do
mousePressed -> mouseMoved -> mouseReleased
but this doesn't fire mouseClicked as a result.
My advice on that would be that you handle mousePressed instead of mouseClicked or try to adapt MouseAdapter#mouseDragged method to your needs.
I am making a Pong program, and I have a start button that begins to draw everything, and quite literally to get the ball rolling (you're welcome for the pun). Anyways, when I hit the start button, a Key Listener to move the paddles won't work unless I click the mouse somewhere on the canvas to give it priority. Is there some sort of code to automatically "click" on the canvas without the user being hassled to do so? Thanks in advance.
This Is running awt by the way. I realize I should learn swing, but never got around to it.
public class Pong extends Applet implements ActionListener, KeyListener
{
Canvas c1;
Graphics myG;
Button start;
ball ball;
paddle LPaddle;
paddle RPaddle;
public void init()
{
this.setSize(1300,700);
c1 = new Canvas();
add(c1);
c1.addKeyListener(this);
c1.setBackground(Color.pink);
start = new Button("Start");
add(start);
start.addActionListener(this);
ball = new ball();
LPaddle = new paddle();
RPaddle = new paddle();
myG = c1.getGraphics();
}
public void paint(Graphics g)
{
c1.setLocation(0,0);
c1.setSize(1251,700);
start.setLocation(1255,350);
start.setSize(40,20);
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode()==KeyEvent.VK_UP)//up
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleUp();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_DOWN)//down
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleDown();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_W)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleUp();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_S)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleDown();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_ENTER)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}
KeyListeners are low level interfaces which have a major, significant draw back: The componet they are registered to must be focusable and be focused.
By clicking the start button, you are giving the button focus.
You could call requestFocusInWindow on the instance of the canvas, but this assumes that the the canvas is focusable in the first place.
If you can, you'd be better of using a JComponent/JPanel as the base for your game canvas and use the key bindings API.
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);
}
}
I've implemented right mouse click for open menu listener on my main Jframe, it works fine except one problem. One out of 5 (give or take) clicks it not responding, this can be very annoying for the user. Here is my code:
contentPane = new JPanel();
contentPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3)
{
//Do Stuff
}
}
});
Can you please help me
You won't get clicks from sub-components of contentPane.
I think your problem is that you have added things to your panel. When the user clicks at regions occupied by a sub-component, that sub-component get's the click event.
Quick fix: I would recommend you to add the same mouse listener to all sub-components.
You are not "clicking"
A click is when the mouse is pressed and release really quickly. If you are not careful you might get events for (for instance) "pressed, moved, released" instead of "clicked".
Quick fix: use mouseReleased event instead.
Use this Code instead:
private MouseAdapter listener = new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (downer) {
downer = false;
if (new Rectangle(e.getComponent().getLocationOnScreen(), e.getComponent().getSize())
.contains(e.getLocationOnScreen())) {
downer = false;
// CODE
new Thread(new Runnable(){
public void run(){
//Your Listener code
}
}).start();
/// COde
}
}
}
boolean downer = false;
public void mousePressed(java.awt.event.MouseEvent e) {
downer = true;
}
};
This code only reacts if you press on the component and release on the component AND starts a new Thread for the custom task. This should work allways, because the AWT Thread isnt blocked with long calculations.