JavaFx rotate text - java

Hi I want to rotate text (-40)/ And I Have problem when a text is short this start on diffrent hegh I Want to that text start when I mark. This punkt I do like this
for(int i=0; i<=Config.busSizeInView; i++){
gc.drawImage(busStop, 10+i*Config.xSize/Config.busSizeInView, Config.ySize -100, 20, 20);
}
I do this
for(int i =0 ;i<Config.busSizeInView;i++){
nameBusStop.add(new Text("Konstytucji 3 Maja - Dworzec PKS 02"));
}
for(int i=0 ; i<nameBusStop.size(); i++){
nameBusStop.get(i).setRotate(-40);
nameBusStop.get(i).setText(Main3.listBusStops.get(i).getName());
}
for(int i =0 ; i<nameBusStop.size(); i++){
nameBusStop.get(i).relocate(i*Config.xSize/Config.busSizeInView-Config.padding-10, Config.ySize - Config.ySize/6 - Config.padding*3);
}
line.getChildren().addAll(canvas,txtPane);
Pane txtPane = new Pane();
for(Text text : nameBusStop){
text.setFont(Font.font ("Verdana", 20));
txtPane.getChildren().add(text);
}
line.getChildren().addAll(canvas,txtPane);
When a text is longer

You could use the Canvas to draw the strings too:
public static void drawStop(double x, double y, String text, GraphicsContext gc) {
gc.save();
gc.translate(x, y);
gc.fillRect(-5, 0, 10, 10);
gc.rotate(-40);
gc.fillText(text, 5, 0);
gc.restore();
}
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas(900, 400);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFont(Font.font ("Verdana", 20));
drawStop(100, 380, "Stop 1", gc);
drawStop(200, 380, "Stop 2", gc);
drawStop(500, 380, "Stop 3 Stop 3 Stop 3 Stop 3 Stop 3 Stop 3", gc);
Scene scene = new Scene(new Group(canvas));
primaryStage.setScene(scene);
primaryStage.show();
}
Alternatively don't use a StackPane which centers the children. Also don't use the rotate property, since it rotates around the center of a Node. Use a Rotate transform instead to rotate around (0, 0):
public static void drawStop(double x, double y, String text, GraphicsContext gc, Pane pane) {
gc.fillRect(x-5, y, 10, 10);
Text textNode = new Text(text);
textNode.setFont(Font.font ("Verdana", 20));
textNode.setBoundsType(TextBoundsType.VISUAL);
textNode.relocate(x, y-15);
textNode.getTransforms().add(new Rotate(-40));
pane.getChildren().add(textNode);
}
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
Canvas canvas = new Canvas(900, 400);
pane.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
drawStop(100, 380, "Stop 1", gc, pane);
drawStop(200, 380, "Stop 2", gc, pane);
drawStop(500, 380, "Stop 3 Stop 3 Stop 3 Stop 3 Stop 3 Stop 3", gc, pane);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}

Related

Misalignment of two synced ScrollPanes

I try to align the vertical scroll position of two javafx.scene.control.ScrollPanes via
sp1.vvalueProperty().bindBidirectional(sp2.vvalueProperty());
The problem is that one of these ScrollPanes may have a horizontal scroll bar. So the more I scroll down the ScrollPanes, the more they get misaligned (see screenshot). How can I handle this?
It's impossible to do this with 2 ScrollPanes and contents of equal height unless you display the scrollbar in both ScrollPanes:
Consider the case where the content fits the viewport of the left ScrollPane exactly. The viewPort of the right ScrollPane can be scrolled by the ScrollBar height. Modifying the left ScrollPane is not possible.
Since the expected result seems to be some kind of scale, you could simply use a Pane with a child you apply transformY to and a clip. The formula to calculate the pixel to be placed at the top, use
top = vvalue * (contentHeight - viewportHeight)
Example
private static Label createLabel(int num, boolean mark) {
Label label = new Label(Integer.toString(num));
label.setPrefSize(50, 50);
label.setMaxSize(Double.MAX_VALUE, Region.USE_PREF_SIZE);
label.setStyle(mark ? "-fx-background-color: #FFFFFF" : "-fx-background-color: #BBBBBB;");
return label;
}
#Override
public void start(Stage primaryStage) throws Exception {
VBox scale = new VBox();
scale.setMinHeight(Region.USE_PREF_SIZE);
GridPane content = new GridPane();
for (int i = 0; i < 40; i++) {
boolean b = ((i % 2) == 0);
scale.getChildren().add(createLabel(i, !b));
for (int j = 0; j < 10; j++) {
content.add(createLabel(i * 10 + j, b), j, i);
}
}
AnchorPane scaleContainer = new AnchorPane(scale);
scaleContainer.setMinWidth(30);
scaleContainer.setMinHeight(0);
AnchorPane.setLeftAnchor(scale, 0d);
AnchorPane.setRightAnchor(scale, 0d);
Rectangle clip = new Rectangle();
scaleContainer.setClip(clip);
clip.widthProperty().bind(scaleContainer.widthProperty());
clip.heightProperty().bind(scaleContainer.heightProperty());
ScrollPane scroll = new ScrollPane(content);
scale.translateYProperty().bind(Bindings.createDoubleBinding(() -> {
double contentHeight = content.getHeight();
double viewportHeight = scroll.getViewportBounds().getHeight();
if (contentHeight <= viewportHeight) {
return 0d;
} else {
return -scroll.getVvalue() * (contentHeight - viewportHeight);
}
}, scroll.viewportBoundsProperty(), scroll.vvalueProperty(), content.heightProperty()));
HBox root = new HBox(scaleContainer, scroll);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}

