package org.smartly.application.desktopgap.impl.app.utils.fx;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import org.smartly.application.desktopgap.impl.app.applications.events.FrameResizeEvent;
import org.smartly.commons.Delegates;
/**
*
*/
public class Sizable {
private static final double MIN_WIDTH = 64;
private static final double MIN_HEIGHT = 64;
private static final double AREA_HEIGHT = 10;
private static final int CORNER_HEIGHT = 15;
private static final String ID_TOP = "__top_stack__";
private static final String ID_RIGHT = "__right_stack__";
private static final String ID_BOTTOM = "__bottom_stack__";
private static final String ID_LEFT = "__left_stack__";
private static enum Direction {
UNDEFINED,
N,
NE,
E,
SE,
S,
SW,
W,
NW
}
private final Delegates.Handlers _eventHandlers;
private final Pane _parent;
private double _initialScreenX;
private double _initialScreenY;
private double _initialSceneX;
private double _initialSceneY;
private double _initialHeight;
private double _initialWidth;
private Direction _direction;
private boolean _dragging;
private double _minWidth;
private double _minHeight;
private double _maxWidth;
private double _maxHeight;
// --------------------------------------------------------------------
// e v e n t s
// --------------------------------------------------------------------
public static interface OnResize {
void handle(final FrameResizeEvent event);
}
private static final Class EVENT_ON_RESIZE = OnResize.class;
// --------------------------------------------------------------------
// c o n s t r u c t o r
// --------------------------------------------------------------------
public Sizable(final Pane pane) {
_eventHandlers = new Delegates.Handlers();
_minWidth = MIN_WIDTH;
_minHeight = MIN_HEIGHT;
_maxWidth = -1;
_maxHeight = -1;
if (null != pane) {
if (pane instanceof AnchorPane) {
_parent = pane;
makeSizable((AnchorPane) pane);
} else {
// add new AnchorPane on top of pane
_parent = null;
}
} else {
_parent = null;
}
}
public Sizable(final Pane pane,
final double minWidth,
final double minHeight,
final double maxWidth,
final double maxHeight) {
this(pane);
_minWidth = minWidth;
_minHeight = minHeight;
_maxWidth = maxWidth;
_maxHeight = maxHeight;
}
public void onResize(final OnResize handler) {
_eventHandlers.add(handler);
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private void makeSizable(final AnchorPane anchorPane) {
if (null == anchorPane) {
return;
}
this.addRight(anchorPane);
this.addLeft(anchorPane);
this.addTop(anchorPane);
this.addBottom(anchorPane);
}
private double getThresholdX() {
if (null != _parent) {
return (_parent.getLayoutX() - _parent.getScene().getX()) * 2;
}
return 0.0;
}
private double getThresholdY() {
if (null != _parent) {
return (_parent.getLayoutY() - _parent.getScene().getY()) * 2;
}
return 0.0;
}
private Direction getDirection(final Node stack, double sceneX, double sceneY) {
final String id = stack.getId();
final double width = _parent.getWidth();
final double height = _parent.getHeight();
if (ID_TOP.equalsIgnoreCase(id)) {
if (sceneX < CORNER_HEIGHT) {
return Direction.NW;
} else if (sceneX > (width - CORNER_HEIGHT)) {
return Direction.NE;
}
return Direction.N;
} else if (ID_RIGHT.equalsIgnoreCase(id)) {
return Direction.E;
} else if (ID_BOTTOM.equalsIgnoreCase(id)) {
if (sceneX < CORNER_HEIGHT) {
return Direction.SW;
} else if (sceneX > (width - CORNER_HEIGHT)) {
return Direction.SE;
}
return Direction.S;
} else if (ID_LEFT.equalsIgnoreCase(id)) {
return Direction.W;
}
return Direction.UNDEFINED;
}
private void setCursor(final Node stack, final Direction direction) {
if (Direction.N.equals(direction)) {
stack.setCursor(Cursor.N_RESIZE);
} else if (Direction.NE.equals(direction)) {
stack.setCursor(Cursor.NE_RESIZE);
} else if (Direction.E.equals(direction)) {
stack.setCursor(Cursor.E_RESIZE);
} else if (Direction.SE.equals(direction)) {
stack.setCursor(Cursor.SE_RESIZE);
} else if (Direction.S.equals(direction)) {
stack.setCursor(Cursor.S_RESIZE);
} else if (Direction.SW.equals(direction)) {
stack.setCursor(Cursor.SW_RESIZE);
} else if (Direction.W.equals(direction)) {
stack.setCursor(Cursor.W_RESIZE);
} else if (Direction.NW.equals(direction)) {
stack.setCursor(Cursor.NW_RESIZE);
}
}
private void handleFocus(final Node stack,
final MouseEvent me) {
if (!_dragging) {
final double x = me.getSceneX();
final double y = me.getSceneY();
_direction = getDirection(stack, x, y);
setCursor(stack, _direction);
}
}
private void handlePressed(final Node stack, final MouseEvent me) {
_dragging = true;
try {
if (me.getButton() != MouseButton.MIDDLE) {
_initialScreenX = me.getScreenX();
_initialScreenY = me.getScreenY();
_initialSceneX = me.getSceneX();
_initialSceneY = me.getSceneY();
_initialHeight = stack.getScene().getHeight();
_initialWidth = stack.getScene().getWidth();
}
} catch (Throwable ignored) {
}
}
private void handleReleased(final Node stack, final MouseEvent me) {
_dragging = false;
this.emitResize();
}
private void handleDragged(final Node stack,
final MouseEvent me) {
try {
if (me.getButton() != MouseButton.MIDDLE) {
//-- delta --//
final double deltaX = me.getScreenX() - _initialScreenX;
final double deltaY = me.getScreenY() - _initialScreenY;
if (Direction.N.equals(_direction)) {
//-- NORTH --//
stack.getScene().getWindow().setHeight(_initialHeight - deltaY);
// move window on Y axis
stack.getScene().getWindow().setY(me.getScreenY() - _initialSceneY);
} else if (Direction.NE.equals(_direction)) {
//-- NORTH-EAST --//
stack.getScene().getWindow().setHeight(_initialHeight - deltaY);
stack.getScene().getWindow().setWidth(_initialWidth + deltaX);
// move window on Y axis
stack.getScene().getWindow().setY(me.getScreenY() - _initialSceneY);
} else if (Direction.E.equals(_direction)) {
//-- EAST --//
stack.getScene().getWindow().setWidth(_initialWidth + deltaX);
} else if (Direction.SE.equals(_direction)) {
//-- SOUTH-EAST --//
stack.getScene().getWindow().setHeight(_initialHeight + deltaY);
stack.getScene().getWindow().setWidth(_initialWidth + deltaX);
// move window on Y axis
//stack.getScene().getWindow().setX(me.getScreenX() - _initialSceneX);
} else if (Direction.S.equals(_direction)) {
//-- SOUTH --//
stack.getScene().getWindow().setHeight(_initialHeight + deltaY);
} else if (Direction.SW.equals(_direction)) {
//-- SOUTH_WEST --//
stack.getScene().getWindow().setHeight(_initialHeight + deltaY);
stack.getScene().getWindow().setWidth(_initialWidth - deltaX);
// move window on Y axis
stack.getScene().getWindow().setX(me.getScreenX() - _initialSceneX);
} else if (Direction.W.equals(_direction)) {
//-- WEST --//
final double x = this.getThresholdX();
// move window on X axis
stack.getScene().getWindow().setX(me.getScreenX() - x + _initialSceneX);
// resize Width
stack.getScene().getWindow().setWidth(_initialWidth - deltaX);
} else if (Direction.NW.equals(_direction)) {
//-- NORTH-WEST --//
final double x = this.getThresholdX();
// move window on X axis
stack.getScene().getWindow().setX(me.getScreenX() - x + _initialSceneX);
// move window on Y axis
stack.getScene().getWindow().setY(me.getScreenY() - _initialSceneY);
// resize
stack.getScene().getWindow().setHeight(_initialHeight - deltaY);
stack.getScene().getWindow().setWidth(_initialWidth - deltaX);
}
// min
if (stack.getScene().getWindow().getHeight() < _minHeight) {
stack.getScene().getWindow().setHeight(_minHeight);
}
if (stack.getScene().getWindow().getWidth() < _minWidth) {
stack.getScene().getWindow().setWidth(_minWidth);
}
// max
if (_maxHeight > _minHeight && stack.getScene().getWindow().getHeight() > _maxHeight) {
stack.getScene().getWindow().setHeight(_maxHeight);
}
if (_maxWidth > _minWidth && stack.getScene().getWindow().getWidth() > _maxWidth) {
stack.getScene().getWindow().setWidth(_maxWidth);
}
}
} catch (Throwable ignored) {
}
}
private void emitResize() {
_eventHandlers.trigger(EVENT_ON_RESIZE, new FrameResizeEvent(_parent, _parent.getWidth(), _parent.getHeight()));
}
// ------------------------------------------------------------------------
// TOP
// ------------------------------------------------------------------------
private void addTop(final AnchorPane anchorPane) {
final StackPane stack = new StackPane();
stack.setId(ID_TOP);
stack.setPrefHeight(AREA_HEIGHT);
stack.setAlignment(Pos.TOP_CENTER);
stack.setFocusTraversable(true);
AnchorPane.setTopAnchor(stack, -(AREA_HEIGHT / 2));
AnchorPane.setRightAnchor(stack, 0.0);
AnchorPane.setLeftAnchor(stack, 0.0);
anchorPane.getChildren().add(stack);
//-- handle --//
stack.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMouseMoved(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handlePressed(stack, me);
}
});
stack.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleDragged(stack, me);
}
});
stack.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleReleased(stack, me);
}
});
}
// ------------------------------------------------------------------------
// RIGHT
// ------------------------------------------------------------------------
private void addRight(final AnchorPane anchorPane) {
final StackPane stack = new StackPane();
stack.setId(ID_RIGHT);
stack.setPrefWidth(AREA_HEIGHT);
stack.setAlignment(Pos.CENTER);
stack.setFocusTraversable(true);
AnchorPane.setRightAnchor(stack, -5.0);
AnchorPane.setTopAnchor(stack, 0.0);
AnchorPane.setBottomAnchor(stack, 0.0);
anchorPane.getChildren().add(stack);
//-- handle --//
stack.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMouseMoved(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handlePressed(stack, me);
}
});
stack.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleDragged(stack, me);
}
});
stack.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleReleased(stack, me);
}
});
}
// ------------------------------------------------------------------------
// BOTTOM
// ------------------------------------------------------------------------
private void addBottom(final AnchorPane anchorPane) {
final StackPane stack = new StackPane();
stack.setId(ID_BOTTOM);
stack.setPrefHeight(AREA_HEIGHT);
stack.setAlignment(Pos.BOTTOM_CENTER);
stack.setFocusTraversable(true);
AnchorPane.setBottomAnchor(stack, -5.0);
AnchorPane.setRightAnchor(stack, 0.0);
AnchorPane.setLeftAnchor(stack, 0.0);
anchorPane.getChildren().add(stack);
//-- handle --//
stack.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMouseMoved(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handlePressed(stack, me);
}
});
stack.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleDragged(stack, me);
}
});
stack.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleReleased(stack, me);
}
});
}
// ------------------------------------------------------------------------
// LEFT
// ------------------------------------------------------------------------
private void addLeft(final AnchorPane anchorPane) {
final StackPane stack = new StackPane();
stack.setId(ID_LEFT);
stack.setPrefWidth(AREA_HEIGHT);
stack.setAlignment(Pos.CENTER);
stack.setFocusTraversable(true);
AnchorPane.setLeftAnchor(stack, 0.0);
AnchorPane.setTopAnchor(stack, 0.0);
AnchorPane.setBottomAnchor(stack, 0.0);
anchorPane.getChildren().add(stack);
//-- handle --//
stack.setOnMouseEntered(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMouseMoved(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleFocus(stack, me);
}
});
stack.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handlePressed(stack, me);
}
});
stack.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleDragged(stack, me);
}
});
stack.setOnMouseReleased(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent me) {
handleReleased(stack, me);
}
});
}
}