Saturday, October 17, 2015

Coordinate conversion in JavaFX 8: local, parent and scene

Here is the code:

package sample;

import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;

public class CoordinateConversion extends Application {
    private Circle marker;
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        TextField fn = new TextField();
        TextField ln = new TextField();
        TextField sal = new TextField();
        marker = new Circle(5);
        marker.setManaged(false);
        marker.setFill(Color.RED);
        marker.setMouseTransparent(true);
        GridPane gp = new GridPane();
        gp.add(new Label("First name"), 1, 1);
        gp.add(new Label("Last name"), 1, 2);
        gp.add(new Label("Salary"), 1, 3);
        gp.add(fn, 2, 1);
        gp.add(ln, 2, 2);
        gp.add(sal, 2, 3);
        gp.setHgap(10);
        gp.setVgap(10);
        Label placeHolder = new Label("Place holder");
        Label placeHolder1 = new Label("Place holder");
        Label placeHolder2 = new Label("Place holder");
        GridPane gpOuter = new GridPane();
        gpOuter.add(placeHolder, 1, 1);
        gpOuter.add(placeHolder1, 1, 2);
        gpOuter.add(placeHolder2, 2, 1);
        gpOuter.add(gp, 2, 2);
        gp.add(marker, 2, 4);
        gpOuter.setHgap(10);
        gpOuter.setVgap(10);
        Scene scene = new Scene(gpOuter, 500, 500);
        scene.focusOwnerProperty().addListener(
          (prop, oldNode, newNode) -> placeMarker(newNode)
        );
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void placeMarker(Node newNode) {
        System.out.println(newNode);
        double nodeMinX = newNode.getLayoutBounds().getMinX();
        double nodeMinY = newNode.getLayoutBounds().getMinY();
        System.out.println("\n\n======================");
        System.out.format("\nnodeMinx: %f, nodeMiny: %f", nodeMinX, nodeMinY);
        Point2D nodeInScene = newNode.localToScene(nodeMinX, nodeMinY);
        System.out.format("\nnis minx: %f, nis miny: %f",
          nodeInScene.getX(), nodeInScene.getY());
        Point2D nodeInMarkerLocal = marker.sceneToLocal(nodeInScene);
        System.out.format("\nniml x: %f, niml y: %f",
          nodeInMarkerLocal.getX(), nodeInMarkerLocal.getY());
        Point2D nodeInMarkerParent = marker.localToParent(nodeInMarkerLocal);
        System.out.format("\nnimp x: %f, nimp y: %f",
          nodeInMarkerParent.getX(), nodeInMarkerParent.getY()
          );

        marker.relocate(
          nodeInMarkerParent.getX() +
            marker.getLayoutBounds().getMinX(),
          nodeInMarkerParent.getY() +
            marker.getLayoutBounds().getMinY()
        );
    }
}

This application draws a red circle at the top left corner of the text field in focus. The circle is a child of the gp grid pane. Try put it in the gpOuter grid pane instead, this might help you understand the differences between local, parent and scene coordinates.

1 comments:

Oliver Maurice said...

Look at this blog for some helpful info about parent monitoring applications