Reputation: 3846
I was trying to extend the java ScrollDemo2 to report if a shape on the canvas had been clicked. I started with a simple rectangle believing that it should be no problem to simply loop over rectangle in the canvas checking if the click point was contained within them. But then something peculiar happened, the contains methods only seems to care if the point is in a rectangle anchored at (0,0), and doesn't seem to care that my component is at y=20. Therefore i get a hit if I click withing the Jpanel from x:[0,20] y:[0,20] when I should only get a hit if I click on x:[0,20] y[20,40]. Is this a bug, or am I doing something incorrectly?
public class CachedDrawableComponent extends JComponent
{
//this will do more later
CachedDrawableComponent(Rectangle bounds)
{
this.setBounds(bounds);
}
protected void paintComponent(Graphics g)
{
g.setColor(Color.magenta);
Rectangle r = this.getBounds();
g.fillRect(r.x, r.y, r.width, r.height);
}
}
public class ScrollDemo2 extends JPanel
implements MouseListener {
private Dimension area; //indicates area taken up by graphics
private Vector<Rectangle> circles; //coordinates used to draw graphics
private Vector<CachedDrawableComponent> otherDrawables;
private JPanel drawingPane;
private final Color colors[] = {
Color.red, Color.blue, Color.green, Color.orange,
Color.cyan, Color.magenta, Color.darkGray, Color.yellow};
private final int color_n = colors.length;
public ScrollDemo2() {
super(new BorderLayout());
area = new Dimension(0,0);
circles = new Vector<Rectangle>();
this.otherDrawables = new Vector<CachedDrawableComponent>();
//Set up the instructions.
JLabel instructionsLeft = new JLabel(
"Click left mouse button to place a circle.");
JLabel instructionsRight = new JLabel(
"Click right mouse button to clear drawing area.");
JPanel instructionPanel = new JPanel(new GridLayout(0,1));
instructionPanel.setFocusable(true);
instructionPanel.add(instructionsLeft);
instructionPanel.add(instructionsRight);
//Set up the drawing area.
drawingPane = new DrawingPane();
drawingPane.setBackground(Color.white);
drawingPane.addMouseListener(this);
TestRect t = new TestRect(new Rectangle(0,20,20,20));
this.otherDrawables.add(t);
//Put the drawing area in a scroll pane.
JScrollPane scroller = new JScrollPane(drawingPane);
scroller.setPreferredSize(new Dimension(200,200));
//Lay out this demo.
add(instructionPanel, BorderLayout.PAGE_START);
add(scroller, BorderLayout.CENTER);
}
/** The component inside the scroll pane. */
public class DrawingPane extends JPanel {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Rectangle rect;
for (int i = 0; i < circles.size(); i++) {
rect = circles.elementAt(i);
g.setColor(colors[(i % color_n)]);
g.fillOval(rect.x, rect.y, rect.width, rect.height);
}
for (int i = 0; i < otherDrawables.size(); i++) {
CachedDrawableComponent drawMe = otherDrawables.elementAt(i);;
g.setColor(colors[(i % color_n)]);
drawMe.paint(g);
}
}
}
//Handle mouse events.
public void mouseReleased(MouseEvent e) {
final int W = 100;
final int H = 100;
boolean changed = false;
if (SwingUtilities.isRightMouseButton(e)) {
//This will clear the graphic objects.
circles.removeAllElements();
area.width=0;
area.height=0;
changed = true;
} else {
int x = e.getX() - W/2;
int y = e.getY() - H/2;
if (x < 0) x = 0;
if (y < 0) y = 0;
Rectangle rect = new Rectangle(x, y, W, H);
circles.addElement(rect);
drawingPane.scrollRectToVisible(rect);
int this_width = (x + W + 2);
if (this_width > area.width) {
area.width = this_width; changed=true;
}
int this_height = (y + H + 2);
if (this_height > area.height) {
area.height = this_height; changed=true;
}
}
if (changed) {
//Update client's preferred size because
//the area taken up by the graphics has
//gotten larger or smaller (if cleared).
drawingPane.setPreferredSize(area);
//Let the scroll pane know to update itself
//and its scrollbars.
drawingPane.revalidate();
}
drawingPane.repaint();
}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mousePressed(MouseEvent e)
{
System.out.println("Did press:"+e.getPoint());
System.out.println(otherDrawables.get(0).getBounds());
if(otherDrawables.get(0).contains(e.getPoint()))
{
System.out.println("Did Hit");
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("ScrollDemo2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new ScrollDemo2();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Output:
Did press:java.awt.Point[x=8,y=7]
java.awt.Rectangle[x=0,y=20,width=20,height=20]
Did Hit
Did press:java.awt.Point[x=14,y=89]
java.awt.Rectangle[x=0,y=20,width=20,height=20]
Upvotes: 0
Views: 742
Reputation: 324147
Check out Playing With Shapes. It may give you some ideas for a slightly different approach.
For example by creating Shapes you don't need to keep track of Rectangle and others since all shapes will be treated the same.
You can also use the Shapes as components is you wish.
Upvotes: 1
Reputation: 51535
The face problem is that you are mixing coordinate systems:
From the api doc of contains:
Checks whether this component "contains" the specified point, where
x
andy
are defined to be relative to the coordinate system of this component.
It's unusual (don't do without a reeeaal good reason :-) to have those components without actually adding them to the drawingPane.
Upvotes: 2