Peter Penzov
Peter Penzov

Reputation: 1678

How to resize component with mouse drag in JavaFX

I'm interested how I can resize this component with mouse drag:

VBox stackedTitledPanes = createStackedTitledPanes();

    ScrollPane scroll = makeScrollable(stackedTitledPanes);

    TabPane tabPane = new TabPane();
    BorderPane mainPane = new BorderPane();

    tabPane.setStyle("-fx-font-size: 12pt;"); // Set global size for the font
    // Create Tabs
    Tab tabA = new Tab();
    tabA.setText("Main Component");
    tabA.setStyle("-fx-font-size: 12pt;"); // Set size of the tab name
    // Add something in Tab
    StackPane tabA_stack = new StackPane();
    tabA_stack.setAlignment(Pos.CENTER);
    tabA_stack.getChildren().add(scroll); 
    tabA.setContent(tabA_stack);
    tabPane.getTabs().add(tabA);

    Tab tabB = new Tab();
    tabB.setText("Second Component");
    tabB.setStyle("-fx-font-size: 12pt;"); // Set size of the tab name
    // Add something in Tab
    StackPane tabB_stack = new StackPane();
    tabB_stack.setAlignment(Pos.CENTER);
    tabB_stack.getChildren().add(new Label("Label@Tab B"));
    tabB.setContent(tabB_stack);
    tabPane.getTabs().add(tabB);

    Tab tabC = new Tab();
    tabC.setText("Last Component");
    tabC.setStyle("-fx-font-size: 12pt;"); // Set size of the tab name
    // Add something in Tab
    StackPane tabC_vBox = new StackPane();
    tabC_vBox.setAlignment(Pos.CENTER);
    tabC_vBox.getChildren().add(new Label("Label@Tab C"));
    tabC.setContent(tabC_vBox);
    tabPane.getTabs().add(tabC);

    mainPane.setCenter(tabPane);

    mainPane.setPrefSize(395, 580);
    mainPane.setLayoutX(850);
    mainPane.setLayoutY(32);

    scroll.setPrefSize(395, 580);
    scroll.setLayoutX(850);
    scroll.setLayoutY(32);

    root.getChildren().add(mainPane);

The problem is that I have several components placed on the main stage. When I resize one component for example increase the height of the component I have to reduce the size of the next component without stepping over the component. How I can do this?

Upvotes: 4

Views: 11179

Answers (3)

Nor.Z
Nor.Z

Reputation: 1349

to make a Node resizable by mouse drag (my thoughts)

logic

  1. a node_textArea_or_canvas that you want to resize by mouse drag
  2. a panel_Wrapper that wraps node_textArea_or_canvas (not strictly required)
  3. a handler_Resize that is also wrapped inside panel_Wrapper & is made make_Draggable()
  4. listen to the position of handler_Resize & drag it
         handler_Resize.layoutXProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
           node_textArea_or_canvas.setPrefWidth((double) newValue);
         });
    

sample code

package com.redfrog.note;

import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import com.google.common.util.concurrent.AtomicDouble;
import com.redfrog.note.TestAppJavafxBoot.StageReadyEvent;

import javafx.beans.value.ChangeListener;
import javafx.geometry.Orientation;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

@Component
public class T1 implements ApplicationListener<StageReadyEvent> {

  @Override
  public void onApplicationEvent(StageReadyEvent stageReadyEvent) {

    Stage primaryStage = stageReadyEvent.getStage();

    FlowPane root = new FlowPane(Orientation.VERTICAL);
    root.setMinSize(800, 800);
    root.setStyle(""
                  + "-fx-border-style: solid;"
                  + "-fx-border-width: 1;"
                  + "-fx-border-color: teal;");

    AnchorPane panel_Wrapper = new AnchorPane();
    panel_Wrapper.setStyle(""
                           + "-fx-border-style: solid;"
                           + "-fx-border-width: 1;"
                           + "-fx-border-color: grey;");

    TextArea node_textArea_or_canvas = new TextArea();
    node_textArea_or_canvas.setPrefSize(300, 80);
    node_textArea_or_canvas.appendText("(- After one button is clicked, then dont touch/click on any button anymore.)");
    node_textArea_or_canvas.setWrapText(true);

    //    Canvas node_textArea_or_canvas = new Canvas();
    //    node_textArea_or_canvas.setWidth(300);
    //    node_textArea_or_canvas.setHeight(80);
    //
    //    GraphicsContext graphicsContext = node_textArea_or_canvas.getGraphicsContext2D();
    //    graphicsContext.beginPath();
    //    graphicsContext.moveTo(20, 30);
    //    graphicsContext.lineTo(180, 260);
    //    graphicsContext.stroke();

    panel_Wrapper.getChildren().add(node_textArea_or_canvas);
    setup_Resizable(node_textArea_or_canvas, panel_Wrapper);

    root.getChildren().add(panel_Wrapper);

    primaryStage.setTitle("DrawAndChatApp - Session init Page");
    primaryStage.setScene(new Scene(root, 650, 450));
    primaryStage.show();
  }

