25

私はJavaFXでスクリーンレコーダーを作成することに取り組んでおり、スクリーンレコーダーで必須のユーティリティの1つは、ユーザーが記録する領域を定義できるようにすることです。

Stageドラッグして領域を定義できる装飾のない半透明をなんとか作成しclose、ユーザーが記録する領域を確認できるようにするボタンを追加しました。

さて、ユーザーがステージの端をドラッグしてステージのサイズを変更できるようにするにはどうすればよいでしょうか?

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");
    }
}  

画像:
ここに画像の説明を入力

4

13 に答える 13

42

その場合に役立つ ResizeHelper クラスを作成しました。

ResizeHelper.addResizeListener(yourStage);

ヘルパー クラス:

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);
                            }
                        }
                    }
                }

            }
        }
    }
}
于 2014-06-03T14:18:41.413 に答える
1

これは@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);
                }
            }
        }
    }
}
于 2018-05-06T11:05:45.920 に答える
1

Kotlin バージョン

ここでの多くの回答に触発されて、考えられるすべての必要な機能を備えた独自の実装を作成しました。

特徴

  • ドラッガビリティ

    • ウィンドウはドラッグ可能
    • カスタマイズ可能なインタラクション エリア (上、右、下、左)
    • ドラッグ領域にカーソルを合わせたときのカスタム カーソル
  • 全画面表示

    • ウィンドウをフルスクリーンに設定できます
    • ウィンドウは以前のサイズと位置に戻ります
    • カスタマイズ可能なインタラクション エリア (上、右、下、左)
  • サイズ変更可能

    • ウィンドウは、任意の方向 (n、ne、e、se、s、se、w、nw) のウィンドウ境界でサイズ変更できます。
    • カスタマイズ可能なインタラクション エリア (n、e、w、s)
    • ウィンドウの境界をダブルクリックすると、ウィンドウのサイズが変更され、その方向に利用可能なすべてのスペースが占有されます
    • サイズ変更領域にカーソルを合わせたときのカスタム カーソル

使用法

クイックセットアップ:

StageInteractor(this)
    .makeDraggable()
    .makeFullscreenable()
    .makeResizable()

カスタム インタラクション エリア:

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;
    }
}
于 2018-08-10T11:34:03.643 に答える
0

このスレッドが古いことは知っていますが、新しい人がこの問題に直面していると思います。上記のコードでは真の方法は実現できませんでした。上記の解決策を 2 日間ほど試してみましたが、完全にドラッグ可能でサイズ変更可能なウィンドウを期待できませんでした。今まで。

https://github.com/goxr3plus/FX-BorderlessScene

このライブラリは私の正気を救い、3 行のコードを使用してシーンに適切な動作を与えることができました。

于 2020-08-28T13:08:05.410 に答える
0

サポート:

  1. ステージのサイズ変更、最小および最大境界に制限あり
  2. ステージの再配置、画面境界で制限あり
  3. ダブルクリック、片側ストレッチ
  4. カーソルは javafx システムによって自動的に設定されるため、グラフィカル カーソルのバグが発生しにくい
  5. CSS ドロップ シャドウに使用できるインデントのサポート
  6. 複雑なネストされたクエリがないため、クリーンで短く、高速で、読みやすく、適応可能なソースコードであるため、スマートなクリッピング方法
  7. Handler を深く登録する必要はなく、ノードはルートからカーソルを継承し、MouseEvent.MOUSE_MOVED を不必要に使用する必要はありません。

ビルドコンセプト:

Alignment.TOP|RIGHT|BOTTOM|LEFTコーナーにとを使用しAlignment.TOP_LEFT|BOTTOM_LEFTて、ルートの StackPane 内に Border Resize Panes を配置します。

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

最小サイズと最大サイズの値をステージに合わせることを忘れないでください。

            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.");
        }
                }

ボーダー ペインに CSS スタイルを追加する

.window{
    -fx-effect: dropshadow(three-pass-box, rgb(0,0,0,0.95), 2, 0.6, 0, 1);
    }
于 2021-02-28T00:27:34.817 に答える