Simon
Simon

Reputation: 4192

SWT ScrolledComposite Hand Tool: Scroll Proportional To Mouse Movement

I would like to implement a "hand tool" with which the user can scroll/move something with the mouse. This feature is known from various programs such as Gimp. If the user presses the left mouse button, the tool should be activated. If the mouse is now moved, the underlying object should also be moved - proportional to the mouse movement. Hence, if the user presses the mouse and moves it by e.g. 10px, the underlying object should also be moved by 10px. I thought that it should work with my code below, but it does not work properly. The movement of the underlying ScrolledComposite is too fast.

The code below is ready-to-run, so you can directly play with it.

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class HandTool {

  public static boolean moveGraphActive = false;
  public static Point moveStartPos = new Point(0, 0);
  public static ScrolledComposite sc;

  public static void main(String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    sc = new ScrolledComposite(shell, SWT.V_SCROLL);

    Text text = new Text(sc, SWT.MULTI | SWT.READ_ONLY);
    String multiLineText = "";
    for (int i = 0; i < 30; i++) {
      multiLineText += "Hello World SWT " + i + System.lineSeparator();
    }
    text.setText(multiLineText);
    text.setEnabled(false);
    text.pack();
    sc.setContent(text);

    sc.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseDown(final MouseEvent e) {
        moveGraphActive = e.button == 1;
        if (moveGraphActive) {
          moveStartPos.x = e.x;
          moveStartPos.y = e.y;
        }
      }

      @Override
      public void mouseUp(final MouseEvent e) {
        moveGraphActive = false;
      }
    });

    sc.addMouseMoveListener(new MouseMoveListener() {
      @Override
      public void mouseMove(final MouseEvent e) {
        if (moveGraphActive) {
          // FIXME the movement of the canvas should be proportional
          // to the mouse
          // movement
          final int newX = sc.getOrigin().x + moveStartPos.x - e.x;
          final int newY = sc.getOrigin().y + moveStartPos.y - e.y;
          sc.setOrigin(newX, newY);
        }
      }
    });

    shell.setSize(500, 150);
    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    display.dispose();
  }
}

Upvotes: 1

Views: 317

Answers (1)

Baz
Baz

Reputation: 36894

You almost got it right, there is just a small mistake.

final int newX = sc.getOrigin().x + moveStartPos.x - e.x;
final int newY = sc.getOrigin().y + moveStartPos.y - e.y;

should be

final int newX = startOrigin.x + moveStartPos.x - e.x;
final int newY = startOrigin.y + moveStartPos.y - e.y;

where startOrigin is the origin of the ScrolledComposite when you started dragging. I've updated your code:

private static boolean           moveGraphActive = false;
private static Point             moveStartPos    = new Point(0, 0);
private static ScrolledComposite sc;
private static Point             startOrigin;

public static void main(String[] args)
{
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setLayout(new FillLayout());
    sc = new ScrolledComposite(shell, SWT.V_SCROLL);

    Text text = new Text(sc, SWT.MULTI | SWT.READ_ONLY);
    String multiLineText = "";
    for (int i = 0; i < 30; i++)
    {
        multiLineText += "Hello World SWT " + i + System.lineSeparator();
    }
    text.setText(multiLineText);
    text.setEnabled(false);
    text.pack();
    sc.setContent(text);

    sc.addMouseListener(new MouseAdapter()
    {
        @Override
        public void mouseDown(final MouseEvent e)
        {
            moveGraphActive = e.button == 1;
            if (moveGraphActive)
            {
                moveStartPos.x = e.x;
                moveStartPos.y = e.y;
                startOrigin = sc.getOrigin();
            }
        }

        @Override
        public void mouseUp(final MouseEvent e)
        {
            moveGraphActive = false;
            startOrigin = null;
        }
    });

    sc.addMouseMoveListener(new MouseMoveListener()
    {
        @Override
        public void mouseMove(final MouseEvent e)
        {
            if (moveGraphActive)
            {
                // FIXED
                final int newX = startOrigin.x + moveStartPos.x - e.x;
                final int newY = startOrigin.y + moveStartPos.y - e.y;
                sc.setOrigin(newX, newY);
            }
        }
    });

    shell.setSize(500, 150);
    shell.open();
    while (!shell.isDisposed())
    {
        if (!display.readAndDispatch())
            display.sleep();
    }
    display.dispose();
}

Upvotes: 2

Related Questions