[英]Allow user to resize an undecorated Stage
I am working on making a screen recorder in JavaFX and one utility that is mandatory in the screen recorder is to let the user define how much area to record.我正在使用 JavaFX 制作屏幕录像机,屏幕录像机中必须使用的一个实用程序是让用户定义要记录的区域。
I managed to make an undecorated , semi-transparent Stage
that can be dragged around to define the area and added a close
button to let the user confirm the area which is to be recorded.我设法制作了一个未装饰的半透明Stage
,可以拖动它来定义区域,并添加了一个close
按钮,让用户确认要录制的区域。
Now, how do I let the user resize the stage by dragging it by its edges ?现在,我如何让用户通过拖动它的边缘来调整它的大小?
SSCCE: SSCCE:
package draggable;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.StackPaneBuilder;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class DraggableStage extends Application{
Button close;
StackPane holder;
Rectangle2D maxBounds;
Scene theScene;
double pressedX;
double pressedY;
double draggedX;
double draggedY;
@Override
public void start(Stage stage) throws Exception {
final Stage theStage = stage;
// determine how big the screen is
maxBounds = Screen.getPrimary().getVisualBounds();
//create the close button
close = ButtonBuilder
.create()
.text("Close")
.build();
//create the StackPane holder for the button
holder = StackPaneBuilder
.create()
.alignment(Pos.CENTER)
.children(close)
.build();
// you cannot resize the screen beyond the max resolution of the screen
holder.setMaxSize(maxBounds.getWidth(), maxBounds.getHeight());
//you cannot resize under half the width and height of the screen
holder.setMinSize(maxBounds.getWidth() / 2,maxBounds.getHeight() / 2);
//the scene where it all happens
theScene = SceneBuilder
.create()
.root(holder)
.width(maxBounds.getWidth() / 2)
.height(maxBounds.getHeight() / 2)
.build();
// add the button listeners
close.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event) {
theStage.close();
}
});
// add the drag and press listener for the StackPane
holder.setOnMousePressed(new EventHandler<MouseEvent>(){
@Override
public void handle(MouseEvent e) {
pressedX = e.getX();
pressedY = e.getY();
}
});
holder.setOnMouseDragged(new EventHandler<MouseEvent>(){
@Override
public void handle(MouseEvent e) {
draggedX = e.getX();
draggedY = e.getY();
double differenceX = draggedX - pressedX;
double differenceY = draggedY - pressedY;
theStage.setX(theStage.getX() + differenceX);
theStage.setY(theStage.getY() + differenceY);
}
});
//the mandatory mumbo jumbo
theScene.setFill(Color.rgb(128, 128, 128, 0.5));
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(theScene);
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
Application.launch("draggable.DraggableStage");
}
}
Image:图片:
I created a ResizeHelper class which can help you in that case, usage:我创建了一个 ResizeHelper 类,它可以在这种情况下为您提供帮助,用法:
ResizeHelper.addResizeListener(yourStage);
the helper class:助手类:
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
//created by Alexander Berg
public class ResizeHelper {
public static void addResizeListener(Stage stage) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, resizeListener);
}
}
public static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, listener);
}
}
}
static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
public ResizeListener(Stage stage) {
this.stage = stage;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
sceneWidth = scene.getWidth(),
sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) == true) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if(MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) == true) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) == true) {
if (Cursor.DEFAULT.equals(cursorEvent) == false) {
if (Cursor.W_RESIZE.equals(cursorEvent) == false && Cursor.E_RESIZE.equals(cursorEvent) == false) {
double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.N_RESIZE.equals(cursorEvent) == true || Cursor.NE_RESIZE.equals(cursorEvent) == true) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY());
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY);
}
}
}
if (Cursor.N_RESIZE.equals(cursorEvent) == false && Cursor.S_RESIZE.equals(cursorEvent) == false) {
double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.W_RESIZE.equals(cursorEvent) == true || Cursor.SW_RESIZE.equals(cursorEvent) == true) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX);
}
}
}
}
}
}
}
}
I updated @Yevhenii.Kanivets ' updated version of @Alexander.Berg 's ResizeHelper class.我更新了 @Alexander.Berg 的 ResizeHelper 类的 @Yevhenii.Kanivets 的更新版本。 This includes the ability to drag the scene, with the best implementation I've found of ResizeHelper.这包括拖动场景的能力,以及我发现的 ResizeHelper 的最佳实现。 Also set minimum stage size to a height of 1 and width of 1 instead of 0, because there are bugs if they stay at 0.还将最小舞台大小设置为高度为 1,宽度为 1 而不是 0,因为如果它们保持为 0,则会出现错误。
Someone still can improve on this answer.有人仍然可以改进这个答案。 The SW, W, NW, N, and NE edges do not resize smoothly, although it's a minor issue. SW、W、NW、N 和 NE 边缘不会平滑地调整大小,尽管这是一个小问题。 When resizing from those edges, the scene also can shift slightly.从这些边缘调整大小时,场景也可能会稍微移动。 They should resize smoothly like the S, SE, and E edges do.它们应该像 S、SE 和 E 边缘一样平滑地调整大小。 The latter edges do not shift the scene.后面的边缘不会改变场景。
I cannot speak for @MK 's implementation as I am not using Kotlin, but I'd give that a shot first if you are.我不能代表 @MK 的实现,因为我没有使用 Kotlin,但如果你是,我会先试一试。
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
/**
* Util class to handle window resizing when a stage style set to StageStyle.UNDECORATED.
* Includes dragging of the stage.
* Original on 6/13/14.
* Updated on 8/15/17.
* Updated on 12/19/19.
*
* @author Alexander.Berg
* @author Evgenii Kanivets
* @author Zachary Perales
*/
public class ResizeHelper {
public static void addResizeListener(Stage stage) {
addResizeListener(stage, 1, 1, Double.MAX_VALUE, Double.MAX_VALUE);
}
public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
resizeListener.setMinWidth(minWidth);
resizeListener.setMinHeight(minHeight);
resizeListener.setMaxWidth(maxWidth);
resizeListener.setMaxHeight(maxHeight);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, resizeListener);
}
}
private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, listener);
}
}
}
static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private boolean resizing = true;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double screenOffsetX = 0;
private double screenOffsetY = 0;
// Max and min sizes for controlled stage
private double minWidth;
private double maxWidth;
private double minHeight;
private double maxHeight;
public ResizeListener(Stage stage) {
this.stage = stage;
}
public void setMinWidth(double minWidth) {
this.minWidth = minWidth;
}
public void setMaxWidth(double maxWidth) {
this.maxWidth = maxWidth;
}
public void setMinHeight(double minHeight) {
this.minHeight = minHeight;
}
public void setMaxHeight(double maxHeight) {
this.maxHeight = maxHeight;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
sceneWidth = scene.getWidth(),
sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
if (!Cursor.DEFAULT.equals(cursorEvent)) {
resizing = true;
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
|| Cursor.NE_RESIZE.equals(cursorEvent)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY() );
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
setStageHeight(mouseEventY + startY);
}
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
|| Cursor.SW_RESIZE.equals(cursorEvent)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
setStageWidth(mouseEventX + startX);
}
}
}
resizing = false;
}
}
if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent)) {
resizing = false;
screenOffsetX = stage.getX() - mouseEvent.getScreenX();
screenOffsetY = stage.getY() - mouseEvent.getScreenY();
}
if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent) && resizing == false) {
stage.setX(mouseEvent.getScreenX() + screenOffsetX);
stage.setY(mouseEvent.getScreenY() + screenOffsetY);
}
}
private void setStageWidth(double width) {
width = Math.min(width, maxWidth);
width = Math.max(width, minWidth);
stage.setWidth(width);
}
private void setStageHeight(double height) {
height = Math.min(height, maxHeight);
height = Math.max(height, minHeight);
stage.setHeight(height);
}
}
}
Edit:编辑:
I've updated this code to not drag any scrollbars, as nobody wants that functionality and I needed to remove it myself.我已更新此代码以不拖动任何滚动条,因为没有人想要该功能,我需要自己将其删除。 It should be easy to disallow additional controls from being dragged if you come across the need, by comparing my two code submissions on here.如果您遇到需要,通过比较我在此处提交的两个代码,应该很容易禁止拖动其他控件。
Note that if the disallowed control is on the edge of the scene, you need to wrap it in something draggable to access resizing on those edges.请注意,如果不允许的控件位于场景的边缘,则需要将其包裹在可拖动的内容中以访问在这些边缘上调整大小。
public class ResizeHelper {
static boolean isScrollbar = false;
public static void addResizeListener(Stage stage) {
addResizeListener(stage, 1, 1, Double.MAX_VALUE, Double.MAX_VALUE);
}
public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
resizeListener.setMinWidth(minWidth);
resizeListener.setMinHeight(minHeight);
resizeListener.setMaxWidth(maxWidth);
resizeListener.setMaxHeight(maxHeight);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
if (child instanceof ScrollBar) {
isScrollbar = true;
} else if (!(child instanceof ScrollBar)) {
isScrollbar = false;
addListenerDeeply(child, resizeListener);
}
}
}
private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
if (child instanceof ScrollBar) {
isScrollbar = true;
} else if (!(child instanceof ScrollBar)) {
isScrollbar = false;
addListenerDeeply(child, listener);
}
}
}
}
static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private boolean resizing = true;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double screenOffsetX = 0;
private double screenOffsetY = 0;
// Max and min sizes for controlled stage
private double minWidth;
private double maxWidth;
private double minHeight;
private double maxHeight;
public ResizeListener(Stage stage) {
this.stage = stage;
}
public void setMinWidth(double minWidth) {
this.minWidth = minWidth;
}
public void setMaxWidth(double maxWidth) {
this.maxWidth = maxWidth;
}
public void setMinHeight(double minHeight) {
this.minHeight = minHeight;
}
public void setMaxHeight(double maxHeight) {
this.maxHeight = maxHeight;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
sceneWidth = scene.getWidth(),
sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) && stage.isMaximized() == false ) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
if (!Cursor.DEFAULT.equals(cursorEvent)) {
resizing = true;
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
|| Cursor.NE_RESIZE.equals(cursorEvent)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY() );
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
setStageHeight(mouseEventY + startY);
}
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
|| Cursor.SW_RESIZE.equals(cursorEvent)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
setStageWidth(mouseEventX + startX);
}
}
}
resizing = false;
}
}
if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent) ) {
resizing = false;
screenOffsetX = stage.getX() - mouseEvent.getScreenX();
screenOffsetY = stage.getY() - mouseEvent.getScreenY();
}
if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent) && resizing == false) {
stage.setX(mouseEvent.getScreenX() + screenOffsetX);
stage.setY(mouseEvent.getScreenY() + screenOffsetY);
}
}
private void setStageWidth(double width) {
width = Math.min(width, maxWidth);
width = Math.max(width, minWidth);
stage.setWidth(width);
}
private void setStageHeight(double height) {
height = Math.min(height, maxHeight);
height = Math.max(height, minHeight);
stage.setHeight(height);
}
}
}
Here is an updated version of ResizeHelper posted by @Alexander.Berg, which supports min and max stage sizes.这是@Alexander.Berg 发布的 ResizeHelper 的更新版本,它支持最小和最大阶段大小。
package sem.helper;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
/**
* Util class to handle window resizing when a stage style set to StageStyle.UNDECORATED.
* Created on 8/15/17.
*
* @author Evgenii Kanivets
*/
public class ResizeHelper {
public static void addResizeListener(Stage stage) {
addResizeListener(stage, 0, 0, Double.MAX_VALUE, Double.MAX_VALUE);
}
public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
resizeListener.setMinWidth(minWidth);
resizeListener.setMinHeight(minHeight);
resizeListener.setMaxWidth(maxWidth);
resizeListener.setMaxHeight(maxHeight);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, resizeListener);
}
}
private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, listener);
}
}
}
static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
// Max and min sizes for controlled stage
private double minWidth;
private double maxWidth;
private double minHeight;
private double maxHeight;
public ResizeListener(Stage stage) {
this.stage = stage;
}
public void setMinWidth(double minWidth) {
this.minWidth = minWidth;
}
public void setMaxWidth(double maxWidth) {
this.maxWidth = maxWidth;
}
public void setMinHeight(double minHeight) {
this.minHeight = minHeight;
}
public void setMaxHeight(double maxHeight) {
this.maxHeight = maxHeight;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
sceneWidth = scene.getWidth(),
sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
if (!Cursor.DEFAULT.equals(cursorEvent)) {
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent)
|| Cursor.NE_RESIZE.equals(cursorEvent)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY());
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
setStageHeight(mouseEventY + startY);
}
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)
|| Cursor.SW_RESIZE.equals(cursorEvent)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
setStageWidth(mouseEventX + startX);
}
}
}
}
}
}
private void setStageWidth(double width) {
width = Math.min(width, maxWidth);
width = Math.max(width, minWidth);
stage.setWidth(width);
}
private void setStageHeight(double height) {
height = Math.min(height, maxHeight);
height = Math.max(height, minHeight);
stage.setHeight(height);
}
}
}
Here is a revision @Alexander.Berg 's post.这是修订版@Alexander.Berg 的帖子。 This correctly handles the minWidth and maxWidth properties of the Scene.这可以正确处理场景的 minWidth 和 maxWidth 属性。 His version does not do this: it completely ignores maxWidth and butchers minWidth.他的版本没有这样做:它完全忽略了 maxWidth 和 butchers minWidth。 There is also a piece of code at the bottom that allows dragging the window.底部还有一段代码允许拖动窗口。
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class ResizeHelper {
public static void addResizeListener(Stage stage) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, resizeListener);
}
}
private static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, listener);
}
}
}
private static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double startScreenX = 0;
private double startScreenY = 0;
public ResizeListener(Stage stage) {
this.stage = stage;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX();
double mouseEventY = mouseEvent.getSceneY();
double sceneWidth = scene.getWidth();
double sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
if (!Cursor.DEFAULT.equals(cursorEvent)) {
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
double maxHeight = stage.getMaxHeight();
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || Cursor.NE_RESIZE.equals(cursorEvent)) {
double newHeight = stage.getHeight() - (mouseEvent.getScreenY() - stage.getY());
if (newHeight >= minHeight && newHeight <= maxHeight) {
stage.setHeight(newHeight);
stage.setY(mouseEvent.getScreenY());
} else {
newHeight = Math.min(Math.max(newHeight, minHeight), maxHeight);
// y1 + h1 = y2 + h2
// y1 = y2 + h2 - h1
stage.setY(stage.getY() + stage.getHeight() - newHeight);
stage.setHeight(newHeight);
}
} else {
stage.setHeight(Math.min(Math.max(mouseEventY + startY, minHeight), maxHeight));
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
double maxWidth = stage.getMaxWidth();
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)) {
double newWidth = stage.getWidth() - (mouseEvent.getScreenX() - stage.getX());
if (newWidth >= minWidth && newWidth <= maxWidth) {
stage.setWidth(newWidth);
stage.setX(mouseEvent.getScreenX());
} else {
newWidth = Math.min(Math.max(newWidth, minWidth), maxWidth);
// x1 + w1 = x2 + w2
// x1 = x2 + w2 - w1
stage.setX(stage.getX() + stage.getWidth() - newWidth);
stage.setWidth(newWidth);
}
} else {
stage.setWidth(Math.min(Math.max(mouseEventX + startX, minWidth), maxWidth));
}
}
}
}
if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startScreenX = mouseEvent.getScreenX();
startScreenY = mouseEvent.getScreenY();
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
if (Cursor.DEFAULT.equals(cursorEvent)) {
stage.setX(stage.getX() + mouseEvent.getScreenX() - startScreenX);
startScreenX = mouseEvent.getScreenX();
stage.setY(stage.getY() + mouseEvent.getScreenY() - startScreenY);
startScreenY = mouseEvent.getScreenY();
}
}
}
}
}
I've looked into some threads where they discuss about it before I tried myself to "solve" it, but in the end there was no Problem.在我尝试自己“解决”它之前,我已经查看了一些他们讨论它的线程,但最终没有问题。
I created a button in the bottom-right corner, gave the Button an OnDraggedMethod and simply used the mouseevent and set the Height/Width on MousePosition - stage X/Y position.我在右下角创建了一个按钮,给按钮一个 OnDraggedMethod 并简单地使用鼠标事件并在 MousePosition 上设置高度/宽度 - 舞台 X/Y 位置。
double newX = event.getScreenX() - stage.getX() + 13;
double newY = event.getScreenY() - stage.getY() + 10;
if (newX % 5 == 0 || newY % 5 == 0) {
if (newX > 550) {
stage.setWidth(newX);
} else {
stage.setWidth(550);
}
if (newY > 200) {
stage.setHeight(newY);
} else {
stage.setHeight(200);
}
}
Also I set a minimal Width and Height.我还设置了最小的宽度和高度。 And because i think that the laggy resize-animation is annoying i surrounded the statement with the if and just resize the stage every 5th pixel.并且因为我认为滞后的调整大小动画很烦人,所以我用 if 包围了语句,并且每隔 5 个像素调整舞台大小。
Looks much better!看起来好多了!
PS: The +13 and +10 is just for the MousePosition. PS:+13 和 +10 仅用于 MousePosition。 Otherweise the Mouse is on the Corner not on the Button^^.否则鼠标在角落上而不是在按钮上^^。
What can I say... I'm a slow learner.我能说什么...我是一个缓慢的学习者。 Took me a bit to really go through all the code and understand it.我花了一点时间来真正浏览所有代码并理解它。 Its not hard... just a lot going on.这并不难......只是发生了很多事情。 I decided to make some changes to use absolute mouse position as opposed to relative mouse position, I also changed the class overall from using static members and whatnot.我决定进行一些更改以使用绝对鼠标位置而不是相对鼠标位置,我还从使用静态成员等整体更改了类。 Here's what I came up with and works quite well for myself.这就是我想出来的,对我自己来说效果很好。 For those searching for similar solutions, enjoy my copious comments... Using this version of this class is as simple as:对于那些正在寻找类似解决方案的人,请享受我的大量评论......使用此类的这个版本非常简单:
ResizeListener listener = new ResizeListener(stage); ResizeListener listener = new ResizeListener(stage);
import draco_logger.LeafLogger;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;
//originally created by Alexander Berg
//https://stackoverflow.com/questions/19455059/allow-user-to-resize-an-undecorated-stage
//
//modified by Joseph Adomatis
//extracted ResizeListener class and made public, added arg for CWinMaxButton for detecting Maximized Window, using my own logger
//changed MouseDragged routines and private variables to make use of screen absolute values instead of relative values
//MouseDragged also updated to respect Min/Max sizes
public class ResizeListener implements EventHandler<MouseEvent> {
public ResizeListener(Stage stage) {
LeafLogger log = new LeafLogger("ResizeListener", "Constructor(Stage)");
this.stage = stage;
this._max = null;
isPressed = false;
cursorEvent = Cursor.DEFAULT;
border = 3;
stageStartH = 0;
stageStartW = 0;
stageStartX = 0;
stageStartY = 0;
this.addResizeListener();
log.Wither();
}
public void AddMaxButton(CWinMaxButton max){ this._max = max; }
private void addResizeListener() {
LeafLogger log = new LeafLogger("ResizeListener", "addResizeListener");
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_ENTERED, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, this);
this.stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, this);
ObservableList<Node> children = this.stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child);
}
log.Wither();
}
private void addListenerDeeply(Node node) {
LeafLogger log = new LeafLogger("ResizeListener", "addListenerDeeply");
node.addEventHandler(MouseEvent.MOUSE_MOVED, this);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, this);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, this);
node.addEventHandler(MouseEvent.MOUSE_ENTERED, this);
node.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, this);
node.addEventHandler(MouseEvent.MOUSE_EXITED, this);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, this);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child);
}
}
log.Wither();
}
@Override
public void handle(MouseEvent mouseEvent) {
LeafLogger log = new LeafLogger("ResizeListener","handle");
// Check if we registered a maximize button
if(this._max != null){
// Check with the maximize button to see if window is currently maximized
if(this._max.GetMaximized()){
// We do not resize Maximized windows
log.Wither();
return;
}
}
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
// set minHeight vars in such a way as to ensure that there is always a border over which we can continue to resize again
// if stage MinHeight were 0 and you resized to 0, the draggable zone is gone and you cannot resize anymore
// so regardless of app preference, we artificially set min sizes to leave all borders
double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
double maxHeight = stage.getMaxHeight();
double maxWidth = stage.getMaxWidth();
// capture the position of the mouse cursor relative to the stage anchor point at the time of the event
double mouseEventX = mouseEvent.getSceneX();
double mouseEventY = mouseEvent.getSceneY();
// capture the current scene Height and Width
double sceneHeight = scene.getHeight();
double sceneWidth = scene.getWidth();
// capture the screen max visual Height and Width
double screenHeight = Screen.getPrimary().getVisualBounds().getHeight();
double screenWidth = Screen.getPrimary().getVisualBounds().getWidth();
// if MOUSE_MOVED and its new position is over one of the stage borders, we want to update the cursor to be one of the resize variety
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
// if MOUSE_EXITED the stage screen area and we'd pressed but did not release the mouse button, then we want to maintain our current cursor
// otherwise, since the mouse is outside our stage, we return it to the default cursor
} else if(MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
if(!isPressed){
scene.setCursor(Cursor.DEFAULT);
}
// similarly, if MOUSE ENTERED the stage screen area and we'd pressed but did not release the mouse button, then we want to maintain the current cursor
// otherwise, since the mouse is coming back to us, we dont want to keep whatever other cursor may have been set by other windows so we return to default
} else if(MouseEvent.MOUSE_ENTERED.equals(mouseEventType) || MouseEvent.MOUSE_ENTERED_TARGET.equals(mouseEventType)){
if(!isPressed){
scene.setCursor(Cursor.DEFAULT);
}
// if MOUSE_PRESSED we might need to keep track that we pressed it and are initiating a potential drag/resize event
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
// right now we dont care if mouse was pressed, but we might
boolean iCare = false;
// check the cursor type, if it is a resize cursor then mouse is over a border and we DO care that we pressed the mouse
if(Cursor.N_RESIZE.equals(cursorEvent) || Cursor.S_RESIZE.equals(cursorEvent) || Cursor.E_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent)){
iCare = true;
} else if(Cursor.NE_RESIZE.equals(cursorEvent) || Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.SE_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)){
iCare = true;
}
// if we care that we pressed the mouse, we need to capture the initial data that will be used by our drag event handler to actually resize the window
if(iCare){
stageStartH = stage.getHeight();
stageStartW = stage.getWidth();
stageStartX = stage.getX();
stageStartY = stage.getY();
mouseStartX = mouseEvent.getScreenX();
mouseStartY = mouseEvent.getScreenY();
isPressed = true;
}
// if MOUSE_RELEASED, we don't care what the mouse does anymore so release our flag
} else if(MouseEvent.MOUSE_RELEASED.equals(mouseEventType)){
isPressed = false;
// if MOUSE_DRAGGED, this handler might have something to do
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
// if the cursor is still default, then this handler doesnt care about the drag event so ignore everything else
// this handler only cares if the cursor is of the resize variety
if(Cursor.DEFAULT.equals(cursorEvent)){
return;
}
// Check if there is a vertical component to the window resize
// The only time there isn't a vertical component is if the mouse is strictly on the west or east side of the stage
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
// There is a vertical component.
// If we are resizing the north side however, we will be resetting both the Y coordinate of the stage anchor as well as the stage height
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || Cursor.NE_RESIZE.equals(cursorEvent)) {
double mouseDifY = mouseStartY - stageStartY;
// we are moving the north side
// figure out where the south side of the stage is
double finalY = stageStartY + stage.getHeight();
// we are free to move the north side until it reaches the point where the distance to the south side is greater than maxHeight
// OR, we run into the top of the screen
double minStageY = (finalY - maxHeight) > 0 ? (finalY - maxHeight): 0;
double minMouseY = minStageY + mouseDifY;
// we are free to move the north side until it reaches the point where the distance to the south side is less than minHeight
double maxStageY = finalY - minHeight;
double maxMouseY = maxStageY + mouseDifY;
// capture the absolute position of the mouse at the time of the event
double curMouseY = mouseEvent.getScreenY();
if(curMouseY < minMouseY){
stage.setY(minStageY);
// Our mouse passed the value at which we would breach max height
// We dont want the curMouseY to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseY = minMouseY;
} else if(curMouseY > maxMouseY){
stage.setY(maxStageY);
// Our mouse passed the value at which we would breach min height
// We dont want the curMouseY to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseY = maxMouseY;
} else {
stage.setY(curMouseY - mouseDifY);
}
double newY = stage.getY();
double newHeight = finalY - newY;
stage.setHeight(newHeight);
// Our stage and mouse start variables were set via the mouse pressed event handle
// If we did above procedure in the mouse released event handle, it would work, but there would be no display update till mouse released.
// By using mouse dragged event handle, we get display update each event cycle... but we have to constantly update our start variables for the next cycle
// While dragging mouse, you aren't releasing and re-pressing it to update the variables....
stageStartY = stage.getY();
stageStartH = stage.getHeight();
mouseStartY = curMouseY;
} else {
// Else, we are resizing the south side, and the Y coordinate remains fixed. We only change the stage height
// figure out where the current south side actually is
double curFinalY = stageStartY + stageStartH;
double mouseDifY = mouseStartY - curFinalY;
// we are free to move the north side until it reaches the point where the distance to the south side is greater than maxHeight
// OR, we run into the bottom of the screen
double maxFinalY = (stageStartY + maxHeight) < screenHeight ? (stageStartY + maxHeight) : screenHeight;
double maxMouseY = maxFinalY + mouseDifY;
// we are free to move the south side until the point where the distance from anchor to south side is less than minHeight
double minFinalY = stageStartY + minHeight;
double minMouseY = minFinalY + mouseDifY;
// capture the absolute position of the mouse at the time of the event
double curMouseY = mouseEvent.getScreenY();
if (curMouseY < minMouseY) {
stage.setHeight(minHeight);
// Our mouse passed the value at which we would breach min height
// We don't want the curMouseY to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseY = minMouseY;
} else if(curMouseY > maxMouseY){
double newFinalY = maxMouseY - mouseDifY;
double newHeight = newFinalY - stageStartY;
stage.setHeight(newHeight);
// Our mouse passed the value at which we would breach max height
// We don't want the curMouseY to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseY = maxMouseY;
} else {
double newFinalY = curMouseY - mouseDifY;
double newHeight = newFinalY - stageStartY;
stage.setHeight(newHeight);
}
// Our stage and mouse start variables were set via the mouse pressed event handle
// If we did above procedure in the mouse released event handle, it would work, but there would be no display update till mouse released.
// By using mouse dragged event handle, we get display update each event cycle... but we have to constantly update our start variables for the next cycle
// While dragging mouse, you aren't releasing and re-pressing it to update the variables....
stageStartY = stage.getY();
stageStartH = stage.getHeight();
mouseStartY = curMouseY;
}
}
// Check if there is a horizontal component to the window resize
// The only time there isn't a horizontal component is if the mouse is strictly on the north or south side of the stage.
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
// There is a horizontal component.
// If we are resizing the west side however, we will be resetting both the X coordinate of the stage anchor as well as the stage width.
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)) {
// we are moving the west side
// figure out where the east side of the stage is
double mouseDifX = mouseStartX - stageStartX;
double finalX = stageStartX + stageStartW;
// we are free to move the west side until it reaches the point where the distance to the east side is greater than maxWidth
// OR, we run into the left of the screen
double minStageX = (finalX - maxHeight) > 0 ? (finalX - maxHeight): 0;
double minMouseX = minStageX + mouseDifX;
// we are free to move the west side until it reaches the point where the distance to the east side is less than minWidth
double maxStageX = finalX - minWidth;
double maxMouseX = maxStageX + mouseDifX;
// capture the absolute position of the mouse at the time of the event
double curMouseX = mouseEvent.getScreenX();
if(curMouseX < minMouseX){
stage.setX(minStageX);
// Our mouse passed the value at which we would breach max width
// We don't want the curMouseX to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseX = minMouseX;
} else if(curMouseX > maxMouseX){
stage.setX(maxStageX);
curMouseX = maxMouseX;
// Our mouse passed the value at which we would breach min width
// We don't want the curMouseX to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
} else {
stage.setX(curMouseX - mouseDifX);
}
double newX = stage.getX();
double newWidth = finalX - newX;
stage.setWidth(newWidth);
// Our stage and mouse start variables were set via the mouse pressed event handle
// If we did above procedure in the mouse released event handle, it would work, but there would be no display update till mouse released.
// By using mouse dragged event handle, we get display update each event cycle... but we have to constantly update our start variables for the next cycle
// While dragging mouse, you aren't releasing and re-pressing it to update the variables....
stageStartX = stage.getX();
stageStartW = stage.getWidth();
mouseStartX = curMouseX;
} else {
// Else, we are resizing the east side, and the X coordinate remains fixed. We only change the stage width.
// figure out where the current east side actually is
double curFinalX = stageStartX + stageStartW;
double mouseDifX = mouseStartX - curFinalX;
// we are free to move the east side until the point where the distance from anchor to east side is less than minWidth
double minFinalX = stageStartX + minWidth;
double minMouseX = minFinalX + mouseDifX;
// we are free to move the east side until it reaches the point where the distance to the west side is greater than maxWidth
// OR, we run into the right of the screen
double maxFinalX = (stageStartX + maxWidth) < screenWidth ? (stageStartX + maxWidth) : screenWidth;
double maxMouseX = maxFinalX + mouseDifX;
// capture the absolute position of the mouse at the time of the event
double curMouseX = mouseEvent.getScreenX();
if (curMouseX < minMouseX) {
stage.setWidth(minWidth);
curMouseX = minMouseX;
// Our mouse passed the value at which we would breach min width
// We don't want the curMouseX to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
} else if(curMouseX > maxMouseX){
double newFinalX = maxMouseX - mouseDifX;
double newWidth = newFinalX - stageStartX;
stage.setWidth(newWidth);
// Our mouse passed the value at which we would breach max width
// We don't want the curMouseY to update any more until the mouse is back over the border.
// Otherwise, the window border would resize relative to mouse movement, not relative to absolute mouse position
curMouseX = maxMouseX;
} else {
double newFinalX = curMouseX - mouseDifX;
double newWidth = newFinalX - stageStartX;
stage.setWidth(newWidth);
}
// Our stage and mouse start variables were set via the mouse pressed event handle
// If we did above procedure in the mouse released event handle, it would work, but there would be no display update till mouse released.
// By using mouse dragged event handle, we get display update each event cycle... but we have to constantly update our start variables for the next cycle
// While dragging mouse, you aren't releasing and re-pressing it to update the variables....
stageStartX = stage.getX();
stageStartW = stage.getWidth();
mouseStartX = curMouseX;
}
}
}
log.Wither();
}
// <editor-fold defaultstate="collapsed" desc="***** Private Variable Declarations *****">
private boolean isPressed;
private Cursor cursorEvent;
private CWinMaxButton _max;
private double mouseStartX;
private double mouseStartY;
private double stageStartH;
private double stageStartW;
private double stageStartX;
private double stageStartY;
private final int border;
private final Stage stage;
// </editor-fold>
}
Here is an updated version of ResizeHelper posted by @Alexander.Berg , which supports window drag :这是@Alexander.Berg发布的 ResizeHelper 的更新版本,它支持窗口拖动:
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
//created by Alexander Berg
public class ResizeHelper {
private static double xOffset = 0;
private static double yOffset = 0;
public static void addResizeListener(Stage stage) {
ResizeListener resizeListener = new ResizeListener(stage);
stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
ObservableList<Node> children = stage.getScene().getRoot().getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, resizeListener);
}
}
public static void addListenerDeeply(Node node, EventHandler<MouseEvent> listener) {
node.addEventHandler(MouseEvent.MOUSE_MOVED, listener);
node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener);
node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED, listener);
node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener);
if (node instanceof Parent) {
Parent parent = (Parent) node;
ObservableList<Node> children = parent.getChildrenUnmodifiable();
for (Node child : children) {
addListenerDeeply(child, listener);
}
}
}
static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
public ResizeListener(Stage stage) {
this.stage = stage;
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
Scene scene = stage.getScene();
double mouseEventX = mouseEvent.getSceneX(), mouseEventY = mouseEvent.getSceneY(), sceneWidth = scene.getWidth(), sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType) == true) {
if (mouseEventX < border && mouseEventY < border) {
cursorEvent = Cursor.NW_RESIZE;
} else if (mouseEventX < border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY < border) {
cursorEvent = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.SE_RESIZE;
} else if (mouseEventX < border) {
cursorEvent = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - border) {
cursorEvent = Cursor.E_RESIZE;
} else if (mouseEventY < border) {
cursorEvent = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - border) {
cursorEvent = Cursor.S_RESIZE;
} else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
} else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) {
scene.setCursor(Cursor.DEFAULT);
} else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) == true) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
xOffset = mouseEvent.getSceneX();
yOffset = mouseEvent.getSceneY();
} else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) == true) {
if (Cursor.DEFAULT.equals(cursorEvent) == false) {
if (Cursor.W_RESIZE.equals(cursorEvent) == false && Cursor.E_RESIZE.equals(cursorEvent) == false) {
double minHeight = stage.getMinHeight() > (border * 2) ? stage.getMinHeight() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.N_RESIZE.equals(cursorEvent) == true || Cursor.NE_RESIZE.equals(cursorEvent) == true) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight());
stage.setY(mouseEvent.getScreenY());
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY);
}
}
}
if (Cursor.N_RESIZE.equals(cursorEvent) == false && Cursor.S_RESIZE.equals(cursorEvent) == false) {
double minWidth = stage.getMinWidth() > (border * 2) ? stage.getMinWidth() : (border * 2);
if (Cursor.NW_RESIZE.equals(cursorEvent) == true || Cursor.W_RESIZE.equals(cursorEvent) == true || Cursor.SW_RESIZE.equals(cursorEvent) == true) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth());
stage.setX(mouseEvent.getScreenX());
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX);
}
}
}
} else if (mouseEvent.getSceneY() < 70) {
stage.setX(mouseEvent.getScreenX() - xOffset);
stage.setY(mouseEvent.getScreenY() - yOffset);
}
}
}
}
}
Thanks @Alexander.Berg for providing this utility class (1st version).感谢 @Alexander.Berg 提供此实用程序类(第 1 版)。 It helped me to get the desired functionality for my Undecorated Stage.它帮助我为我的未装饰舞台获得了所需的功能。
However, I have few queries regarding some points:但是,我对以下几点几乎没有疑问:
I tried to modify your code to address all the above queries.我试图修改您的代码以解决上述所有查询。 Please find below the updated ResizeHelper.请在下面找到更新的 ResizeHelper。 I am only trying to simplify the things a bit further.我只是想进一步简化事情。 Once again thanks for providing the code and letting me to think with a starting point.再次感谢您提供代码并让我从一个起点开始思考。
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
/**
* Helper class to set the resizing implementation for any given undecorated stage.
*/
public class ResizeHelper {
/**
* Handler to process the resizing of the the given stage.
*/
static class ResizeHandler implements EventHandler<MouseEvent> {
/** Space to consider around the stage border for resizing */
private static final int BORDER = 10;
/** Stage to which the handler is implemented */
private final Stage stage;
/** Current cursor reference to the scene */
private Cursor cursor = Cursor.DEFAULT;
/** X position of the drag start */
private double startX = 0;
/** Y position of the drag start */
private double startY = 0;
/**
* Constructor.
*
* @param stageTmp Stage to which resizing to be set.
*/
public ResizeHandler(final Stage stageTmp) {
stage = stageTmp;
}
@Override
public void handle(final MouseEvent event) {
final EventType<? extends MouseEvent> eventType = event.getEventType();
final Scene scene = stage.getScene();
final double mouseEventX = event.getSceneX();
final double mouseEventY = event.getSceneY();
final double sceneWidth = scene.getWidth();
final double sceneHeight = scene.getHeight();
if (MouseEvent.MOUSE_MOVED.equals(eventType)) {
setCursor(mouseEventX, mouseEventY, sceneWidth, sceneHeight);
scene.setCursor(cursor);
} else if (MouseEvent.MOUSE_PRESSED.equals(eventType)) {
startX = stage.getWidth() - mouseEventX;
startY = stage.getHeight() - mouseEventY;
consumeEventIfNotDefaultCursor(event);
} else if (MouseEvent.MOUSE_DRAGGED.equals(eventType) && !Cursor.DEFAULT.equals(cursor)) {
consumeEventIfNotDefaultCursor(event);
if (!Cursor.W_RESIZE.equals(cursor) && !Cursor.E_RESIZE.equals(cursor)) {
processVerticalDrag(event);
}
if (!Cursor.N_RESIZE.equals(cursor) && !Cursor.S_RESIZE.equals(cursor)) {
processHorizontalDrag(event);
}
}
}
/**
* Consumes the mouse event if the cursor is not the DEFAULT cursor.
*
* @param event MouseEvent instance.
*/
private void consumeEventIfNotDefaultCursor(final MouseEvent event) {
if (!cursor.equals(Cursor.DEFAULT)) {
event.consume();
}
}
/**
* Processes the horizontal drag movement and resizes the window width.
*
* @param event MouseEvent instance.
*/
private void processHorizontalDrag(final MouseEvent event) {
final double minWidth =
stage.getMinWidth() > BORDER * 2 ? stage.getMinWidth() : BORDER * 2;
final double mouseEventX = event.getSceneX();
if (Cursor.NW_RESIZE.equals(cursor)
|| Cursor.W_RESIZE.equals(cursor)
|| Cursor.SW_RESIZE.equals(cursor)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
stage.setWidth(stage.getX() - event.getScreenX() + stage.getWidth());
stage.setX(event.getScreenX());
}
} else if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX);
}
}
/**
* Processes the vertical drag movement and resizes the window height.
*
* @param event MouseEvent instance.
*/
private void processVerticalDrag(final MouseEvent event) {
final double minHeight =
stage.getMinHeight() > BORDER * 2 ? stage.getMinHeight() : BORDER * 2;
final double mouseEventY = event.getSceneY();
if (Cursor.NW_RESIZE.equals(cursor)
|| Cursor.N_RESIZE.equals(cursor)
|| Cursor.NE_RESIZE.equals(cursor)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
stage.setHeight(stage.getY() - event.getScreenY() + stage.getHeight());
stage.setY(event.getScreenY());
}
} else if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY);
}
}
/**
* Determines and sets the appropriate cursor based on the mouse position in relative to scene bounds.
*
* @param mouseEventX X position of mouse in the scene.
* @param mouseEventY Y position of mouse in the scene.
* @param sceneWidth Width of the scene.
* @param sceneHeight Height of the scene.
*/
private void setCursor(final double mouseEventX, final double mouseEventY, final double sceneWidth,
final double sceneHeight) {
final Cursor cursor1;
if (mouseEventX < BORDER && mouseEventY < BORDER) {
cursor1 = Cursor.NW_RESIZE;
} else if (mouseEventX < BORDER && mouseEventY > sceneHeight - BORDER) {
cursor1 = Cursor.SW_RESIZE;
} else if (mouseEventX > sceneWidth - BORDER && mouseEventY < BORDER) {
cursor1 = Cursor.NE_RESIZE;
} else if (mouseEventX > sceneWidth - BORDER && mouseEventY > sceneHeight - BORDER) {
cursor1 = Cursor.SE_RESIZE;
} else if (mouseEventX < BORDER) {
cursor1 = Cursor.W_RESIZE;
} else if (mouseEventX > sceneWidth - BORDER) {
cursor1 = Cursor.E_RESIZE;
} else if (mouseEventY < BORDER) {
cursor1 = Cursor.N_RESIZE;
} else if (mouseEventY > sceneHeight - BORDER) {
cursor1 = Cursor.S_RESIZE;
} else {
cursor1 = Cursor.DEFAULT;
}
cursor = cursor1;
}
}
/**
* Constructor.
*
*/
private ResizeHelper() {
}
/**
* Adds the resize handler to the provided stage.
*
* @param stage Stage to which the resizing should be implemented.
*/
public static void addResizeHandler(final Stage stage) {
ResizeHandler resizeHandler = new ResizeHandler(stage);
stage.setMinHeight(stage.getHeight());
stage.setMinWidth(stage.getWidth());
stage.setMaxHeight(stage.getMaxHeight());
stage.setMaxWidth(stage.getMaxWidth());
stage.getScene().addEventFilter(MouseEvent.ANY, resizeHandler);
}
}
Inspired by the many answers here, I wrote my own implementation that has all the necessary features that I could think of:受到这里许多答案的启发,我编写了自己的实现,其中包含我能想到的所有必要功能:
Draggability可拖动性
Fullscreen全屏
Resizeable可调整大小
Quick setup:快速设置:
StageInteractor(this)
.makeDraggable()
.makeFullscreenable()
.makeResizable()
Custom interaction areas:自定义交互区域:
StageInteractor(this)
// custom width / height for drag areas (top, right, bottom, left)
.makeDraggable(50, 20, 50, 20)
// custom width / height for double-clickable areas (top, right, bottom, left)
.makeFullscreenable(50, 20, 50, 20)
// custom width / height for resize areas (top, right, bottom, left)
.makeResizable(20, 20, 20, 20)
import javafx.scene.Cursor
import javafx.scene.Scene
import javafx.scene.input.MouseButton
import javafx.scene.input.MouseEvent
import javafx.stage.Screen
import javafx.stage.Stage
class StageInteractor {
private val stage: Stage
private val scene: Scene
private var isDraggable = false
private var isDragging = false
private var allowDragging = true
private var dragMarginTop = 0.0
private var dragMarginRight = 0.0
private var dragMarginBottom = 0.0
private var dragMarginLeft = 0.0
private var isFullscreenable = false
private var isFullscreen = false
private var allowFullscreen = true
private var fullscreenMarginTop = 0.0
private var fullscreenMarginRight = 0.0
private var fullscreenMarginBottom = 0.0
private var fullscreenMarginLeft = 0.0
private var stageWidthBeforeFullscreen = 0.0
private var stageHeightBeforeFullscreen = 0.0
private var stageXBeforeFullscreen = 0.0
private var stageYBeforeFullscreen = 0.0
private var isResizeable = false
private var isResizing = false
private var allowResizing = true
private var resizeDirection: ResizeDirection? = null
private var resizeMarginTop = 0.0
private var resizeMarginRight = 0.0
private var resizeMarginBottom = 0.0
private var resizeMarginLeft = 0.0
constructor(stage: Stage) {
this.stage = stage
this.scene = stage.scene
}
fun makeDraggable(
marginTop: Double = 50.0,
marginRight: Double = 0.0,
marginBottom: Double = 0.0,
marginLeft: Double = 0.0
): StageInteractor {
dragMarginTop = marginTop
dragMarginRight = marginRight
dragMarginBottom = marginBottom
dragMarginLeft = marginLeft
if (!isDraggable) {
isDraggable = true
var dragStartOffsetX = 0.0
var dragStartOffsetY = 0.0
scene.addEventHandler(MouseEvent.MOUSE_MOVED) {
val isWithinBounds = detectDraggingBounds(it)
if (isDraggable && allowDragging && isWithinBounds) {
scene.cursor = Cursor.OPEN_HAND
} else {
if (scene.cursor == Cursor.OPEN_HAND) {
scene.cursor = Cursor.DEFAULT
}
}
}
scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
dragStartOffsetX = stage.x - it.screenX
dragStartOffsetY = stage.y - it.screenY
}
scene.addEventHandler(MouseEvent.MOUSE_DRAGGED) {
val isWithinBounds = detectDraggingBounds(it)
if (isDraggable && allowDragging && isWithinBounds) {
isDragging = true
scene.cursor = Cursor.CLOSED_HAND
}
if (isDragging) {
stage.x = it.screenX + dragStartOffsetX
stage.y = it.screenY + dragStartOffsetY
}
}
scene.addEventHandler(MouseEvent.MOUSE_RELEASED) {
if (isDragging) {
isDragging = false
scene.cursor = Cursor.DEFAULT
}
}
}
return this
}
private fun detectDraggingBounds(event: MouseEvent): Boolean {
return event.sceneY <= dragMarginTop
|| scene.height - event.sceneY <= dragMarginBottom
|| event.sceneX <= dragMarginLeft
|| scene.width - event.sceneX <= dragMarginRight
}
fun makeFullscreenable(
marginTop: Double = 50.0,
marginRight: Double = 0.0,
marginBottom: Double = 0.0,
marginLeft: Double = 0.0
): StageInteractor {
fullscreenMarginTop = marginTop
fullscreenMarginRight = marginRight
fullscreenMarginBottom = marginBottom
fullscreenMarginLeft = marginLeft
if (!isFullscreenable) {
isFullscreenable = true
scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
val isDoubleClick = it.button == MouseButton.PRIMARY && it.clickCount >= 2
if (isFullscreenable && allowFullscreen && isDoubleClick && detectFullscreenBounds(it)) {
if (isFullscreen) {
isFullscreen = false
allowDragging = true
allowResizing = true
stage.x = stageXBeforeFullscreen
stage.y = stageYBeforeFullscreen
stage.width = stageWidthBeforeFullscreen
stage.height = stageHeightBeforeFullscreen
} else {
isFullscreen = true
allowDragging = false
allowResizing = false
stageWidthBeforeFullscreen = stage.width
stageHeightBeforeFullscreen = stage.height
stageXBeforeFullscreen = stage.x
stageYBeforeFullscreen = stage.y
val screenBounds = Screen.getPrimary().visualBounds
val newWidth = if (stage.maxWidth < screenBounds.width) {
stage.maxWidth
} else {
screenBounds.width
}
val newHeight = if (stage.maxHeight < screenBounds.height) {
stage.maxHeight
} else {
screenBounds.height
}
stage.width = newWidth
stage.height = newHeight
stage.x = screenBounds.minX
stage.y = screenBounds.minY
}
}
}
}
return this
}
private fun detectFullscreenBounds(event: MouseEvent): Boolean {
val isWithinBounds = event.sceneY <= fullscreenMarginTop
|| scene.height - event.sceneY <= fullscreenMarginBottom
|| event.sceneX <= fullscreenMarginLeft
|| scene.width - event.sceneX <= fullscreenMarginRight
val resizeDirection = detectResizeDirection(event)
return isWithinBounds && resizeDirection == null
}
fun makeResizable(
marginTop: Double = 10.0,
marginRight: Double = 10.0,
marginBottom: Double = 10.0,
marginLeft: Double = 10.0
): StageInteractor {
resizeMarginTop = marginTop
resizeMarginRight = marginRight
resizeMarginBottom = marginBottom
resizeMarginLeft = marginLeft
if (!isResizeable) {
isResizeable = true
scene.addEventHandler(MouseEvent.MOUSE_MOVED) {
if (isResizeable && allowResizing && !isResizing) {
when (detectResizeDirection(it)) {
ResizeDirection.NORTH_WEST -> scene.cursor = Cursor.NW_RESIZE
ResizeDirection.NORTH_EAST -> scene.cursor = Cursor.NE_RESIZE
ResizeDirection.SOUTH_WEST -> scene.cursor = Cursor.SW_RESIZE
ResizeDirection.SOUTH_EAST -> scene.cursor = Cursor.SE_RESIZE
ResizeDirection.NORTH -> scene.cursor = Cursor.N_RESIZE
ResizeDirection.SOUTH -> scene.cursor = Cursor.S_RESIZE
ResizeDirection.WEST -> scene.cursor = Cursor.W_RESIZE
ResizeDirection.EAST -> scene.cursor = Cursor.E_RESIZE
else -> {
val cursors = listOf(
Cursor.NW_RESIZE,
Cursor.NE_RESIZE,
Cursor.SW_RESIZE,
Cursor.SE_RESIZE,
Cursor.N_RESIZE,
Cursor.S_RESIZE,
Cursor.W_RESIZE,
Cursor.E_RESIZE
)
if (cursors.contains(scene.cursor)) {
scene.cursor = Cursor.DEFAULT
}
}
}
}
}
var resizeStartFromSceneX = 0.0
var resizeStartFromSceneY = 0.0
var resizeStartFromScreenX = 0.0
var resizeStartFromScreenY = 0.0
var resizeStartStageWidth = 0.0
var resizeStartStageHeight = 0.0
scene.addEventHandler(MouseEvent.MOUSE_PRESSED) {
if (isResizeable && allowResizing && !isResizing) {
resizeDirection = detectResizeDirection(it)
if (resizeDirection != null) {
if (it.button == MouseButton.PRIMARY && it.clickCount >= 2) {
val screenBounds = Screen.getPrimary().visualBounds
if (resizeDirection == ResizeDirection.NORTH || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.NORTH_EAST) {
stage.height = ensureStageHeightIsWithinLimits(
stage.height + stage.y - screenBounds.minY
)
stage.y = 0.0
}
if (resizeDirection == ResizeDirection.SOUTH || resizeDirection == ResizeDirection.SOUTH_WEST || resizeDirection == ResizeDirection.SOUTH_EAST) {
stage.height = ensureStageHeightIsWithinLimits(
screenBounds.height - stage.y + screenBounds.minY
)
if (stage.height == screenBounds.height) {
stage.y = 0.0
}
}
if (resizeDirection == ResizeDirection.WEST || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.SOUTH_WEST) {
stage.width = ensureStageWidthIsWithinLimits(
stage.width + stage.x
)
stage.x = 0.0
}
if (resizeDirection == ResizeDirection.EAST || resizeDirection == ResizeDirection.NORTH_EAST || resizeDirection == ResizeDirection.SOUTH_EAST) {
stage.width = ensureStageWidthIsWithinLimits(
screenBounds.width - stage.x
)
if (stage.width == screenBounds.width) {
stage.x = 0.0
}
}
} else {
isResizing = true
isDraggable = false
isFullscreenable = false
resizeStartFromScreenX = it.screenX
resizeStartFromScreenY = it.screenY
resizeStartFromSceneX = it.sceneX
resizeStartFromSceneY = it.sceneY
resizeStartStageWidth = stage.width
resizeStartStageHeight = stage.height
}
}
}
}
scene.addEventHandler(MouseEvent.MOUSE_DRAGGED) {
if (isResizing) {
if (resizeDirection == ResizeDirection.NORTH || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.NORTH_EAST) {
val newHeight = ensureStageHeightIsWithinLimits(
resizeStartStageHeight + (resizeStartFromScreenY - it.screenY)
)
val newY = when (newHeight) {
stage.maxHeight, stage.minHeight -> stage.y
else -> it.screenY - resizeStartFromSceneY
}
stage.height = newHeight
stage.y = newY
}
if (resizeDirection == ResizeDirection.SOUTH || resizeDirection == ResizeDirection.SOUTH_WEST || resizeDirection == ResizeDirection.SOUTH_EAST) {
val newHeight = ensureStageHeightIsWithinLimits(
resizeStartStageHeight + (it.screenY - resizeStartFromScreenY)
)
stage.height = newHeight
}
if (resizeDirection == ResizeDirection.WEST || resizeDirection == ResizeDirection.NORTH_WEST || resizeDirection == ResizeDirection.SOUTH_WEST) {
val newWidth = ensureStageWidthIsWithinLimits(
resizeStartStageWidth + (resizeStartFromScreenX - it.screenX)
)
val newX = when (newWidth) {
stage.maxWidth, stage.minWidth -> stage.x
else -> it.screenX - resizeStartFromSceneX
}
stage.width = newWidth
stage.x = newX
}
if (resizeDirection == ResizeDirection.EAST || resizeDirection == ResizeDirection.NORTH_EAST || resizeDirection == ResizeDirection.SOUTH_EAST) {
val newWidth = ensureStageWidthIsWithinLimits(
resizeStartStageWidth + (it.screenX - resizeStartFromScreenX)
)
stage.width = newWidth
}
}
}
scene.addEventHandler(MouseEvent.MOUSE_RELEASED) {
if (isResizing) {
isResizing = false
isDraggable = true
isFullscreenable = true
}
}
}
return this
}
private fun detectResizeDirection(event: MouseEvent): ResizeDirection? {
val isNorthResize = event.sceneY <= resizeMarginTop
val isSouthResize = scene.height - event.sceneY <= resizeMarginBottom
val isWestResize = event.sceneX <= resizeMarginLeft
val isEastResize = scene.width - event.sceneX <= resizeMarginRight
val isNorthWestResize = isNorthResize && isWestResize
val isNorthEastResize = isNorthResize && isEastResize
val isSouthWestResize = isSouthResize && isWestResize
val isSouthEastResize = isSouthResize && isEastResize
return when {
isNorthWestResize -> ResizeDirection.NORTH_WEST
isNorthEastResize -> ResizeDirection.NORTH_EAST
isSouthWestResize -> ResizeDirection.SOUTH_WEST
isSouthEastResize -> ResizeDirection.SOUTH_EAST
isNorthResize -> ResizeDirection.NORTH
isSouthResize -> ResizeDirection.SOUTH
isWestResize -> ResizeDirection.WEST
isEastResize -> ResizeDirection.EAST
else -> null
}
}
private fun ensureStageWidthIsWithinLimits(width: Double): Double {
val screenBounds = Screen.getPrimary().visualBounds
return when {
width > stage.maxWidth -> stage.maxWidth
width < stage.minWidth -> stage.minWidth
width > screenBounds.width -> screenBounds.width
else -> width
}
}
private fun ensureStageHeightIsWithinLimits(height: Double): Double {
val screenBounds = Screen.getPrimary().visualBounds
return when {
height > stage.maxHeight -> stage.maxHeight
height < stage.minHeight -> stage.minHeight
height > screenBounds.height -> screenBounds.height
else -> height
}
}
enum class ResizeDirection {
NORTH, NORTH_EAST, NORTH_WEST,
SOUTH, SOUTH_EAST, SOUTH_WEST,
EAST, WEST;
}
}
I made the original solution also support padding in the root container of the stage.我制作的原始解决方案也支持在舞台的根容器中填充。 Otherwise the cursor wouldn't be set properly and the resizing also.否则光标将无法正确设置,并且大小也会调整。 The resizing also works better by taking into account the mouse position inside the stage when starting to drag, to avoid "jumps" in the repositioning of the Stage.通过在开始拖动时考虑舞台内的鼠标位置,调整大小也能更好地工作,以避免在舞台重新定位时“跳跃” 。
package apro2.canbustool.ui.util;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
//created by Alexander Berg
public class ResizeHelper {
public static ResizeListener addResizeListener (Stage stage) {
ResizeListener resizeListener = new ResizeListener(stage);
Scene scene = stage.getScene();
scene.addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener);
scene.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener);
return resizeListener;
}
public static class ResizeListener implements EventHandler<MouseEvent> {
private Stage stage;
private Scene scene;
private Cursor cursorEvent = Cursor.DEFAULT;
private int border = 4;
private double startX = 0;
private double startY = 0;
private double sceneOffsetX = 0;
private double sceneOffsetY = 0;
private double padTop = 0;
private double padRight = 0;
private double padBottom = 0;
private double padLeft = 0;
public ResizeListener (Stage stage) {
this.stage = stage;
this.scene = stage.getScene();
}
public void setPadding (Insets padding) {
padTop = padding.getTop();
padRight = padding.getRight();
padBottom = padding.getBottom();
padLeft = padding.getLeft();
}
@Override
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
double mouseEventX = mouseEvent.getSceneX(),
mouseEventY = mouseEvent.getSceneY(),
viewWidth = stage.getWidth() - padLeft - padRight,
viewHeight = stage.getHeight() - padTop - padBottom;
if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) {
if (mouseEventX < border + padLeft && mouseEventY < border + padTop) {
cursorEvent = Cursor.NW_RESIZE;
}
else if (mouseEventX < border + padLeft && mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.SW_RESIZE;
}
else if (mouseEventX > viewWidth - border + padLeft && mouseEventY < border + padTop) {
cursorEvent = Cursor.NE_RESIZE;
}
else if (mouseEventX > viewWidth - border + padLeft && mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.SE_RESIZE;
}
else if (mouseEventX < border + padLeft) {
cursorEvent = Cursor.W_RESIZE;
}
else if (mouseEventX > viewWidth - border + padLeft) {
cursorEvent = Cursor.E_RESIZE;
}
else if (mouseEventY < border + padTop) {
cursorEvent = Cursor.N_RESIZE;
}
else if (mouseEventY > viewHeight - border + padTop) {
cursorEvent = Cursor.S_RESIZE;
}
else {
cursorEvent = Cursor.DEFAULT;
}
scene.setCursor(cursorEvent);
}
else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)){
scene.setCursor(Cursor.DEFAULT);
}
else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
startX = viewWidth - mouseEventX;
startY = viewHeight - mouseEventY;
sceneOffsetX = mouseEvent.getSceneX();
sceneOffsetY = mouseEvent.getSceneY();
}
else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && !Cursor.DEFAULT.equals(cursorEvent)) {
if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) {
double minHeight = stage.getMinHeight() > (border*2) ? stage.getMinHeight() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || Cursor.NE_RESIZE.equals(cursorEvent)) {
if (stage.getHeight() > minHeight || mouseEventY < 0) {
double height = stage.getY() - mouseEvent.getScreenY() + stage.getHeight() + sceneOffsetY;
double y = mouseEvent.getScreenY() - sceneOffsetY;
stage.setHeight(height);
stage.setY(y);
}
} else {
if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) {
stage.setHeight(mouseEventY + startY + padBottom + padTop);
}
}
}
if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) {
double minWidth = stage.getMinWidth() > (border*2) ? stage.getMinWidth() : (border*2);
if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || Cursor.SW_RESIZE.equals(cursorEvent)) {
if (stage.getWidth() > minWidth || mouseEventX < 0) {
double width = stage.getX() - mouseEvent.getScreenX() + stage.getWidth() + sceneOffsetX;
double x = mouseEvent.getScreenX() - sceneOffsetX;
stage.setWidth(width);
stage.setX(x);
}
} else {
if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) {
stage.setWidth(mouseEventX + startX + padLeft + padRight);
}
}
}
}
}
}
} }
I know this thread is old, but i think new people are stumbling against this problem and no true way was made possible by the code provided above, i played around 2 days with the solutions above and had no hope for perfect draggable and resizable window, until now.我知道这个线程很旧,但我认为新人遇到了这个问题,上面提供的代码没有实现真正的方法,我用上面的解决方案玩了大约 2 天,对完美的可拖动和可调整大小的窗口没有希望,直到现在。
https://github.com/goxr3plus/FX-BorderlessScene https://github.com/goxr3plus/FX-BorderlessScene
This library saved my sanity, using 3 lines of code i managed to give the proper behaviour to my scene.这个库保存了我的理智,使用 3 行代码,我设法为我的场景提供了正确的行为。
Supports:支持:
Build concept:建设理念:
Use Alignment.TOP|RIGHT|BOTTOM|LEFT
and Alignment.TOP_LEFT|BOTTOM_LEFT
for corners to place the Border Resize Panes inside the root StackPane对角使用Alignment.TOP|RIGHT|BOTTOM|LEFT
和Alignment.TOP_LEFT|BOTTOM_LEFT
将 Border Resize Panes 放置在根 StackPane 内
Gluon GUI Designer FXML_Structure: Gluon GUI 设计器 FXML_Structure:
/** Copyright © 2021 Izon Company, Free To Share: class ResizeHelper.java */
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.stage.Screen;
import javafx.stage.Stage;
/**
* handles Stage resizing for StageStyle.UNDECORATED and deals with a
* indentation which can be used to render a CSS drop shadow effect around the
* scene with transparent background.
*
* @author Henryk Zschuppan,
* @date MARCH 01,21
*/
public class ResizeHandler implements EventHandler<MouseEvent> {
public static ResizeHandler install(Stage stage, double titlebarHeight, double pullEdgeDepth, double indentation) {
ResizeHandler handler = new ResizeHandler(stage, titlebarHeight, pullEdgeDepth, indentation);
stage.getScene().addEventHandler(MouseEvent.ANY, handler);
return handler;
}
public static Rectangle2D SCREEN_BOUNDS = Screen.getPrimary().getVisualBounds();
/** select the boundary clipping orientation in relation to the stage */
private static enum CHECK {
LOW,
HIGH,
NONE;
}
/** Stage to which the handler is implemented */
final private Stage stage;
/** Area from top to consider for stage reposition */
double titlebarHeight;
/** Space to consider around the stage border for resizing */
final private int depth;
/** padding space to render in the CSS effect drop shadow */
final private double pad;
/** stage size limits */
final private double minWidth, minHeight, maxWidth, maxHeight;
/** start point of mouse position on screen */
private Point2D startDrag = null;
/** frame rectangle of the stage on drag start */
private Rectangle2D startRectangle;
/** the relative mouse orientation to the stage */
private CHECK checkX = CHECK.NONE, checkY = CHECK.NONE;
private boolean inRepositioningArea = false;
private ResizeHandler(Stage stage, double titlebarHeight, double pullEdgeDepth, double indentation) {
this.stage = stage;
this.titlebarHeight = titlebarHeight;
pad = indentation;
depth = (int) (indentation + pullEdgeDepth);
minWidth = stage.getMinWidth();
minHeight = stage.getMinHeight();
maxWidth = stage.getMaxWidth();
maxHeight = stage.getMaxHeight();
}
@Override
public void handle(MouseEvent mouseEvent) {
if (!mouseEvent.getButton().equals(MouseButton.PRIMARY))
return;
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
final double mX = mouseEvent.getScreenX();
final double mY = mouseEvent.getScreenY();
/* local coordinates inside stage */
final double lX = mouseEvent.getSceneX();
final double lY = mouseEvent.getSceneY();
final double sW = stage.getWidth();
final double sH = stage.getHeight();
if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) {
if (lX < depth && lY < depth) {
setXYCheck(CHECK.LOW, CHECK.LOW);
} else if (lX < depth && lY > sH - depth) {
setXYCheck(CHECK.LOW, CHECK.HIGH);
} else if (lX > sW - depth && lY < depth) {
setXYCheck(CHECK.HIGH, CHECK.LOW);
} else if (lX > sW - depth && lY > sH - depth) {
setXYCheck(CHECK.HIGH, CHECK.HIGH);
} else if (lX < depth) {
setXYCheck(CHECK.LOW, CHECK.NONE);
} else if (lX > sW - depth) {
setXYCheck(CHECK.HIGH, CHECK.NONE);
} else if (lY < depth) {
setXYCheck(CHECK.NONE, CHECK.LOW);
} else if (lY > sH - depth) {
setXYCheck(CHECK.NONE, CHECK.HIGH);
} else {
setXYCheck(CHECK.NONE, CHECK.NONE);
}
/* check mouse is not inside the resize border space */
if (lX < pad || lY < pad || lX > sW - pad || lY > sH - pad) {
setXYCheck(CHECK.NONE, CHECK.NONE);
}
inRepositioningArea = lY >= depth && lY < this.titlebarHeight + pad;
startDrag = new Point2D(mX, mY);
startRectangle = new Rectangle2D(stage.getX(), stage.getY(), sW, sH);
} else if (!isNone() && MouseEvent.MOUSE_DRAGGED.equals(mouseEventType)) {
/* stage resizing */
double dX = mX - startDrag.getX();
double dY = mY - startDrag.getY();
double min, max;
/* don't overwrite start values */
double x = startRectangle.getMinX(), y = startRectangle.getMinY(), x2 = startRectangle.getMaxX(), y2 = startRectangle.getMaxY();
switch (checkX) {
case LOW :// LEFT
min = Math.max(x - maxWidth, (0 - pad));
max = x2 - minWidth;
x = clip(x + dX, min, max);
break;
case HIGH : // RIGHT
min = x + minWidth;
max = Math.min(x + maxWidth, SCREEN_BOUNDS.getWidth() + pad);
x2 = clip(x2 + dX, min, max);
default :
break;
}
switch (checkY) {
case LOW : // TOP
min = Math.max(y2 - maxHeight, (0 - pad));
max = y2 - minHeight;
y = clip(y + dY, min, max);
break;
case HIGH :// BOTTOM
min = y + minHeight;
max = Math.min(y + maxHeight, SCREEN_BOUNDS.getHeight() + pad);
y2 = clip(y2 + dY, min, max);
default :
break;
}
updateStagePosition(x, y, x2, y2);
} else if (isNone() && MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && inRepositioningArea) {
/* stage repositioning */
double dX = mX - startDrag.getX();
double dY = mY - startDrag.getY();
this.stage.setX(startRectangle.getMinX() + dX);
this.stage.setY(startRectangle.getMinY() + dY);
stagePositionInsideScreen();
} else if (!isNone() && MouseEvent.MOUSE_RELEASED.equals(mouseEventType) && mouseEvent.getClickCount() == 2) {
/* The stage side is expanded or minimized by double-clicking */
double min, max;
/* don't overwrite start values */
double x = startRectangle.getMinX(), y = startRectangle.getMinY(), x2 = startRectangle.getMaxX(), y2 = startRectangle.getMaxY();
switch (checkX) {
case LOW :// LEFT
if (x > (0 - pad)) {
min = Math.max(x - maxWidth, (0 - pad));
max = x2 - minWidth;
x = clip((0 - pad), min, max);
} else {
x = x2 - minWidth;
}
break;
case HIGH : // RIGHT
if (x2 < SCREEN_BOUNDS.getWidth() + pad) {
min = x + minWidth;
max = Math.min(x + maxWidth, SCREEN_BOUNDS.getWidth() + pad);
x2 = clip(SCREEN_BOUNDS.getWidth() + pad, min, max);
} else {
x2 = x + minWidth;
}
default :
break;
}
switch (checkY) {
case LOW : // TOP
if (y > (0 - pad)) {
min = Math.max(y2 - maxHeight, (0 - pad));
max = y2 - minHeight;
y = clip((0 - pad), min, max);
} else {
y = y2 - minHeight;
}
break;
case HIGH :// BOTTOM
if (y2 < SCREEN_BOUNDS.getHeight() + pad) {
min = y + minHeight;
max = Math.min(y + maxHeight, SCREEN_BOUNDS.getHeight() + pad);
y2 = clip(SCREEN_BOUNDS.getHeight() + pad, min, max);
} else {
y2 = y + minHeight;
}
default :
break;
}
updateStagePosition(x, y, x2, y2);
}
}
private double clip(double checkValue, double minValue, double maxValue) {
if (checkValue < minValue) {
return minValue;
}
if (checkValue > maxValue) {
return maxValue;
}
return checkValue; // unmodified
}
private void setXYCheck(CHECK X, CHECK Y) {
checkX = X;
checkY = Y;
}
/** @return true if checkX and checkY is set to CHECK.NONE */
private boolean isNone() {
return checkX.equals(CHECK.NONE) && checkY.equals(CHECK.NONE);
}
private void stagePositionInsideScreen() {
int width = (int) this.stage.getWidth();
int height = (int) this.stage.getHeight();
if (stage.getX() + width - pad >= SCREEN_BOUNDS.getWidth()) {
stage.setX(SCREEN_BOUNDS.getWidth() - width + pad);
}
if (stage.getX() + pad < 0.0D) {
stage.setX(0.0D - pad);
}
if (stage.getY() + height - pad >= SCREEN_BOUNDS.getHeight()) {
stage.setY(SCREEN_BOUNDS.getHeight() - height + pad);
}
if (stage.getY() + pad < 0.0D)
stage.setY(0.0D - pad);
}
private void updateStagePosition(double x1, double y1, double x2, double y2) {
stage.setX(x1);
stage.setY(y1);
stage.setWidth(x2 - x1);
stage.setHeight(y2 - y1);
}
} // CLASS END
Don't forget to adapt the min and max size values to the Stage:不要忘记根据舞台调整最小和最大尺寸值:
public void setApplicationContentLayout(AbstractApp app) {
Pane contentLayout= app.getRootLayout();
BorderPane contentBorderPane = (BorderPane) rootStackPane.getChildren().get(0);
try {
contentBorderPane.setCenter(contentLayout);
// 2 is border width
contentBorderPane.setMinWidth(contentLayout.getMinWidth() + 2);
contentBorderPane.setMaxWidth(contentLayout.getMaxWidth() + 2);
// add titlebar height
contentBorderPane.setMinHeight(contentBorderPane.getMinHeight() + contentLayout.getMinHeight() + 2);
contentBorderPane.setMaxHeight(contentBorderPane.getMinHeight() + contentLayout.getMaxHeight() + 2);
stage.setMinWidth(contentBorderPane.getMinWidth() + 4);
stage.setMinHeight(contentBorderPane.getMinHeight() + 4);
stage.setMaxWidth(contentBorderPane.getMaxWidth() + 4);
stage.setMaxHeight(contentBorderPane.getMaxHeight() + 4);
} catch (NullPointerException e) {
System.out.print("error report:\n");
if (contentLayout == null)
System.out.print("WindowFrame:setApplicationContent: null \n");
}
if (stage.isResizable()) {
ResizeHelper.install(stage, 28, 7, 0);
} else {
System.out.println("ResizeHelper not set, stage not resizable.");
}
}
Add the CSS-Style to Border Pane将 CSS 样式添加到边框窗格
.window{
-fx-effect: dropshadow(three-pass-box, rgb(0,0,0,0.95), 2, 0.6, 0, 1);
}
Undecorator 似乎是唯一的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.