Increase the hitbox of Circle in JavaFX

I'm creating circles of size 5 with something like Circle c = new Circle(x, y, 5);. Then I do c.setOnMousePressed(mousePressedEventHandler); but I've got a problem here : my circle is too small and it's easy to miss it. I would like to keep this size so is there a way to increase the hitbox of a circle without doing something like creating an invisible circle bigger and then set the listner on it ?
Increase the hit area by adding a transparent stroke to the circles.
Note: To actually use hit boxes you need to set the pickOnBounds property to true.
private static Circle createCircle(double x, double y, double radius, double hitRadius) {
Circle circle = new Circle(x, y, radius, Color.BLACK);
circle.setStrokeType(StrokeType.OUTSIDE);
circle.setStroke(Color.TRANSPARENT);
circle.setStrokeWidth((hitRadius < radius) ? 0 : (hitRadius - radius));
return circle;
}
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Circle circle1 = createCircle(100, 100, 5, 20);
Circle circle2 = createCircle(150, 150, 5, 20);
circle1.setOnMouseClicked(evt -> System.out.println("clicked 1"));
circle2.setOnMouseClicked(evt -> System.out.println("clicked 2"));
root.getChildren().addAll(
circle1,
circle2
);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}

How to add text to each face of a box [JavaFX]

