-
-
Save GeoffCapper/54fb16ac548f2c13b99f273b79293517 to your computer and use it in GitHub Desktop.
DragResizer can be used to add mouse listeners to a Region and make it resizable by the user by clicking and dragging the border in the same way as a window. Height and width resizing from all cardinal points is possible, along with the option of dragging the region, and constraining it within the bounds of the parent.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.event.EventHandler; | |
import javafx.scene.Cursor; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.layout.Region; | |
/** | |
* {@link DragResizer} can be used to add mouse listeners to a {@link Region} | |
* and make it moveable and resizable by the user by clicking and dragging the | |
* border in the same way as a window, or clicking within the window to move it | |
* around. | |
* <p> | |
* Height and width resizing is implemented, from the sides and corners. | |
* Dragging of the region is also optionally available, and the movement and | |
* resizing can be constrained within the bounds of the parent. | |
* <p> | |
* Usage: | |
* <pre>DragResizer.makeResizable(myAnchorPane);</pre> makes the region | |
* resizable, but not moveable, and not constrained within the parent. | |
* <p> | |
* Usage: | |
* <pre>DragResizer.makeResizable(myAnchorPane, true, true);</pre> makes the | |
* region resizable and moveable, but only within the bounds of the parent. | |
* <p> | |
* Builds on the modifications of Cannibalsticky to the original version by | |
* AndyTill. | |
* <p> | |
* | |
* @author Geoff Capper | |
*/ | |
public class DragResizer { | |
/** | |
* Enum containing the zones that we can drag around. | |
*/ | |
enum Zone { | |
NONE, N, NE, E, SE, S, SW, W, NW, C | |
} | |
/** | |
* The margin around the control that a user can click in to start resizing | |
* the region. | |
*/ | |
private static final int RESIZE_MARGIN = 5; | |
/** | |
* How small can we go? | |
*/ | |
private static final int MIN_SIZE = 10; | |
private final Region region; | |
private double y; | |
private double x; | |
private boolean initMinHeight; | |
private boolean initMinWidth; | |
private Zone zone; | |
private boolean dragging; | |
/** | |
* Whether the sizing and movement of the region is constrained within the | |
* bounds of the parent. | |
*/ | |
private final boolean constrainToParent; | |
/** | |
* Whether dragging of the region is allowed. | |
*/ | |
private final boolean allowMove; | |
private DragResizer(Region aRegion, boolean allowMove, boolean constrainToParent) { | |
region = aRegion; | |
this.constrainToParent = constrainToParent; | |
this.allowMove = allowMove; | |
} | |
/** | |
* Makes the region resizable, but not moveable or constrained in any way. | |
* | |
* @param region | |
*/ | |
public static void makeResizable(Region region) { | |
DragResizer.makeResizable(region, false, false); | |
} | |
/** | |
* Makes the region resizable, and optionally moveable, and constrained | |
* within the bounds of the parent. | |
* | |
* @param region | |
* @param allowMove Allow a click in the centre of the region to start | |
* dragging it around. | |
* @param constrainToParent Prevent movement and/or resizing outside the | |
* parent. | |
*/ | |
public static void makeResizable(Region region, boolean allowMove, boolean constrainToParent) { | |
final DragResizer resizer = new DragResizer(region, allowMove, constrainToParent); | |
region.setOnMousePressed(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
resizer.mousePressed(event); | |
} | |
}); | |
region.setOnMouseDragged(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
resizer.mouseDragged(event); | |
} | |
}); | |
region.setOnMouseMoved(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
resizer.mouseOver(event); | |
} | |
}); | |
region.setOnMouseReleased(new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(MouseEvent event) { | |
resizer.mouseReleased(event); | |
} | |
}); | |
} | |
protected void mouseReleased(MouseEvent event) { | |
dragging = false; | |
region.setCursor(Cursor.DEFAULT); | |
} | |
protected void mouseOver(MouseEvent event) { | |
if (isInDraggableZone(event) || dragging) { | |
switch (zone) { | |
case N: { | |
region.setCursor(Cursor.N_RESIZE); | |
break; | |
} | |
case NE: { | |
region.setCursor(Cursor.NE_RESIZE); | |
break; | |
} | |
case E: { | |
region.setCursor(Cursor.E_RESIZE); | |
break; | |
} | |
case SE: { | |
region.setCursor(Cursor.SE_RESIZE); | |
break; | |
} | |
case S: { | |
region.setCursor(Cursor.S_RESIZE); | |
break; | |
} | |
case SW: { | |
region.setCursor(Cursor.SW_RESIZE); | |
break; | |
} | |
case W: { | |
region.setCursor(Cursor.W_RESIZE); | |
break; | |
} | |
case NW: { | |
region.setCursor(Cursor.NW_RESIZE); | |
break; | |
} | |
case C: { | |
region.setCursor(Cursor.MOVE); | |
break; | |
} | |
} | |
} else { | |
region.setCursor(Cursor.DEFAULT); | |
} | |
} | |
protected boolean isInDraggableZone(MouseEvent event) { | |
zone = Zone.NONE; | |
if ((event.getY() < RESIZE_MARGIN) && (event.getX() < RESIZE_MARGIN)) { | |
zone = Zone.NW; | |
} else if ((event.getY() < RESIZE_MARGIN) && (event.getX() > (region.getWidth() - RESIZE_MARGIN))) { | |
zone = Zone.NE; | |
} else if ((event.getY() > (region.getHeight() - RESIZE_MARGIN)) && (event.getX() > (region.getWidth() - RESIZE_MARGIN))) { | |
zone = Zone.SE; | |
} else if ((event.getY() > (region.getHeight() - RESIZE_MARGIN)) && (event.getX() < RESIZE_MARGIN)) { | |
zone = Zone.SW; | |
} else if (event.getY() < RESIZE_MARGIN) { | |
zone = Zone.N; | |
} else if (event.getX() < RESIZE_MARGIN) { | |
zone = Zone.W; | |
} else if (event.getY() > (region.getHeight() - RESIZE_MARGIN)) { | |
zone = Zone.S; | |
} else if (event.getX() > (region.getWidth() - RESIZE_MARGIN)) { | |
zone = Zone.E; | |
} else if (allowMove) { | |
zone = Zone.C; | |
} | |
return !Zone.NONE.equals(zone); | |
} | |
protected void mouseDragged(MouseEvent event) { | |
if (!dragging) { | |
return; | |
} | |
double deltaY = event.getSceneY() - y; | |
double deltaX = event.getSceneX() - x; | |
double originY = region.getLayoutY(); | |
double originX = region.getLayoutX(); | |
double newHeight = region.getMinHeight(); | |
double newWidth = region.getMinWidth(); | |
switch (zone) { | |
case N: { | |
originY += deltaY; | |
newHeight -= deltaY; | |
break; | |
} | |
case NE: { | |
originY += deltaY; | |
newHeight -= deltaY; | |
newWidth += deltaX; | |
break; | |
} | |
case E: { | |
newWidth += deltaX; | |
break; | |
} | |
case SE: { | |
newHeight += deltaY; | |
newWidth += deltaX; | |
break; | |
} | |
case S: { | |
newHeight += deltaY; | |
break; | |
} | |
case SW: { | |
originX += deltaX; | |
newHeight += deltaY; | |
newWidth -= deltaX; | |
break; | |
} | |
case W: { | |
originX += deltaX; | |
newWidth -= deltaX; | |
break; | |
} | |
case NW: { | |
originY += deltaY; | |
originX += deltaX; | |
newWidth -= deltaX; | |
newHeight -= deltaY; | |
break; | |
} | |
case C: { | |
originY += deltaY; | |
originX += deltaX; | |
break; | |
} | |
} | |
if (constrainToParent) { | |
if (originX < 0) { | |
if (!Zone.C.equals(zone)) { | |
newWidth -= Math.abs(originX); | |
} | |
originX = 0; | |
} | |
if (originY < 0) { | |
if (!Zone.C.equals(zone)) { | |
newHeight -= Math.abs(originY); | |
} | |
originY = 0; | |
} | |
if (Zone.C.equals(zone)) { | |
if ((newHeight + originY) > region.getParent().getBoundsInLocal().getHeight()) { | |
originY = region.getParent().getBoundsInLocal().getHeight() - newHeight; | |
} | |
if ((newWidth + originX) > region.getParent().getBoundsInLocal().getWidth()) { | |
originX = region.getParent().getBoundsInLocal().getWidth() - newWidth; | |
} | |
} else { | |
if ((newHeight + originY) > region.getParent().getBoundsInLocal().getHeight()) { | |
newHeight = region.getParent().getBoundsInLocal().getHeight() - originY; | |
} | |
if ((newWidth + originX) > region.getParent().getBoundsInLocal().getWidth()) { | |
newWidth = region.getParent().getBoundsInLocal().getWidth() - originX; | |
} | |
} | |
} | |
if (newWidth < MIN_SIZE) { | |
newWidth = MIN_SIZE; | |
} | |
if (newHeight < MIN_SIZE) { | |
newHeight = MIN_SIZE; | |
} | |
if (!Zone.C.equals(zone)) { | |
// need to set Pref Height/Width otherwise they act as minima. | |
region.setMinHeight(newHeight); | |
region.setPrefHeight(newHeight); | |
region.setMinWidth(newWidth); | |
region.setPrefWidth(newWidth); | |
} | |
region.relocate(originX, originY); | |
y = event.getSceneY(); | |
x = event.getSceneX(); | |
} | |
protected void mousePressed(MouseEvent event) { | |
// ignore clicks outside of the draggable margin | |
if (!isInDraggableZone(event)) { | |
return; | |
} | |
dragging = true; | |
// make sure that the minimum height is set to the current height once, | |
// setting a min height that is smaller than the current height will | |
// have no effect | |
if (!initMinHeight) { | |
region.setMinHeight(region.getHeight()); | |
initMinHeight = true; | |
} | |
y = event.getSceneY(); | |
if (!initMinWidth) { | |
region.setMinWidth(region.getWidth()); | |
initMinWidth = true; | |
} | |
x = event.getSceneX(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment