package nodebox.handle;
import nodebox.graphics.GraphicsContext;
import nodebox.graphics.Path;
import nodebox.graphics.Point;
import nodebox.graphics.Rect;
public class FourPointHandle extends AbstractHandle {
private enum DragState {
NONE, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, CENTER
}
private String positionName, widthName, heightName;
private DragState dragState = DragState.NONE;
private double px, py;
private double ocx, ocy, owidth, oheight;
public FourPointHandle() {
this("position", "width", "height");
}
public FourPointHandle(String positionName, String widthName, String heightName) {
this.positionName = positionName;
this.widthName = widthName;
this.heightName = heightName;
update();
}
@Override
public void update() {
if (hasInput("shape"))
setVisible(isConnected("shape"));
}
public void draw(GraphicsContext ctx) {
Point cp = (Point) getValue(positionName);
if (cp == null) {
return;
}
double cx = cp.x;
double cy = cp.y;
double width = (Double) getValue(widthName);
double height = (Double) getValue(heightName);
double left = cx - width / 2;
double right = cx + width / 2;
double top = cy - height / 2;
double bottom = cy + height / 2;
Path cornerPath = new Path();
cornerPath.setFillColor(HANDLE_COLOR);
cornerPath.setStrokeWidth(0);
drawDot(cornerPath, left, top);
drawDot(cornerPath, right, top);
drawDot(cornerPath, right, bottom);
drawDot(cornerPath, left, bottom);
drawDot(cornerPath, cx, cy);
ctx.draw(cornerPath);
Path strokePath = new Path();
strokePath.setFillColor(null);
strokePath.setStrokeColor(HANDLE_COLOR);
strokePath.rect(cx, cy, width, height);
ctx.draw(strokePath);
}
@Override
public boolean mousePressed(Point pt) {
px = pt.getX();
py = pt.getY();
Point op = (Point) getValue(positionName);
ocx = op.x;
ocy = op.y;
owidth = (Double) getValue(widthName);
oheight = (Double) getValue(heightName);
double left = ocx - owidth / 2;
double right = ocx + owidth / 2;
double top = ocy - oheight / 2;
double bottom = ocy + oheight / 2;
Rect topLeft = createHitRectangle(left, top);
Rect topRight = createHitRectangle(right, top);
Rect bottomLeft = createHitRectangle(left, bottom);
Rect bottomRight = createHitRectangle(right, bottom);
Rect center = new Rect(left, top, owidth, oheight);
if (topLeft.contains(pt)) {
dragState = DragState.TOP_LEFT;
} else if (topRight.contains(pt)) {
dragState = DragState.TOP_RIGHT;
} else if (bottomLeft.contains(pt)) {
dragState = DragState.BOTTOM_LEFT;
} else if (bottomRight.contains(pt)) {
dragState = DragState.BOTTOM_RIGHT;
} else if (center.contains(pt)) {
dragState = DragState.CENTER;
} else {
dragState = DragState.NONE;
return false;
}
return true;
}
@Override
public boolean mouseDragged(Point pt) {
if (dragState == DragState.NONE) return false;
double x = pt.getX();
double y = pt.getY();
double dx = x - px;
double dy = y - py;
// The delta value is multiplied by 2 to create the float effect of moving
// the top left corner down and the bottom left corner up (in the case of
// the top left handle).
if (dx == 0 && dy == 0) return false;
startCombiningEdits("Set Value");
switch (dragState) {
case TOP_LEFT:
silentSet(widthName, owidth - dx * 2);
silentSet(heightName, oheight - dy * 2);
break;
case TOP_RIGHT:
silentSet(heightName, oheight - dy * 2);
silentSet(widthName, owidth + dx * 2);
break;
case BOTTOM_LEFT:
silentSet(widthName, owidth - dx * 2);
silentSet(heightName, oheight + dy * 2);
break;
case BOTTOM_RIGHT:
silentSet(widthName, owidth + dx * 2);
silentSet(heightName, oheight + dy * 2);
break;
case CENTER:
silentSet(positionName, new Point(ocx + dx, ocy + dy));
}
return true;
}
@Override
public boolean mouseReleased(Point pt) {
if (dragState == DragState.NONE) return false;
dragState = DragState.NONE;
stopCombiningEdits();
return true;
}
public boolean hasDragState() {
return dragState != DragState.NONE;
}
}