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();
}
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.