/*
*
*/
package borderless;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.fxml.FXML;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Pane;
import javafx.stage.Screen;
import javafx.stage.Stage;
/**
* Controller implements window controls: maximize, minimize, drag, and Aero
* Snap.
*
* @version 1.0
*/
public class BorderlessController {
/** The stage. */
private Stage stage;
/** The prev size. */
protected Delta prevSize;
/** The prev pos. */
protected Delta prevPos;
/** The maximized. */
private ReadOnlyBooleanWrapper maximized;
/** The snapped. */
private boolean snapped;
/** The left pane. */
@FXML
private Pane leftPane;
/** The right pane. */
@FXML
private Pane rightPane;
/** The top pane. */
@FXML
private Pane topPane;
/** The bottom pane. */
@FXML
private Pane bottomPane;
/** The top left pane. */
@FXML
private Pane topLeftPane;
/** The top right pane. */
@FXML
private Pane topRightPane;
/** The bottom left pane. */
@FXML
private Pane bottomLeftPane;
/** The bottom right pane. */
@FXML
private Pane bottomRightPane;
/** The bottom. */
String bottom = "bottom";
/**
* The constructor.
*/
public BorderlessController() {
prevSize = new Delta();
prevPos = new Delta();
maximized = new ReadOnlyBooleanWrapper(false);
snapped = false;
}
/**
* Maximized property.
*
* @return True is the window is maximized or False if it is not
*/
public ReadOnlyBooleanProperty maximizedProperty() {
return maximized.getReadOnlyProperty();
}
/**
* Called after the FXML layout is loaded.
*/
@FXML
private void initialize() {
setResizeControl(leftPane, "left");
setResizeControl(rightPane, "right");
setResizeControl(topPane, "top");
setResizeControl(bottomPane, bottom);
setResizeControl(topLeftPane, "top-left");
setResizeControl(topRightPane, "top-right");
setResizeControl(bottomLeftPane, bottom + "-left");
setResizeControl(bottomRightPane, bottom + "-right");
}
/**
* Set the Stage of the controller.
*
* @param primaryStage
* the new stage
*/
protected void setStage(Stage primaryStage) {
this.stage = primaryStage;
}
/**
* Maximize on/off the application.
*/
protected void maximize() {
Rectangle2D screen;
try {
if (Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth() / 2, stage.getHeight() / 2).isEmpty())
screen = Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth(), stage.getHeight()).get(0).getVisualBounds();
else
screen = Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth() / 2, stage.getHeight() / 2).get(0)
.getVisualBounds();
} catch (Exception ex) {
ex.printStackTrace();
return;
}
if (maximized.get()) {
stage.setWidth(prevSize.x);
stage.setHeight(prevSize.y);
stage.setX(prevPos.x);
stage.setY(prevPos.y);
setMaximized(false);
} else {
// Record position and size, and maximize.
if (!snapped) {
prevSize.x = stage.getWidth();
prevSize.y = stage.getHeight();
prevPos.x = stage.getX();
prevPos.y = stage.getY();
} else if (!screen.contains(prevPos.x, prevPos.y)) {
if (prevSize.x > screen.getWidth())
prevSize.x = screen.getWidth() - 20;
if (prevSize.y > screen.getHeight())
prevSize.y = screen.getHeight() - 20;
prevPos.x = screen.getMinX() + ( screen.getWidth() - prevSize.x ) / 2;
prevPos.y = screen.getMinY() + ( screen.getHeight() - prevSize.y ) / 2;
}
stage.setX(screen.getMinX());
stage.setY(screen.getMinY());
stage.setWidth(screen.getWidth());
stage.setHeight(screen.getHeight());
setMaximized(true);
}
}
/**
* Minimize the application.
*/
protected void minimize() {
stage.setIconified(true);
}
/**
* Set a node that can be pressed and dragged to move the application
* around.
*
* @param node
* the node.
*/
@SuppressWarnings("boxing")
protected void setMoveControl(final Node node) {
final Delta delta = new Delta();
final Delta eventSource = new Delta();
// Record drag deltas on press.
node.setOnMousePressed(m -> {
if (m.isPrimaryButtonDown()) {
delta.x = m.getSceneX(); //getX()
delta.y = m.getSceneY(); //getY()
if (maximized.get() || snapped) {
delta.x = prevSize.x * ( m.getSceneX() / stage.getWidth() );//(m.getX() / stage.getWidth())
delta.y = prevSize.y * ( m.getSceneY() / stage.getHeight() );//(m.getY() / stage.getHeight())
} else {
prevSize.x = stage.getWidth();
prevSize.y = stage.getHeight();
prevPos.x = stage.getX();
prevPos.y = stage.getY();
}
eventSource.x = m.getScreenX();
eventSource.y = node.prefHeight(stage.getHeight());
}
});
// Dragging moves the application around.
node.setOnMouseDragged(m -> {
if (m.isPrimaryButtonDown()) {
// Move x axis.
stage.setX(m.getScreenX() - delta.x);
if (snapped) {
// Aero Snap off.
Rectangle2D screen = Screen.getScreensForRectangle(m.getScreenX(), m.getScreenY(), 1, 1).get(0).getVisualBounds();
stage.setHeight(screen.getHeight());
if (m.getScreenY() > eventSource.y) {
stage.setWidth(prevSize.x);
stage.setHeight(prevSize.y);
snapped = false;
}
} else {
// Move y axis.
stage.setY(m.getScreenY() - delta.y);
}
// Aero Snap off.
if (maximized.get()) {
stage.setWidth(prevSize.x);
stage.setHeight(prevSize.y);
setMaximized(false);
}
}
});
// Maximize on double click.
node.setOnMouseClicked(m -> {
if ( ( m.getButton().equals(MouseButton.PRIMARY) ) && ( m.getClickCount() == 2 ))
maximize();
});
// Aero Snap on release.
node.setOnMouseReleased(m -> {
if ( ( m.getButton().equals(MouseButton.PRIMARY) ) && ( m.getScreenX() != eventSource.x )) {
Rectangle2D screen = Screen.getScreensForRectangle(m.getScreenX(), m.getScreenY(), 1, 1).get(0).getVisualBounds();
// Aero Snap Left.
if (m.getScreenX() == screen.getMinX()) {
stage.setY(screen.getMinY());
stage.setHeight(screen.getHeight());
stage.setX(screen.getMinX());
if (screen.getWidth() / 2 < stage.getMinWidth()) {
stage.setWidth(stage.getMinWidth());
} else {
stage.setWidth(screen.getWidth() / 2);
}
snapped = true;
}
// Aero Snap Right.
else if (m.getScreenX() == screen.getMaxX() - 1) {
stage.setY(screen.getMinY());
stage.setHeight(screen.getHeight());
if (screen.getWidth() / 2 < stage.getMinWidth()) {
stage.setWidth(stage.getMinWidth());
} else {
stage.setWidth(screen.getWidth() / 2);
}
stage.setX(screen.getMaxX() - stage.getWidth());
snapped = true;
}
// Aero Snap Top.
else if (m.getScreenY() == screen.getMinY()) {
if (!screen.contains(prevPos.x, prevPos.y)) {
if (prevSize.x > screen.getWidth())
prevSize.x = screen.getWidth() - 20;
if (prevSize.y > screen.getHeight())
prevSize.y = screen.getHeight() - 20;
prevPos.x = screen.getMinX() + ( screen.getWidth() - prevSize.x ) / 2;
prevPos.y = screen.getMinY() + ( screen.getHeight() - prevSize.y ) / 2;
}
stage.setX(screen.getMinX());
stage.setY(screen.getMinY());
stage.setWidth(screen.getWidth());
stage.setHeight(screen.getHeight());
setMaximized(true);
}
}
});
}
/**
* Set pane to resize application when pressed and dragged.
*
* @param pane
* the pane the action is set to.
* @param direction
* the resize direction. Diagonal: 'top' or 'bottom' + 'right' or
* 'left'.
*/
private void setResizeControl(Pane pane , final String direction) {
pane.setOnMouseDragged(m -> {
if (m.isPrimaryButtonDown()) {
double width = stage.getWidth();
double height = stage.getHeight();
// Horizontal resize.
if (direction.endsWith("left") && ( ( width > stage.getMinWidth() ) || ( m.getX() < 0 ) )) {
stage.setWidth(width - m.getScreenX() + stage.getX());
stage.setX(m.getScreenX());
} else if ( ( direction.endsWith("right") ) && ( ( width > stage.getMinWidth() ) || ( m.getX() > 0 ) )) {
stage.setWidth(width + m.getX());
}
// Vertical resize.
if (direction.startsWith("top")) {
if (snapped) {
stage.setHeight(prevSize.y);
snapped = false;
} else if ( ( height > stage.getMinHeight() ) || ( m.getY() < 0 )) {
stage.setHeight(height - m.getScreenY() + stage.getY());
stage.setY(m.getScreenY());
}
} else if (direction.startsWith(bottom)) {
if (snapped) {
stage.setY(prevPos.y);
snapped = false;
} else if ( ( height > stage.getMinHeight() ) || ( m.getY() > 0 ))
stage.setHeight(height + m.getY());
}
}
});
// Record application height and y position.
pane.setOnMousePressed(m -> {
if ( ( m.isPrimaryButtonDown() ) && ( !snapped )) {
prevSize.y = stage.getHeight();
prevPos.y = stage.getY();
}
});
// Aero Snap Resize.
pane.setOnMouseReleased(m -> {
if ( ( m.getButton().equals(MouseButton.PRIMARY) ) && ( !snapped )) {
Rectangle2D screen = Screen.getScreensForRectangle(m.getScreenX(), m.getScreenY(), 1, 1).get(0).getVisualBounds();
if ( ( stage.getY() <= screen.getMinY() ) && ( direction.startsWith("top") )) {
stage.setHeight(screen.getHeight());
stage.setY(screen.getMinY());
snapped = true;
}
if ( ( m.getScreenY() >= screen.getMaxY() ) && ( direction.startsWith(bottom) )) {
stage.setHeight(screen.getHeight());
stage.setY(screen.getMinY());
snapped = true;
}
}
});
// Aero Snap resize on double click.
pane.setOnMouseClicked(m -> {
if ( ( m.getButton().equals(MouseButton.PRIMARY) ) && ( m.getClickCount() == 2 )
&& ( "top".equals(direction) || bottom.equals(direction) )) {
Rectangle2D screen = Screen.getScreensForRectangle(stage.getX(), stage.getY(), stage.getWidth() / 2, stage.getHeight() / 2).get(0)
.getVisualBounds();
if (snapped) {
stage.setHeight(prevSize.y);
stage.setY(prevPos.y);
snapped = false;
} else {
prevSize.y = stage.getHeight();
prevPos.y = stage.getY();
stage.setHeight(screen.getHeight());
stage.setY(screen.getMinY());
snapped = true;
}
}
});
}
/**
* Determines if the Window is maximized.
*
* @param maximized
* the new maximized
*/
private void setMaximized(boolean maximized) {
this.maximized.set(maximized);
setResizable(!maximized);
}
/**
* Disable/enable the resizing of your stage. Enabled by default.
*
* @param bool
* false to disable, true to enable.
*/
protected void setResizable(boolean bool) {
leftPane.setDisable(!bool);
rightPane.setDisable(!bool);
topPane.setDisable(!bool);
bottomPane.setDisable(!bool);
topLeftPane.setDisable(!bool);
topRightPane.setDisable(!bool);
bottomLeftPane.setDisable(!bool);
bottomRightPane.setDisable(!bool);
}
}