blob: 6a631957e6f9f36056bb68f34a07e4512e410cde [file] [log] [blame]
/*
* Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.oomph.ui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
/**
* @author Eike Stepper
*/
public abstract class MouseHandler
{
private final MouseTrackListener mouseTrackListener = new MouseTrackListener()
{
public void mouseEnter(MouseEvent e)
{
// Do nothing.
}
public void mouseHover(MouseEvent e)
{
// Do nothing.
}
public void mouseExit(MouseEvent e)
{
onMouseMove(e.widget, Integer.MIN_VALUE, Integer.MIN_VALUE);
}
};
private final MouseMoveListener mouseMoveListener = new MouseMoveListener()
{
public void mouseMove(MouseEvent e)
{
onMouseMove(e.widget, e.x, e.y);
}
};
private final MouseListener mouseListener = new MouseListener()
{
public void mouseDoubleClick(MouseEvent e)
{
// Do nothing.
}
public void mouseDown(MouseEvent e)
{
if (e.button == 1)
{
start = new Point(e.x, e.y);
}
}
public void mouseUp(MouseEvent e)
{
if (start != null)
{
if (e.widget instanceof Control)
{
Control control = (Control)e.widget;
afterEnd(control, start, e.x, e.y);
}
start = null;
}
}
};
private Point start;
public MouseHandler()
{
}
public void hookControl(Control control)
{
if (shouldHookControl(control))
{
control.addMouseTrackListener(mouseTrackListener);
control.addMouseMoveListener(mouseMoveListener);
control.addMouseListener(mouseListener);
}
if (control instanceof Composite)
{
for (Control child : ((Composite)control).getChildren())
{
hookControl(child);
}
}
}
protected boolean shouldHookControl(Control control)
{
Class<? extends Control> c = control.getClass();
return c == Composite.class || c == StackComposite.class || control instanceof Shell || c == Label.class || c == Link.class;
}
protected void beforeStart(Control control, int x, int y)
{
}
protected void afterStart(Control control, Point start, int x, int y)
{
}
protected void afterEnd(Control control, Point start, int x, int y)
{
}
protected boolean moveShell(Control control, Point start, int x, int y)
{
Shell shell = control.getShell();
Point location = shell.getLocation();
location.x += x - start.x;
location.y += y - start.y;
shell.setLocation(location);
return true;
}
private void onMouseMove(Widget widget, int x, int y)
{
if (widget instanceof Control)
{
Control control = (Control)widget;
if (start == null)
{
beforeStart(control, x, y);
}
else
{
afterStart(control, start, x, y);
}
}
}
/**
* @author Eike Stepper
*/
public static class Move extends MouseHandler
{
public Move()
{
}
@Override
protected void afterStart(Control control, Point start, int x, int y)
{
moveShell(control, start, x, y);
}
}
/**
* @author Eike Stepper
*/
public static class MoveAndResize extends MouseHandler
{
private static final int AREA_THICKNESS = 5;
private static final int CORNER_SIZE = 25;
private static final int NORTH = 1 << 0;
private static final int SOUTH = 1 << 1;
private static final int EAST = 1 << 2;
private static final int WEST = 1 << 3;
private static final Cursor[] CURSORS = { null, null, null, null, null, null, null, null, null, null, null };
private final Rectangle[] areas = { null, null, null, null, null, null, null, null, null, null, null };
private final Control mainControl;
private BoundsUpdater boundsUpdater;
private Rectangle bounds;
private int index;
public MoveAndResize(Control control)
{
mainControl = control;
computeAreas(control);
hookControl(control);
}
public final int getIndex()
{
return index;
}
@Override
protected void beforeStart(Control control, int x, int y)
{
if (control == mainControl)
{
index = getIndex(control, x, y);
if (index >= 0)
{
control.setCursor(CURSORS[index]);
return;
}
}
resetCursor(control);
}
@Override
protected void afterStart(Control control, Point start, int x, int y)
{
if (control == mainControl)
{
if (index >= 0)
{
Rectangle currentBounds = control.getBounds();
Rectangle newBounds = new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
x -= start.x;
y -= start.y;
if ((index & NORTH) != 0)
{
y -= bounds.y - currentBounds.y;
newBounds.y += y;
newBounds.height -= y;
}
if ((index & SOUTH) != 0)
{
newBounds.height += y;
}
if ((index & WEST) != 0)
{
x -= bounds.x - currentBounds.x;
newBounds.x += x;
newBounds.width -= x;
}
if ((index & EAST) != 0)
{
newBounds.width += x;
}
// if (!newBounds.equals(currentBounds))
{
synchronized (this)
{
if (boundsUpdater == null)
{
boundsUpdater = new BoundsUpdater();
}
boundsUpdater.updateBounds(newBounds);
}
}
return;
}
}
moveShell(control, start, x, y);
computeAreas(mainControl);
}
@Override
protected void afterEnd(Control control, Point start, int x, int y)
{
if (control == mainControl)
{
if (index >= 0)
{
resetCursor(control);
computeAreas(control);
}
index = -1;
}
}
private void computeAreas(Control control)
{
bounds = control.getBounds();
areas[NORTH] = new Rectangle(bounds.x + CORNER_SIZE, bounds.y, bounds.width - 2 * CORNER_SIZE, AREA_THICKNESS);
areas[SOUTH] = new Rectangle(bounds.x + CORNER_SIZE, bounds.y + bounds.height - AREA_THICKNESS, bounds.width - 2 * CORNER_SIZE, AREA_THICKNESS);
areas[WEST] = new Rectangle(bounds.x, bounds.y + CORNER_SIZE, AREA_THICKNESS, bounds.height - 2 * CORNER_SIZE);
areas[EAST] = new Rectangle(bounds.x + bounds.width - AREA_THICKNESS, bounds.y + CORNER_SIZE, AREA_THICKNESS, bounds.height - 2 * CORNER_SIZE);
areas[NORTH | WEST] = new Rectangle(bounds.x, bounds.y, CORNER_SIZE, CORNER_SIZE);
areas[NORTH | EAST] = new Rectangle(bounds.x + bounds.width - CORNER_SIZE, bounds.y, CORNER_SIZE, CORNER_SIZE);
areas[SOUTH | WEST] = new Rectangle(bounds.x, bounds.y + bounds.height - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE);
areas[SOUTH | EAST] = new Rectangle(bounds.x + bounds.width - CORNER_SIZE, bounds.y + bounds.height - CORNER_SIZE, CORNER_SIZE, CORNER_SIZE);
}
private int getIndex(Control control, int x, int y)
{
x += bounds.x;
y += bounds.y;
for (int i = 0; i < areas.length; i++)
{
Rectangle area = areas[i];
if (area != null)
{
if (area.contains(x, y))
{
return i;
}
}
}
return -1;
}
private void resetCursor(Control control)
{
if (control.getCursor() != null)
{
control.setCursor(null);
}
}
public static String format(int index)
{
StringBuilder builder = new StringBuilder();
if ((index & NORTH) != 0)
{
builder.append(" NORTH");
}
if ((index & SOUTH) != 0)
{
builder.append(" SOUTH");
}
if ((index & WEST) != 0)
{
builder.append(" WEST");
}
if ((index & EAST) != 0)
{
builder.append(" EAST");
}
return builder.toString().trim();
}
static
{
Display display = UIUtil.getDisplay();
CURSORS[NORTH] = display.getSystemCursor(SWT.CURSOR_SIZEN);
CURSORS[SOUTH] = display.getSystemCursor(SWT.CURSOR_SIZES);
CURSORS[WEST] = display.getSystemCursor(SWT.CURSOR_SIZEW);
CURSORS[EAST] = display.getSystemCursor(SWT.CURSOR_SIZEE);
CURSORS[NORTH | WEST] = display.getSystemCursor(SWT.CURSOR_SIZENW);
CURSORS[NORTH | EAST] = display.getSystemCursor(SWT.CURSOR_SIZENE);
CURSORS[SOUTH | WEST] = display.getSystemCursor(SWT.CURSOR_SIZESW);
CURSORS[SOUTH | EAST] = display.getSystemCursor(SWT.CURSOR_SIZESE);
}
/**
* @author Eike Stepper
*/
private final class BoundsUpdater implements Runnable
{
private Rectangle newBounds;
public void updateBounds(Rectangle newBounds)
{
boolean schedule = this.newBounds == null;
this.newBounds = newBounds;
if (schedule)
{
mainControl.getDisplay().asyncExec(this);
}
}
public void run()
{
try
{
Rectangle bounds;
synchronized (MoveAndResize.this)
{
bounds = newBounds;
newBounds = null;
}
if (bounds != null)
{
mainControl.setBounds(bounds);
mainControl.getDisplay().asyncExec(this);
}
else
{
boundsUpdater = null;
}
}
catch (Exception ex)
{
boundsUpdater = null;
}
}
}
}
}