tags:

views:

2684

answers:

1

I'm resizing a JPanel inside of a JScrollPane, and I want to make sure that the point on the JPanel where my mouse is currently located retains its position with respect to the JScrollPane after the resize (like Google maps does when you zoom in/out).

I find the mouse position on the JPanel, which lets me deal with the viewport being at various positions. I multiply it by the zoom factor so I know where the point will be after scaling. I then subtract the position of the mouse on the ScrollPane so that I know where the point was with respect to the viewable area. I'm doing something wrong however, and I just can't see what.

Example Code:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;


public class Test 
{
    public static void main(String[] in)
    {
     javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
       new Test();
      }
     });
    }

    public Test()
    {
     final JFrame frame = new JFrame();
     final ScalablePanel child = new ScalablePanel();

     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setLayout(new BorderLayout());
     frame.add(child, BorderLayout.CENTER);
     frame.pack();
     frame.setLocationRelativeTo(null);
     frame.setVisible(true);
    }
}

class ScalablePanel
extends JScrollPane
implements MouseWheelListener
{
    final double ZOOM_IN_FACTOR = 1.1;
    final double ZOOM_OUT_FACTOR = 0.9;
    final JPanel zoomPanel = new JPanel();

    public ScalablePanel()
    {
     final javax.swing.JLabel marker = new javax.swing.JLabel("Testing the mouse position on zoom");
     marker.setHorizontalAlignment(javax.swing.JLabel.CENTER);

     zoomPanel.setLayout(new BorderLayout());
     zoomPanel.add(marker,BorderLayout.CENTER);

     getViewport().setView(zoomPanel);
     setPreferredSize(new Dimension(300,300));
     addMouseWheelListener(this);
    }

    public void mouseWheelMoved(final MouseWheelEvent e) 
    {
     if (e.isControlDown())
     {
      if (e.getWheelRotation() < 0)
       zoomIn(e);
      else
       zoomOut(e);
      e.consume();
     }
    }

    public void zoomIn(final MouseWheelEvent e)
    {
     // Get the mouse position with respect to the zoomPanel
     final Point pointOnZoomPanel = SwingUtilities.convertPoint(
       e.getComponent(), e.getPoint(), zoomPanel);

     // Resize panel
     final Dimension currSize = zoomPanel.getSize();
     zoomPanel.setPreferredSize(
       new Dimension(
         (int)(currSize.width * ZOOM_IN_FACTOR),
         (int)(currSize.height * ZOOM_IN_FACTOR) ));

     // Find out where our point on the zoom panel is now that we've resized it
     final Point newViewPos = new Point();
     newViewPos.x = (int)(ZOOM_IN_FACTOR * pointOnZoomPanel.x - e.getPoint().x);
     newViewPos.y = (int)(ZOOM_IN_FACTOR * pointOnZoomPanel.y - e.getPoint().y);
     // Move the viewport to the new position to keep the area our mouse was in the same spot
     getViewport().setViewPosition(newViewPos);

     zoomPanel.revalidate();
    }

    public void zoomOut(final MouseWheelEvent e)
    {
     // Get the mouse position with respect to the zoomPanel
     final Point pointOnZoomPanel = SwingUtilities.convertPoint(
       e.getComponent(), e.getPoint(), zoomPanel);

     // Resize panel
     final Dimension currSize = zoomPanel.getSize();
     zoomPanel.setPreferredSize(
       new Dimension(
         (int)(currSize.width * ZOOM_OUT_FACTOR),
         (int)(currSize.height * ZOOM_OUT_FACTOR) ));

     // Find out where our point on the zoom panel is now that we've resized it
     final Point newViewPos = new Point();
     newViewPos.x = (int)(ZOOM_OUT_FACTOR * pointOnZoomPanel.x - e.getPoint().x);
     newViewPos.y = (int)(ZOOM_OUT_FACTOR * pointOnZoomPanel.y - e.getPoint().y);
     // Move the viewport to the new position to keep the area our mouse was in the same spot
     getViewport().setViewPosition(newViewPos);

     zoomPanel.revalidate();
    }
}
+2  A: 

Try instead finding the position of the mouse as a percentage of the current size, then applying that to the new size:

newViewPos.x = (int)((ZOOM_IN_FACTOR * currSize.width) * (e.getPoint().x/(double)currSize.width));

as an example. In this way, you are looking at the mouse position relative to the scroll pane, and preserving that relationship on the zoomPanel.

Zoe Gagnon