I'm trying to make a custom border for a text field. I used the boundsInParentProperty
to make the border. However, these bounds behave like a bounding box which aren't suitable for my case. The following is a snapshot of the text field with the border:
The problems arise when the node is rotated, the following is another snapshot when the text field is rotated:
This is a sample code:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.Background;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;
import org.jetbrains.annotations.NotNull;
public class CustomBorder extends Application {
@Override
public void start(Stage primaryStage) {
TextField textField = new TextField("Hello, World!");
textField.setStyle("-fx-padding: 8; -fx-max-width: 20; -fx-font-size: 18;" +
"-fx-border-insets: 0; -fx-background-insets: 0; -fx-background-radius: 0; " +
"-fx-border-style: none; -fx-background-color: rgba(255,0,0,0.5); -fx-min-width: 140");
textField.setRotate(-25);
Path border = new Path();
border.getElements().addAll(createBorder(textField, BorderLine.LEFT));
border.getElements().addAll(createBorder(textField, BorderLine.TOP));
border.getElements().addAll(createBorder(textField, BorderLine.RIGHT));
border.getElements().addAll(createBorder(textField, BorderLine.BOTTOM));
border.setManaged(false);
border.setViewOrder(0);
HBox root = new HBox(textField, border);
root.setBackground(Background.fill(Color.TRANSPARENT));
root.getTransforms().add(new Scale(2, 2));
root.relocate(100, 100);
primaryStage.setTitle("Custom Border");
primaryStage.setScene(new Scene(root, 500, 400));
primaryStage.show();
root.requestFocus();
}
@NotNull
private PathElement[] createBorder(Region region, @NotNull BorderLine borderLine) {
MoveTo moveTo = new MoveTo();
LineTo lineTo = new LineTo();
switch (borderLine) {
case LEFT -> region.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
moveTo.setX(newValue.getMinX());
moveTo.setY(newValue.getMinY());
lineTo.setX(newValue.getMinX());
lineTo.setY(newValue.getMaxY());
});
case TOP -> region.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
moveTo.setX(newValue.getMinX());
moveTo.setY(newValue.getMinY());
lineTo.setX(newValue.getMaxX());
lineTo.setY(newValue.getMinY());
});
case RIGHT -> region.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
moveTo.setX(newValue.getMaxX());
moveTo.setY(newValue.getMinY());
lineTo.setX(newValue.getMaxX());
lineTo.setY(newValue.getMaxY());
});
case BOTTOM -> region.boundsInParentProperty().addListener((observable, oldValue, newValue) -> {
moveTo.setX(newValue.getMinX());
moveTo.setY(newValue.getMaxY());
lineTo.setX(newValue.getMaxX());
lineTo.setY(newValue.getMaxY());
});
}
return new PathElement[]{moveTo, lineTo};
}
public enum BorderLine {
LEFT, RIGHT, TOP, BOTTOM;
}
public static void main(String[] args) {
launch(args);
}
}
How can I make the border have the correct x/y values?
Since you're creating the text field and the border as two separate components, you need to rotate both of them. One way is to put them in a Group
and rotate the Group
(instead of the text field):
public void start(Stage primaryStage) {
TextField textField = new TextField("Hello, World!");
textField.setStyle("-fx-padding: 8; -fx-max-width: 20; -fx-font-size: 18;" +
"-fx-border-insets: 0; -fx-background-insets: 0; -fx-background-radius: 0; " +
"-fx-border-style: none; -fx-background-color: rgba(255,0,0,0.5); -fx-min-width: 140");
// textField.setRotate(-25);
Path border = new Path();
border.getElements().addAll(createBorder(textField, BorderLine.LEFT));
border.getElements().addAll(createBorder(textField, BorderLine.TOP));
border.getElements().addAll(createBorder(textField, BorderLine.RIGHT));
border.getElements().addAll(createBorder(textField, BorderLine.BOTTOM));
border.setManaged(false);
border.setViewOrder(0);
Group textBox = new Group(textField, border);
textBox.setRotate(-25);
// HBox root = new HBox(textField, border);
HBox root = new HBox(textBox);
root.setRotate(-25);
root.setBackground(Background.fill(Color.TRANSPARENT));
root.getTransforms().add(new Scale(2, 2));
root.relocate(100, 100);
primaryStage.setTitle("Custom Border");
primaryStage.setScene(new Scene(root, 500, 400));
primaryStage.show();
root.requestFocus();
}