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