I create a box which I can rotate and what will do ~some action~ when clicked. The problem I'm having is display text on all the faces of this box, for example;
1 on the front, 2 on the top, 3 on the back, 4 on the bottom, 5 on the left and 6 on the right.
I understand that StackPane can be used to overlay a text box on-top of the cube but I don't think that'd really help in this scenario.
Since box is essentially a pre-constructed TriangleMesh, is this possible to do?
As far as seen, box doesn't have any in-built functionality to do this.
static double mousePosX;
static double mousePosY;
static double mouseOldX;
static double mouseOldY;
public static Scene testScene(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root, stage.getWidth(), stage.getHeight(), true, SceneAntialiasing.BALANCED);
scene.setFill(Paint.valueOf("Blue"));
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);
camera.setFarClip(10000.0);
camera.setTranslateZ(-10);
scene.setCamera(camera);
Box box = new Box(1,1,1);
box.setOnMouseClicked(e -> {
System.out.println("Test");
});
Rotate rotateX = new Rotate(10, 0, 0, 0, Rotate.X_AXIS);
Rotate rotateY = new Rotate(5, 0, 0, 0, Rotate.Y_AXIS);
box.getTransforms().addAll(rotateX, rotateY);
scene.setOnMousePressed(me -> {
mouseOldX = me.getSceneX();
mouseOldY = me.getSceneY();
});
scene.setOnMouseDragged(me -> {
mousePosX = me.getSceneX();
mousePosY = me.getSceneY();
rotateX.setAngle(rotateX.getAngle() - (mousePosY - mouseOldY));
rotateY.setAngle(rotateY.getAngle() + (mousePosX - mouseOldX));
mouseOldX = mousePosX;
mouseOldY = mousePosY;
});
root.getChildren().add(box);
return scene;
}
This is the code I've got so far and any assistance would be greatly appreciated.
This solution is based in the answer to this question, where the CuboidMesh from the FXyz library is used.
The main idea is to use an image as texture for the cube. The built-in JavaFX Box will apply this image to each of the 6 faces, so if we want to have different text in each face, we have to use the CuboidMesh, that makes use of the net image:
The cube can be generated as:
CuboidMesh cuboid = new CuboidMesh(100f, 100f, 100f);
cuboid.setTextureModeImage(getClass().getResource("net.png").toExternalForm());
The idea now is to write the text in each of the 6 faces and save the texture image that will be used later on.
This method will generate this net image:
private Image generateNet(String face1, String face2, String face3, String face4, String face5, String face6) {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
Label label1 = new Label(face1);
label1.setRotate(90);
GridPane.setHalignment(label1, HPos.CENTER);
Label label2 = new Label(face2);
GridPane.setHalignment(label2, HPos.CENTER);
Label label3 = new Label(face3);
GridPane.setHalignment(label3, HPos.CENTER);
Label label4 = new Label(face4);
GridPane.setHalignment(label4, HPos.CENTER);
Label label5 = new Label(face5);
GridPane.setHalignment(label5, HPos.CENTER);
Label label6 = new Label(face6);
label6.setRotate(90);
GridPane.setHalignment(label6, HPos.CENTER);
grid.add(label1, 1, 0);
grid.add(label2, 0, 1);
grid.add(label3, 1, 1);
grid.add(label4, 2, 1);
grid.add(label5, 3, 1);
grid.add(label6, 1, 2);
grid.setGridLinesVisible(true);
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(25);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(25);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(25);
ColumnConstraints col4 = new ColumnConstraints();
col4.setPercentWidth(25);
grid.getColumnConstraints().addAll(col1, col2, col3, col4);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(33.33);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(33.33);
RowConstraints row3 = new RowConstraints();
row3.setPercentHeight(33.33);
grid.getRowConstraints().addAll(row1, row2, row3);
grid.setPrefSize(600, 450);
Scene tmpScene = new Scene(grid);
tmpScene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
return grid.snapshot(null, null);
}
where style.css contains:
.root {
-fx-background-color: white;
}
.label {
-fx-font-size: 6em;
}
With it, the labels size and font can be adjusted properly.
Now you can generate a net image for any text:
Image net = generateNet("1", "2", "3", "4", "5", "6");
Finally, you can apply this texture to the cuboid:
PhongMaterial mat = new PhongMaterial();
mat.setDiffuseMap(net);
cuboid.setMaterial(mat);
And you will have your text applied:

JavaFx path transition in gridPane