  public void setup_Resizable(final /*Node*/ TextArea node_textArea_or_canvas, Pane panel_Wrapper) {
    AnchorPane handler_Resize = new AnchorPane();
    handler_Resize.setStyle(""
                            + "-fx-border-style: solid;"
                            + "-fx-border-width: 1;"
                            + "-fx-border-color: grey;"); // let transparent ... 

    handler_Resize.setPrefSize(10, 10);
    make_Draggable(handler_Resize, true);
    //    handler_Resize.setLayoutX(panel_Wrapper.getWidth());
    //    handler_Resize.setLayoutY(panel_Wrapper.getHeight()); // wont work cuz 0 // thought layer adding no, just 0 

    panel_Wrapper.getChildren().add(handler_Resize); // @note: layer pb ..? 

    handler_Resize.layoutXProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
      node_textArea_or_canvas.setPrefWidth((double) newValue);
      //      node_textArea_or_canvas.setWidth((double) newValue);

    });
    handler_Resize.layoutYProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
      node_textArea_or_canvas.setPrefHeight((double) newValue);
      //      node_textArea_or_canvas.setHeight((double) newValue);
    });

    //    // can do sth like: listen to key event confirm then execute ... nah too much trouble // even undo is a thing ...  
    //    // saying the conflict of event.comuse() too ... @pb ..
    //    Class<? extends Node> clazz = node_textArea_or_canvas.getClass();
    //    if (Region.class.isAssignableFrom(clazz)) {
    //      Region textArea = (Region) node_textArea_or_canvas;
    //      handler_Resize.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
    //        if (!event.isConsumed()) {
    //          if (event.isControlDown() && event.isAltDown() && !event.isShiftDown() && event.isSecondaryButtonDown()) {
    //            event.consume();
    //            textArea.setPrefWidth(handler_Resize.getLayoutX());
    //            textArea.setPrefHeight(handler_Resize.getLayoutY());
    //          }
    //        }
    //      });
    //    }
    //    else if (Canvas.class.isAssignableFrom(clazz)) {
    //      Canvas canvas = (Canvas) node_textArea_or_canvas;
    //      handler_Resize.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
    //        if (!event.isConsumed()) {
    //          if (event.isControlDown() && event.isAltDown() && !event.isShiftDown() && event.isSecondaryButtonDown()) {
    //            event.consume();
    //            canvas.setWidth(handler_Resize.getLayoutX());
    //            canvas.setHeight(handler_Resize.getLayoutY());
    //          }
    //        }
    //      });
    //    }
    //    else {
    //      throw new Error();
    //    }

  }

  public static void make_Draggable(final Node node, boolean det_PreventMovePassLeft) {
    final AtomicDouble posX_offset = new AtomicDouble();
    final AtomicDouble posY_offset = new AtomicDouble();

    final AtomicDouble posX_ori = new AtomicDouble();
    final AtomicDouble posY_ori = new AtomicDouble();

    node.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
      if (!event.isConsumed()) {
        if (event.isControlDown() && event.isAltDown() && !event.isShiftDown() && event.isPrimaryButtonDown()) {
          event.consume();

          double x = event.getSceneX();
          double y = event.getSceneY();
          posX_offset.set(node.getLayoutX() - x);
          posY_offset.set(node.getLayoutY() - y);
          node.setCursor(Cursor.HAND);

          posX_ori.set(node.getLayoutX());
          posY_ori.set(node.getLayoutY());
        }
      }
    });
    node.addEventHandler(MouseEvent.MOUSE_DRAGGED, event -> {
      if (!event.isConsumed()) {
        if (event.isControlDown() && event.isAltDown() && !event.isShiftDown() && event.isPrimaryButtonDown()) {
          event.consume();
          double x = event.getSceneX() + posX_offset.get();
          double y = event.getSceneY() + posY_offset.get();

          if (det_PreventMovePassLeft) {
            if (x < 0) { x = 0; }
            if (y < 0) { y = 0; }
          }

          node.setLayoutX(x);
          node.setLayoutY(y);
          node.setCursor(Cursor.MOVE);
        }
      }
    });

  }

}

misc note

Upvotes: 0

user12551103
user12551103

Reputation: 1

Here is a modified version for the case of an upper and lower Region. If one Region grows the other one gets smaller.

public class DragResizer {

    /**
     * The margin around the control that a user can click in to start resizing
     * the region.
     */
    private static final int RESIZE_MARGIN = 5;

    private final Region top, bottom;

    private double y;

    private boolean initMinHeight;

    private boolean dragging;

    private double ySum;

    private DragResizer(Region top, Region bottom) {
        this.top = top;
        this.bottom = bottom;
        ySum = top.getPrefHeight() + bottom.getPrefHeight();
    }

