Java MouseEvent, check if pressed down
Asked Answered
U

5

9

I have a class that implements MouseListener (JPanel). When I click on the panel something happens. What I want is some kind of while-loop that loops as long as left mousebutton is pressed down.

@Override
public void mousePressed(MouseEvent e) {
    while(e.isPressedDownD) {      // <--
        //DO SOMETHING
    }
}

This obviously doesn't work, but I hope you understand what I'm trying to achieve. The whole class for those that are interested:

package control;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import model.GridModel;
import view.GUIView;

public class MapListener implements MouseListener{
    private GridModel model;
    private GUIView view;
    private int posX;
    private int posY;

    public MapListener(GridModel model, GUIView view) {
        this.model = model;
        this.view = view;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        posX = e.getX();
        posY = e.getY();
        model.setMouseAtX(posX);
        model.setMouseAtY(posY);
        view.paintTile();
        System.out.println("X: " + posX + " Y: " + posY);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        while(e.getModifiers() == MouseEvent.MOUSE_PRESSED) { //Obviously doesn't work
            //DO SOMETHING
        }
    }

    @Override
    public void mouseReleased(MouseEvent arg0) {
    }
}
Unclasp answered 26/7, 2011 at 10:31 Comment(0)
A
14

As pointed out by other answers, the place to do your work is not in the mouse event listener methods.

Also there is no explicit "mouse pressed" notion in MouseEvent, so you must track that yourself. I have provided an example of how to do this. Also note the MouseEvent.BUTTON1 references, as this is just to track the state of the left mouse button.

This is where you must start to learn about concurrency. For that reason, I've added in a synchronized method as you need to be aware that funny things happen when multiple threads access properties at the same time, and synchronized is a mechanism for keeping this sane. Consider it further reading beyond the scope of this example.

Untested, but this should work:

volatile private boolean mouseDown = false;

public void mousePressed(MouseEvent e) {
    if (e.getButton() == MouseEvent.BUTTON1) {
        mouseDown = true;
        initThread();
    }
}

public void mouseReleased(MouseEvent e) {
    if (e.getButton() == MouseEvent.BUTTON1) {
        mouseDown = false;
    }
}

volatile private boolean isRunning = false;
private synchronized boolean checkAndMark() {
    if (isRunning) return false;
    isRunning = true;
    return true;
}
private void initThread() {
    if (checkAndMark()) {
        new Thread() {
            public void run() {
                do {
                    //do something
                } while (mouseDown);
                isRunning = false;
            }
        }.start();
    }
}
Apologue answered 26/7, 2011 at 10:57 Comment(2)
Just curious (I know this is old), how come you never call checkAndMark() ?Baxie
In this example both mouseDown and isRunning should be declared volatile. (This causes all threads to look at these variables in memory, bypassing instances in the cache that may not reflect their most up-to-date values. For further discussion, see this answer: https://mcmap.net/q/15165/-what-is-the-volatile-keyword-useful-for )Treasure
K
14

Why do so many of these answers wrongly assert that there is no explicit "mouse pressed" notion in MouseEvent?

Although the other commenters are correct that the OP should not be doing all that stuff in an event handler, there are other situations in which querying the button state in a mouse listener is useful. In those cases, you actually CAN determine the button down state. For example:

@Override
public void mouseExited(MouseEvent event) // Or any other mouse event handler...
{
    int buttonsDownMask = MouseEvent.BUTTON1_DOWN_MASK 
            | MouseEvent.BUTTON2_DOWN_MASK 
            | MouseEvent.BUTTON3_DOWN_MASK; // Or whichever buttons you care about...
    if ( (event.getModifiersEx() & buttonsDownMask) != 0 )
        System.out.println("Hey! Some button is pressed!");
}

Notice in particular the use of the MouseEvent.getModifiersEx() method, along with MouseEvent.BUTTON1_DOWN_MASK and friends.

Kruter answered 18/7, 2014 at 18:13 Comment(1)
That looks like a badly designed API to me. Why would one hide those things in masks? They should be a function of sorts.Apologue
R
4

You could create a new Thread containing your while loop. You start that Thread when the mouse button is pressed. You stop it when the mouse button is released.

Remindful answered 26/7, 2011 at 10:35 Comment(0)
U
3

You shouldn't be doing that in an event handler as no more events will be processed until the event handler exits.

What you want to achieve can be done with a separate worker thread. Create the thread from the mousePressed listener, do whatever you want to do in the thread (this should contain the while loop) and make the thread exit when the mouse is released (your mouseReleased listener should notify the thread).

Unaccountedfor answered 26/7, 2011 at 10:37 Comment(0)
S
2

for example

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ClickListener extends MouseAdapter implements ActionListener {

    private final static int clickInterval = (Integer) Toolkit.getDefaultToolkit().getDesktopProperty("awt.multiClickInterval");
    private MouseEvent lastEvent;
    private Timer timer;

    public ClickListener() {
        this(clickInterval);
    }

    public ClickListener(int delay) {
        timer = new Timer(delay, this);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        /*if (e.getClickCount() > 2) {
            return;
        }
        lastEvent = e;
        if (timer.isRunning()) {
            timer.stop();
            doubleClick(lastEvent);
        } else {
            timer.restart();
        }*/

        if (timer.isRunning() && !e.isConsumed() && e.getClickCount() > 1) {
            System.out.println("double");
            timer.stop();
        } else {
            timer.restart();
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        timer.stop();
        singleClick(lastEvent);
    }

    public void singleClick(MouseEvent e) {
    }

    public void doubleClick(MouseEvent e) {
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Double Click Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.addMouseListener(new ClickListener() {

            @Override
            public void singleClick(MouseEvent e) {
                System.out.println("single");
            }

            @Override
            public void doubleClick(MouseEvent e) {
                System.out.println("double");
            }
        });
        frame.setPreferredSize(new Dimension(200, 200));
        frame.pack();
        frame.setVisible(true);
    }
}
Stunning answered 26/7, 2011 at 10:41 Comment(1)
Interesting. I was unaware of multiClickInterval; I always thought that getClickCount() used the platform's control panel setting.Keheley

© 2022 - 2024 — McMap. All rights reserved.