I can not understand how moveTo method work in javafx. here is my example
I have a GridPane that consist of 4 columns and 1 row. All the columns h has a StackPane and the last Stackpane also has an empty label.
public class PathTransitionTExample extends Application {
StackPane pane;
GridPane grid;
Label label;
Scene scene;
#Override
public void start(Stage primaryStage) {
label = new Label();
label.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
grid = new GridPane();
grid.getStyleClass().add("gridPane");
for (int x = 0; x < 4; x++) {
grid.add(pane = new StackPane(), x, 0);
pane.setPrefSize(50, 50);
pane.getStyleClass().add("stackPane");
}
pane = (StackPane) grid.getChildren().get(3);
pane.getChildren().add(label);
scene = new Scene(grid, 260, 50);
scene.getStylesheets().add(PathTransitionTest.class.getResource("pathCSS.css").toExternalForm());
scene.setOnMouseClicked(me -> pathTransition());
primaryStage.setTitle("Path Transition");
primaryStage.setScene(scene);
primaryStage.show();
}
//pathCSS.css
.gridPane{
-fx-background-color: red;
-fx-hGap: 20;
}
.stackPane{
-fx-background-color: orange;
}
.label {
-fx-background-color: blue;
}
I want to move the label in the grid pane from 0.3 to 0.0 however when I am trying to do that the whole transition slips.
private void pathTransition() {
//the Coordinates of the label
double oldMinX = grid.getChildren().get(3).getLayoutX();
double oldMinY = grid.getChildren().get(3).getLayoutY();
//the coordinates of the stack pane where i want the label move
double newMinX = grid.getChildren().get(1).getLayoutX();
double newMinY = grid.getChildren().get(1).getLayoutY();
Path path = new Path();
path.getElements().add(new MoveTo(oldMinX, oldMinY ));
path.getElements().add(new LineTo(newMinX,newMinY ));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(new Duration(500));
pathTransition.setPath(path);
pathTransition.setNode(label);
pathTransition.play();
}
If I change the arguments of MoveTo and LineTo by the following I can achieve the animation I want but I cant understand why. :\
double oldMinX = grid.getChildren().get(3).getLayoutX() -185;
double oldMinY = grid.getChildren().get(3).getLayoutY()+ grid.getChildren().get(3).getBoundsInLocal().getHeight()/2 ;
double newMinX = grid.getChildren().get(1).getLayoutX()-255 ;
double newMinY = grid.getChildren().get(1).getLayoutY() + grid.getChildren().get(0).getBoundsInLocal().getHeight()/2 ;
I guess It is because transitions use different coordinate systems than scenes but I cant really find anything that explains well :( Could someone give me some hints how It is working?
Thank you so much in advance.
I realized that i shouldn't use GridPane. If i do it without using containers the transition is working fine.

JavaFX Button isn't clickable

So I have this button. It can be pressed with the enter key, and can be selected through tabbing to it, but it cannot be clicked on, and doesn't appear to exist as far as the cursor is concerned. I don't believe anything is covering it, but it most be covered some how, as a button created with identical code is clickable.
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Mortgage Calculator");
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(10);
pane.setVgap(10);
pane.setPadding(new Insets(25, 25, 25, 25));
Scene scene = new Scene(pane, 350, 325);
Text sceneTitle = new Text("Mortgage Calculator");
sceneTitle.setFont(Font.font("Arial", FontWeight.NORMAL, 20));
pane.add(sceneTitle, 0, 0, 2, 1);
Label total = new Label("Value of your Mortgage:");
pane.add(total, 0, 1);
final TextField totalField = new TextField();
pane.add(totalField, 1, 1);
Label percent = new Label("% Interest:");
pane.add(percent, 0, 2);
final TextField percentField = new TextField();
pane.add(percentField, 1, 2);
Label time = new Label("Length of mortgage:");
pane.add(time, 0, 3);
final TextField timeField = new TextField();
pane.add(timeField, 1, 3);
final Text amountOwed = new Text();
pane.add(amountOwed, 1, 7);
final Text payment = new Text();
pane.add(payment, 1, 8);
Button calculateButton = new Button("Calculate");
HBox hbox1 = new HBox(10);
hbox1.setAlignment(Pos.BOTTOM_RIGHT);
hbox1.getChildren().add(calculateButton);
calculateButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
double principal = Double.parseDouble(totalField.getText());
double interest = (Double.parseDouble(percentField.getText()) / 100) / 12;
int time = Integer.parseInt(timeField.getText());
int numPayment = time * 12;
double monthlyPayment = (principal * (interest * (Math.pow((interest + 1), numPayment)))) / (Math.pow((1 + interest), numPayment) - 1);
//double totalPayment = ;
//amountOwed.setText("Amount owed is: " + totalPayment);
payment.setText("Monthly payment is: $" + (int)Math.ceil(monthlyPayment));
}
});
pane.add(hbox1, 1, 4);
Button clearButton = new Button("Clear");
HBox hbox = new HBox(10);
hbox.setAlignment(Pos.BOTTOM_LEFT);
hbox.getChildren().add(clearButton);
pane.add(hbox, 1, 4);
EventHandler<ActionEvent> handler1 = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
totalField.setText("");
percentField.setText("");
timeField.setText("");
payment.setText("");
}
};
clearButton.setOnAction(handler1);
primaryStage.setScene(scene);
primaryStage.show();
}
}
mouseTransparent Issue
You put the "Calculate" button in a HBox and then you call setMouseTransparent(true) on the HBox. Such a call will disable mouse input on the HBox and all it's children. This behavior is documented in the linked Javadoc:
If true, this node (together with all its children) is completely transparent to mouse events. When choosing target for mouse event, nodes with mouseTransparent set to true and their subtrees won't be taken into account.
You don't need the setMouseTransparent(true) call; just remove it, and the "Calculate" button will be clickable as you expect.
Overlapping Components Issue
You also need to ensure that you don't overlap some components with others otherwise only the top components will be clickable. In your case, the HBox containing the "Clear" button is overlapping the "Calculate" button.
So change:
pane.add(hbox, 1, 4);
To:
pane.add(clearButton, 1, 4);
Debugging Assistance
You can debug layouts either by calling layout.setStyle("-fx-background-color: green;") to show the extent of the layout, or by using ScenicView.

Categories

Resources