    public static void makeResizable(Region top, Region bottom) {
        final DragResizer resizer = new DragResizer(top, bottom);

        top.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mousePressed(event);
            }});
        top.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseDragged(event);
            }});
        top.setOnMouseMoved(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseOver(event);
            }});
        top.setOnMouseReleased(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseReleased(event);
            }});
    }

    protected void mouseReleased(MouseEvent event) {
        dragging = false;
        top.setCursor(Cursor.DEFAULT);
    }

    protected void mouseOver(MouseEvent event) {
        if(isInDraggableZone(event) || dragging) {
            top.setCursor(Cursor.S_RESIZE);
        }
        else {
            top.setCursor(Cursor.DEFAULT);
        }
    }

    protected boolean isInDraggableZone(MouseEvent event) {
        return event.getY() > (top.getHeight() - RESIZE_MARGIN);
    }

    protected void mouseDragged(MouseEvent event) {
        if(!dragging) {
            return;
        }

        double mousey = event.getY();

        double dy = (mousey - y);

        if(top.getMinHeight() + dy >= ySum || bottom.getPrefHeight() - dy >= ySum)
            return;

        top.setMinHeight(top.getMinHeight() + dy);
        top.setPrefHeight(top.getPrefHeight() + dy);

        bottom.setMinHeight(bottom.getMinHeight() - dy);
        bottom.setPrefHeight(bottom.getPrefHeight() - dy);

        y = mousey;
    }

    protected void mousePressed(MouseEvent event) {

        // ignore clicks outside of the draggable margin
        if(!isInDraggableZone(event)) {
            return;
        }

        dragging = true;

        // make sure that the minimum height is set to the current height once,
        // setting a min height that is smaller than the current height will
        // have no effect
        if (!initMinHeight) {
            top.setMinHeight(top.getHeight());
            initMinHeight = true;
        }

        y = event.getY();
    }
}

Upvotes: 0

Andy Till
Andy Till

Reputation: 3511

I think it is stack pane that is causing the problem, it does not support resize behaviour like VBox or HBox does. I am dragging in my own program to resize TextArea or List components to make them larger if necessary. Check out the code here:

https://bitbucket.org/atill/estimate/src/22390a2ca034b55f1916e46435b714e5c489b90e/src/main/java/projmon/ui/DragResizer.java?at=master

and usage:

https://bitbucket.org/atill/estimate/src/22390a2ca034b55f1916e46435b714e5c489b90e/src/main/java/projmon/control/TaskFormController.java?at=master

EDIT I have pulled EstiMate but the drag resizer is still open source in this gist.

https://gist.github.com/andytill/4369729

Or the full code.

import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;

/**
 * {@link DragResizer} can be used to add mouse listeners to a {@link Region}
 * and make it resizable by the user by clicking and dragging the border in the
 * same way as a window.
 * <p>
 * Only height resizing is currently implemented. Usage: <pre>DragResizer.makeResizable(myAnchorPane);</pre>
 * 
 * @author atill
 * 
 */
public class DragResizer {

    /**
     * The margin around the control that a user can click in to start resizing
     * the region.
     */
    private static final int RESIZE_MARGIN = 5;

    private final Region region;

    private double y;

    private boolean initMinHeight;

    private boolean dragging;

    private DragResizer(Region aRegion) {
        region = aRegion;
    }

    public static void makeResizable(Region region) {
        final DragResizer resizer = new DragResizer(region);

        region.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mousePressed(event);
            }});
        region.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseDragged(event);
            }});
        region.setOnMouseMoved(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseOver(event);
            }});
        region.setOnMouseReleased(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                resizer.mouseReleased(event);
            }});
    }

    protected void mouseReleased(MouseEvent event) {
        dragging = false;
        region.setCursor(Cursor.DEFAULT);
    }

    protected void mouseOver(MouseEvent event) {
        if(isInDraggableZone(event) || dragging) {
            region.setCursor(Cursor.S_RESIZE);
        }
        else {
            region.setCursor(Cursor.DEFAULT);
        }
    }

    protected boolean isInDraggableZone(MouseEvent event) {
        return event.getY() > (region.getHeight() - RESIZE_MARGIN);
    }

    protected void mouseDragged(MouseEvent event) {
        if(!dragging) {
            return;
        }

        double mousey = event.getY();

        double newHeight = region.getMinHeight() + (mousey - y);

        region.setMinHeight(newHeight);

        y = mousey;
    }

    protected void mousePressed(MouseEvent event) {

        // ignore clicks outside of the draggable margin
        if(!isInDraggableZone(event)) {
            return;
        }

        dragging = true;

        // make sure that the minimum height is set to the current height once,
        // setting a min height that is smaller than the current height will
        // have no effect
        if (!initMinHeight) {
            region.setMinHeight(region.getHeight());
            initMinHeight = true;
        }

        y = event.getY();
    }
}

Upvotes: 5

Related Questions