Reputation: 314
I've attempted to integrate http://java-swing-tips.blogspot.co.uk/2008/06/mouse-drag-auto-scrolling.html into my program, but it seems to misbehave and I'm not sure what's happening.
The gameGrid label is actually a separate class with click events, which is why I have the mouse listeners attempting to call the parent mouseListeners (previously the grid mouseListeners were overriding the scrollable viewport mouseListeners and so it wouldn't drag), but the JPanel with some shapes shows the same behaviour.
package uk.co.mhayward.games.factions;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
public class WhyYouJump extends JFrame {
public static void main(String[] args) {
new WhyYouJump();
}
private static final long serialVersionUID = 3697837493691218641L;
private final JPanel gameGridPanel;
private final JScrollPane gridScrollPane;
private final JViewport gridScrollPaneViewport;
private final JTextArea jTextArea = new JTextArea();
public WhyYouJump() throws HeadlessException {
super();
gameGridPanel = new JPanel() {
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
Polygon shape1 = new Polygon();
Polygon shape2 = new Polygon();
Polygon shape3 = new Polygon();
for (int i = 0; i < 6; i++) {
shape1.addPoint((int) (200 + 50 * Math.cos(i * 2 * Math.PI / 6)),
(int) (200 + 50 * Math.sin(i * 2 * Math.PI / 6)));
shape2.addPoint((int) (400 + 50 * Math.cos(i * 2 * Math.PI / 6)),
(int) (200 + 50 * Math.sin(i * 2 * Math.PI / 6)));
shape3.addPoint((int) (100 + 50 * Math.cos(i * 2 * Math.PI / 6)),
(int) (100 + 50 * Math.sin(i * 2 * Math.PI / 6)));
}
g2d.setStroke(new BasicStroke(3));
g2d.setPaint(Color.WHITE);
g2d.fill(shape1);
g2d.fill(shape2);
g2d.fill(shape3);
g2d.setPaint(Color.BLACK);
g2d.draw(shape1);
g2d.draw(shape2);
g2d.draw(shape3);
}
};
gameGridPanel.setPreferredSize(new Dimension(1440, 900));
gameGridPanel.setSize(new Dimension(1440, 900));
gridScrollPane = new JScrollPane(gameGridPanel);
gameGridPanel.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
for (MouseListener l : gameGridPanel.getParent().getMouseListeners()) {
e.setSource(gridScrollPaneViewport);
l.mousePressed(e);
}
jTextArea.append(e.getPoint().toString() + "\n");
}
@Override
public void mouseReleased(MouseEvent e) {
for (MouseListener l : gameGridPanel.getParent().getMouseListeners()) {
e.setSource(gridScrollPaneViewport);
l.mouseReleased(e);
}
}
@Override
public void mouseExited(MouseEvent e) {
for (MouseListener l : gameGridPanel.getParent().getMouseListeners()) {
e.setSource(gridScrollPaneViewport);
l.mouseExited(e);
}
}
});
gameGridPanel.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
for (MouseMotionListener l : gameGridPanel.getParent().getMouseMotionListeners()) {
e.setSource(gridScrollPaneViewport);
l.mouseDragged(e);
}
}
});
gameGridPanel.addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
for (HierarchyListener l : gameGridPanel.getParent().getHierarchyListeners()) {
e.setSource(gridScrollPaneViewport);
l.hierarchyChanged(e);
}
}
});
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gridScrollPane.setPreferredSize(new Dimension(500, 300));
gridScrollPane.getVerticalScrollBar().setUnitIncrement(16);
ViewportDragScrollListener l = new ViewportDragScrollListener(gameGridPanel, false);
gridScrollPaneViewport = gridScrollPane.getViewport();
gridScrollPaneViewport.addMouseMotionListener(l);
gridScrollPaneViewport.addMouseListener(l);
gridScrollPaneViewport.addHierarchyListener(l);
this.add("Center", gridScrollPane);
JScrollPane textScrollPane = new JScrollPane(jTextArea);
textScrollPane.setPreferredSize(new Dimension(140, 180));
this.add("South", textScrollPane);
this.pack();
this.setLocation(100, 100);
this.setVisible(true);
}
class ViewportDragScrollListener extends MouseAdapter implements HierarchyListener {
private static final int SPEED = 4;
private static final int DELAY = 10;
private final Cursor dc;
private final Cursor hc = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
private final javax.swing.Timer scroller;
private final JComponent label;
private final Point startPt = new Point();
private final Point move = new Point();
private boolean autoScroll = false;
public ViewportDragScrollListener(JComponent comp, boolean autoScroll) {
this.label = comp;
this.autoScroll = autoScroll;
this.dc = comp.getCursor();
this.scroller = new javax.swing.Timer(DELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
JViewport vport = (JViewport) label.getParent();
Point vp = vport.getViewPosition();
vp.translate(move.x, move.y);
label.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
}
});
}
public void hierarchyChanged(HierarchyEvent e) {
JComponent c = (JComponent) e.getSource();
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0 && !c.isDisplayable() && autoScroll) {
scroller.stop();
}
}
@Override
public void mouseDragged(MouseEvent e) {
JViewport vport = (JViewport) e.getSource();
Point pt = e.getPoint();
int dx = startPt.x - pt.x;
int dy = startPt.y - pt.y;
Point vp = vport.getViewPosition();
vp.translate(dx, dy);
label.scrollRectToVisible(new Rectangle(vp, vport.getSize()));
move.setLocation(SPEED * dx, SPEED * dy);
startPt.setLocation(pt);
}
@Override
public void mousePressed(MouseEvent e) {
((JComponent) e.getSource()).setCursor(hc); //label.setCursor(hc);
startPt.setLocation(e.getPoint());
move.setLocation(0, 0);
if (autoScroll) {
scroller.stop();
}
}
@Override
public void mouseReleased(MouseEvent e) {
((JComponent) e.getSource()).setCursor(dc); //label.setCursor(dc);
if (autoScroll) {
scroller.start();
}
}
@Override
public void mouseExited(MouseEvent e) {
((JComponent) e.getSource()).setCursor(dc); //label.setCursor(dc);
move.setLocation(0, 0);
if (autoScroll) {
scroller.stop();
}
}
}
}
Upvotes: 0
Views: 1229
Reputation: 9808
One way, using SwingUtilities.convertMouseEvent(gamePanel,mouseEvent,viewport): (Edit: As Guillaume Polet already says)
MouseAdapter convertMouseEventListener = new MouseAdapter() {
private void dispatchEvent(MouseEvent e) {
JComponent c = (JComponent)e.getComponent();
JComponent p = (JComponent)e.getComponent().getParent();
p.dispatchEvent(SwingUtilities.convertMouseEvent(c,e,p));
}
@Override public void mouseDragged(MouseEvent e) { dispatchEvent(e); }
@Override public void mouseClicked(MouseEvent e) { dispatchEvent(e); }
@Override public void mouseEntered(MouseEvent e) { dispatchEvent(e); }
@Override public void mouseExited(MouseEvent e) { dispatchEvent(e); }
@Override public void mousePressed(MouseEvent e) {
jTextArea.append(e.getPoint().toString() + "\n");
dispatchEvent(e);
}
@Override public void mouseReleased(MouseEvent e) { dispatchEvent(e); }
};
gameGridPanel.addMouseMotionListener(convertMouseEventListener);
gameGridPanel.addMouseListener(convertMouseEventListener);
Another, If gameGridPanel has its own MouseListeners, I sugest to use ComponentDragScrollListener instead of ViewportDragScrollListener:
ComponentDragScrollListener l = new ComponentDragScrollListener(gameGridPanel);
gameGridPanel.addMouseMotionListener(l);
gameGridPanel.addMouseListener(l);
gameGridPanel.addHierarchyListener(l);
Upvotes: 2
Reputation: 47608
I am not sure what this is precisely doing but I can spot at least one thing: mousePressed
mouseReleased
and mouseExited
are never invoked in your class ViewportDragScrollListener
because you don't forward the events. After looking at the article, they don't use such technique and I think this might also be one part of your problems.
Consider adding the mouse listeners directly on the correct components instead of forwarding events like you do.
EDIT:
When you forward the event, you modify the source, but the point is still in the original component coordinate. Consider creating a new event instead with the following code:
e = SwingUtilities.convertMouseEvent((Component) e.getSource(), e, gridScrollPaneViewport);
Upvotes: 2