/* CloudTrail Viewer, is a Java desktop application for reading AWS CloudTrail logs files. Copyright (C) 2017 Mark P. Haskins This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package io.haskins.java.cloudtrailviewer.utils; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.input.MouseEvent; /** * This class enables a widget to be dragged around, as well as providing a resize functionality. * * This class is based on work done by varren on GitHub : https://github.com/varren/JavaFX-Resizable-Draggable-Node * * Created by markhaskins on 23/01/2017. */ public class DragResizeWidget { public enum S { DEFAULT, DRAG, SE_RESIZE } private double clickX, clickY, nodeX, nodeY, nodeH, nodeW; private S state = S.DEFAULT; private final Node node; private final OnDragResizeEventListener listener; private static final int MARGIN = 8; private DragResizeWidget(Node theNode, OnDragResizeEventListener listener) { this.node = theNode; this.listener = listener; } public static void makeResizable(Node theNode, OnDragResizeEventListener listener) { final DragResizeWidget resizer = new DragResizeWidget(theNode, listener); theNode.setOnMousePressed(resizer::mousePressed); theNode.setOnMouseDragged(resizer::mouseDragged); theNode.setOnMouseMoved(resizer::mouseOver); theNode.setOnMouseReleased(resizer::mouseReleased); } private void mouseReleased(MouseEvent event) { node.setCursor(Cursor.DEFAULT); state = S.DEFAULT; } private void mouseOver(MouseEvent event) { S state = currentMouseState(event); Cursor cursor = getCursorForState(state); node.setCursor(cursor); } private S currentMouseState(MouseEvent event) { S state = S.DEFAULT; boolean right = isRightResizeZone(event); boolean bottom = isBottomResizeZone(event); if (right && bottom) { state = S.SE_RESIZE; } else if (isInDragZone(event)) { state = S.DRAG; } return state; } private static Cursor getCursorForState(S state) { switch (state) { case SE_RESIZE: return Cursor.SE_RESIZE; case DRAG: return Cursor.MOVE; default: return Cursor.DEFAULT; } } private void mouseDragged(MouseEvent event) { double mouseX = parentX(event.getX()); double mouseY = parentY(event.getY()); if (state == S.DRAG) { listener.onDrag(mouseX - clickX, mouseY - clickY); } else if (state != S.DEFAULT) { double newH = nodeH; double newW = nodeW; if (state == S.SE_RESIZE) { newW = mouseX - nodeX; newH = mouseY - nodeY; } listener.onResize(newH, newW); } } private void mousePressed(MouseEvent event) { node.toFront(); if (isInResizeZone(event)) { setNewInitialEventCoordinates(event); state = currentMouseState(event); } else if (isInDragZone(event)) { clickX = event.getX(); clickY = event.getY(); state = S.DRAG; } else { state = S.DEFAULT; } } private void setNewInitialEventCoordinates(MouseEvent event) { nodeX = nodeX(); nodeY = nodeY(); nodeH = nodeH(); nodeW = nodeW(); clickX = event.getX(); clickY = event.getY(); } private boolean isInResizeZone(MouseEvent event) { return isRightResizeZone(event) || isBottomResizeZone(event) ; } private boolean isInDragZone(MouseEvent event) { double xPos = parentX(event.getX()); double yPos = parentY(event.getY()); double nodeX = nodeX() + 1; double nodeY = nodeY() + 1; double nodeX0 = nodeX() + nodeW() - 1; double nodeY0 = nodeY() + nodeH() - 1; return (xPos > nodeX && xPos < nodeX0) && (yPos > nodeY && yPos < nodeY0); } private boolean isRightResizeZone(MouseEvent event) { return intersect(this.node.getBoundsInParent().getWidth(), event.getX()); } private boolean isBottomResizeZone(MouseEvent event) { return intersect(this.node.getBoundsInParent().getHeight(), event.getY()); } private boolean intersect(double side, double point) { return side + MARGIN > point && side - MARGIN < point; } private double parentX(double localX) { return nodeX() + localX; } private double parentY(double localY) { return nodeY() + localY; } private double nodeX() { return this.node.getBoundsInParent().getMinX(); } private double nodeY() { return this.node.getBoundsInParent().getMinY(); } private double nodeW() { return this.node.getBoundsInParent().getWidth(); } private double nodeH() { return this.node.getBoundsInParent().getHeight(); } }