Search code examples
javagraphics2dmouselistener

MouseListener on a drawString() Method


How can I detect if the text ("Resume", "Restart", "Quit") that I drew with a drawString() method is being clicked?

My code so far:

public class Pause {

    public Pause() {

    }


    public void draw(Graphics2D g) {

        g.setFont(new Font("Arial", Font.BOLD, 14));
        int intValue = Integer.parseInt( "ff5030",16);      
        g.setColor(new Color(intValue));

        g.drawString("Resume", 200, 156);
        g.drawString("Restart", 200, 172);
        g.drawString("Quit", 200, 188);    
    }
}

I hope you can help me. Thanks

@aioobe I tried to write it simple as possible. Here the SSCCE:

GameFrame.java

public class GameFrame extends JFrame implements Runnable { 
    /**
     * 
     */
    private static final long serialVersionUID = 1L;


    // dimensions
    public static final int WIDTH = 448;
    public static final int HEIGHT = 288;
    public static final double SCALE = 2.5; 

    // game thread
    private Thread thread;
    private boolean running;

    private int FPS = 60;
    private long targetTime = 1000 / FPS;

    // image
    private BufferedImage image;
    private Graphics2D g;

    //displays
    private Pause pauseDisplay;


    public GameFrame() {
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.pack();
        this.setLocationRelativeTo(null);
        this.setPreferredSize(new Dimension((int)(WIDTH * SCALE), (int)(HEIGHT * SCALE)));
        this.setBounds(100, 100, (int)(WIDTH * SCALE), (int)(HEIGHT * SCALE));
        this.setLocationRelativeTo(null);
        this.setFocusable(true);
        this.requestFocus();
        this.setVisible(true);
    }

    public void addNotify() {
        super.addNotify();
        if(thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }

    private void init() {       
        image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        g = (Graphics2D) image.getGraphics();

        running = true;     
    }

    public void run() {     
        init();

        long start;
        long elapsed;
        long wait;

        // game loop
        while(running) {
            start = System.nanoTime();          
            elapsed = System.nanoTime() - start;            
            wait = targetTime - elapsed / 1000000;
            if(wait < 0) wait = 5;

            try {
                Thread.sleep(wait);
            }
            catch(Exception e) {
                e.printStackTrace();
            }

            pauseDisplay = new Pause(this);
            drawToScreen();
            draw();         
        }       
    }


    private void draw() {               
        pauseDisplay.draw(g);
    }


    private void drawToScreen() {
        Graphics g2 = getGraphics();
        g2.drawImage(image, 0, 0, (int)(WIDTH * SCALE), (int)(HEIGHT * SCALE), null);
        g2.dispose();
    }       


    public static void main(String[] args) {
        GameFrame game = new GameFrame();
    }    
}

Pause.java

public class Pause implements MouseListener{

    private Rectangle2D resumeRect;
    private Rectangle2D restartRect;

    public Pause(GameFrame GameFrame) {
        GameFrame.addMouseListener(this);
    }


    public void draw(Graphics2D g) {        
        g.setFont(new Font("Arial", Font.BOLD, 14));
        int intValue = Integer.parseInt( "ff5030",16);      
        g.setColor(new Color(intValue));

        g.drawString("Resume", 200, 156);
        resumeRect= g.getFontMetrics().getStringBounds("Resume", g);

        g.drawString("Restart", 200, 172);
        restartRect = g.getFontMetrics().getStringBounds("Restart", g);

        g.drawString("Quit", 200, 188);
    }


    public void mouseClicked(MouseEvent e) {
        if (resumeRect.contains(e.getPoint())) {
            System.out.println("clicked");
        }

        System.out.println(resumeRect);
        System.out.println(restartRect);
        System.out.println(e.getPoint());
    }


    public void mousePressed(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}

}

Solution

  • You would have to remember the position at which you drew the string. You could have one Rectangle2D for each string.

    The rectangle of the String can be computed as follows:

    Rectangle2D r = g.getFontMetrics().getStringBounds(str, g);
    

    (You need to adjust the rect position according to where you draw the string.)

    You would then register a mouse listener that checks click coordinates against these rectangles:

    if (resumeRect.contains(mouseEvent.getPoint())) {
        ...
    }
    

    That being said, I'd recommend you to reconsider your GUI code and see if you can't use JLabel or JButton for this purpose.


    Regarding your edit:

    Your NullPointerException is due to the fact that you can click on the frame before the image is rendered (i.e. before the rectangles have been initialized).

    Apart from that you need to do two edits:

    1. You need to take the SCALE into account.

      if (resumeRect.contains(e.getPoint().getX() / GameFrame.SCALE,
                              e.getPoint().getY() / GameFrame.SCALE)) {
      

      and

    2. you need to compensate for the fact that drawString draws the string on the base line, so the rectangle should be lifted up from the base line to the top left corner of the text:

      g.drawString("Resume", 200, 156);
      resumeRect= g.getFontMetrics().getStringBounds("Resume", g);
      
      // Add this:
      resumeRect.setRect(200,
                         156 - g.getFontMetrics().getAscent(),
                         resumeRect.getWidth(),
                         resumeRect.getHeight());