Allow user to resize an undecorated Stage

后端 未结 12 1504
悲&欢浪女
悲&欢浪女 2020-12-24 02:21

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.

I m

12条回答
  •  自闭症患者
    2020-12-24 03:20

    Thanks @Alexander.Berg for providing this utility class (1st version). It helped me to get the desired functionality for my Undecorated Stage.

    However, I have few queries regarding some points:

    1. What is need to traverse through all the child nodes and set the handler to each and every parent node. Is is not sufficient to put the handler on Scene only?
    2. If we are anyway checking for the EventType in handler, what is need in setting the same handler to different event types. Can it be set on one super mouse event (MouseEvent.ANY) and can skip the rest.
    3. If point#1 is handled, then there is no need to consider MOUSE_EXITED and MOUSE_EXITED_TARGET to set the default cursor back.
    4. There is every possible chance to trigger the event on underlying node(s) in the drag space(border space) if the handler is added as event handler. The most common case can be if the custom stage close button is very near to corner which comes under the border space. Trying to resize at that space will eventually trigger the close button. Should it need to be implement by adding it as filter (and consume the event when neccessary) rather than handler?

    I tried to modify your code to address all the above queries. Please find below the updated 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 {
    
            /** 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 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);
        }
    }
    

提交回复
热议问题