Gorbles
Gorbles

Reputation: 1168

Combining MouseListeners with Polygons in Graphics2D

I've tried extending Polygon for my data class of choice (implementing MouseListener). My paintComponent override in the parent JPanel is rendering the extended Polygon class (I called it Hexagon) with .fillPolygon - it renders fine!

But it doesn't let me interact with the MouseListener implemented in the code of it. Is there a stage I'm missing somewhere?

Looked around for inspiration, arrived at this:

https://docs.oracle.com/javase/tutorial/2d/advanced/user.html

Unfortunately this limits application - it handles event clicks well, but for using hover events, the MouseListener being applied to the parent container means that it only tracks enter / exit to that container. There's no way to get it to work on enter / exit for a Shape within that container.

Hexagon (abstract class):

import java.awt.Polygon;
import java.util.ArrayList;

import spare.Theme;

public abstract class Hexagon extends Polygon {

    private static final long serialVersionUID = 1L;

    public enum HEX_TYPE { ALIVE, INFECTED, DEAD };
    public enum SELECTION_TYPE { SELECTED, GROUPED, NONE };

    private int hexEdgeWidth = 20;
    private int hexBorderWidth = 3;
    private ArrayList<Hexagon> children;

    private Theme theme;

    private boolean hover = false;
    private boolean selected = false;

    public ArrayList<Hexagon> getChildren() {
        return children;
    }

    public int getHexEdgeWidth() {
        return hexEdgeWidth;
    }

    public void setHexEdgeWidth(int hexEdgeWidth) {
        this.hexEdgeWidth = hexEdgeWidth;
    }

    public int getHexBorderWidth() {
        return hexBorderWidth;
    }

    public void setHexBorderWidth(int hexBorderWidth) {
        this.hexBorderWidth = hexBorderWidth;
    }

    public void setChildren(ArrayList<Hexagon> children) {
        this.children = children;
    }

    public Theme getTheme() {
        return theme;
    }

    public void setTheme(Theme theme) {
        this.theme = theme;
    }

    public boolean isHover() {
        return hover;
    }

    public void setHover(boolean hover) {
        this.hover = hover;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

}

BasicHexagon (concrete subclass):

import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import spare.Theme;

public class BasicHexagon extends Hexagon implements MouseListener {

    private static final long serialVersionUID = 1L;

    public BasicHexagon() {
        setChildren(null);
        int n = 6;
        int size = getHexEdgeWidth();
        for (int i = 0; i < n; i++) {
            // x + radius * cosine of angle * iteration in radians
            // y + radius * sine of angle * iteration in radians
            addPoint(
                    (int) (size * Math.cos(Math.toRadians(360 / n * i))),
                    (int) (size * Math.sin(Math.toRadians(360 / n * i))));
        }

        setTheme(new Theme(
                new Color(255, 255, 255, 180),      // primary
                new Color(255, 255, 255, 255),      // primary hover
                new Color(255, 0, 0, 180),          // secondary
                new Color(255, 0, 0, 255),          // secondary hover
                new Color(255, 255, 100, 255),      // border
                new Color(255, 255, 100, 255)));    // text
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println("clicked");  
        BasicHexagon bH = (BasicHexagon) e.getSource();
        if(bH.isSelected()) {
            bH.setSelected(false);
        } else {
            bH.setSelected(true);
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        BasicHexagon bH = (BasicHexagon) e.getSource();
        bH.setHover(true);
        System.out.println("in the zone");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        BasicHexagon bH = (BasicHexagon) e.getSource();
        bH.setHover(false);
        System.out.println("fleeing");
    }

}

paintComponent for my custom JPanel:

@Override
public void paintComponent(Graphics g) {
    Toolkit.getDefaultToolkit().sync();
    super.paintComponent(g);
    switch (getGameState()) {
    case IN_GAME:
        doIngameDrawing(g);
        break;
    case END_GAME:
        break;
    case PAUSED:
        break;
    case PRE_GAME:
        break;
    default:
        break;
    }
}

private void doIngameDrawing(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    for(Hexagon h : getTiles()) {
        h.translate(getPreferredSize().width / 2, getPreferredSize().height / 2);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                RenderingHints.VALUE_ANTIALIAS_ON);
        if(h.isHover() && h.isSelected()) {
            g2d.setColor(h.getTheme().getSecondaryHover());
            g2d.fillPolygon(h);
        } else if(h.isHover()) {
            g2d.setColor(h.getTheme().getPrimaryHover());
            g2d.fillPolygon(h);
        } else if(h.isSelected()) {
            g2d.setColor(h.getTheme().getSecondary());
            g2d.fillPolygon(h);
        } else {
            // draw the normal colours;
            g2d.setColor(h.getTheme().getPrimary());
            g2d.fillPolygon(h);
        }

        g2d.setStroke(new BasicStroke(h.getHexBorderWidth()));
        g2d.setColor(h.getTheme().getBorder());
        g2d.drawPolygon(h);         
        if(h.getChildren() != null) {
            // child rendering goes here
        }
    }

    g2d.dispose();
}

Upvotes: 1

Views: 225

Answers (1)

Cinnam
Cinnam

Reputation: 1922

You will have to use mouseMoved() for that. Then you can check which shape is currently under the mouse cursor and react accordingly. You could do something like this:

 // This is in your custom JPanel, which for simplicity is also its own MouseListener.
 // Assuming getHexagons() returns Hexagon[].
 // Assuming Hexagon implements MouseListener.

 private Hexagon lastHovered;

 public void mouseMoved(MouseEvent e) {
     Hexagon current = null;
     boolean changed = false;
     for (Hexagon hex : getHexagons()) {
         if (hex.contains(e.getX(), e.getY())) {
             current = hex;
             break;
         }
     }
     if (lastHovered != current) {
         changed = true;
         if (lastHovered != null) {
             lastHovered.mouseExited(e);
         }
         if (current != null) {
             current.mouseEntered(e);
         }
     }
     lastHovered = current;
     if (changed) {
         repaint();
     }
 }

Since contains() is a method of Shape, it works for any shape. Since your shapes implement MouseListener, you implement the enter/exit functionality and pass mouse events to them.

Upvotes: 1

Related Questions