Creating a large body of text with different styles - JavaFX FXML - java

In the fxml class of my JavaFx application I want to add a large body of text using minimal components (instead of adding multiple labels per line). I would also like to create varying styles of text in the same component. What component should I use (e.g TextArea) and how would I go about create multiple styles inside it (using css).

Use a TextFlow and add Text to it. You can style individual Text component with different style using css on them.
Complete example :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class TextFlowExample extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Text text1 = new Text("First Text\n");
text1.setStyle("-fx-font-size: 20; -fx-fill: darkred;");
Text text2 = new Text("\nSecond Text");
text2.setStyle("-fx-font-size: 30; -fx-fill: goldenrod;");
TextFlow textFlow = new TextFlow(text1, text2);
primaryStage.setScene(new Scene(textFlow, 200, 200));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Output
Equivalent FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import java.lang.*?>
<?import javafx.scene.text.*?>
<TextFlow lineSpacing="10.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" textAlignment="CENTER" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" style="-fx-font-size: 20; -fx-fill: darkred;" text="
First Text" />
<Text strokeType="OUTSIDE" strokeWidth="0.0" style="-fx-font-size: 30; -fx-fill: goldenrod;" text="
Second Text" />
</children>
</TextFlow>

Related

JavaFX: How to make pane always a square by binding its width and height properties together

I'm looking for a way to make a JavaFX pane always square no matter how the windows it's in is resized. The size should be determined by whatever is smaller, height or width. So it uses the maximum available space in whichever direction is smaller.
I made a simple example here:
This is the .fxml-File and I want paneInnerPane to be square at all times.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.MyController">
<center>
<VBox fx:id="vBoxCenter" BorderPane.alignment="CENTER">
<children>
<StackPane fx:id="stackPaneOuterPane" minHeight="800.0" minWidth="800.0">
<children>
<Pane fx:id="paneinnerPane" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: red;" />
</children>
</StackPane>
<ButtonBar prefHeight="40.0" prefWidth="200.0">
<buttons>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</buttons>
</ButtonBar>
</children>
</VBox>
</center>
</BorderPane>
This is the Controller and the main class:
public class MyController {
public VBox vBoxCenter;
public StackPane stackPaneOuterPane;
public Pane paneinnerPane;
#FXML
public void initialize() {
VBox.setVgrow(paneinnerPane, Priority.ALWAYS);
}
#FXML
private Label welcomeText;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
}
public class MyApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(MyApplication.class.getResource("myview.fxml"));
Scene scene = new Scene(fxmlLoader.load());
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I tried thinks like this:
paneinnerPane.prefHeightProperty().bind(paneinnerPane.widthProperty());
paneinnerPane.prefWidthProperty().bind(paneinnerPane.heightProperty());
(Also with minWidth and maxWidth)
But the result is always the same:
The red rectangle should be square.
What do I have to do to make a Pane always be a square?

How to place elements proportionally in the HBox/VBox using javafx/fxml

I try to place buttons and text evenly in the window. I use FXML for javafx project:
<VBox>
<HBox>
<TextField promptText="Text1" prefWidth="60" alignment="CENTER_LEFT">
<padding>
<Insets left="10" right="30"/>
</padding>
</TextField>
<Label text="Some text"/>
<TextField promptText="Text2" prefWidth="60" alignment="CENTER_RIGHT">
<padding>
<Insets left="30" right="10"/>
</padding>
</TextField>
</HBox>
</VBox>
I use padding, 'alignment' properties but nothing help, elements are offset:
How to correctly to place elements in window?
You need to:
Make sure the VBox is filling the width of the window
(your code snippet doesn’t indicate if it is in another container).
Make sure the HBox fills the width of the VBox
add fillWidth=“true” to the VBox element.
Let each component grow in the HBox
add HBox.hgrow = “ALWAYS” and maxWidth = “Infinity” to the text fields and label.
remove the padding and
insets, and existing preferred widths.
Here is an example:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.*?>
<VBox fillWidth="true" xmlns:fx="http://javafx.com/fxml">
<HBox>
<TextField promptText="Text1" maxWidth="Infinity" HBox.hgrow="ALWAYS"/>
<Label alignment="CENTER" text="Some text" maxWidth="Infinity" HBox.hgrow="ALWAYS"/>
<TextField promptText="Text2" maxWidth="Infinity" HBox.hgrow="ALWAYS"/>
</HBox>
</VBox>
and the application file:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 600, 400);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}

Images showing in scene builder, but not after compilation

I just want to display an image but no matter whati've tried, it's not showing up.
I opened up a new project from a default template to see if I had done something, but it won't work either.
It will show in the scene builder, but once i compile, nothing there.
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 400, 600);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
and the fxml file being:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fitHeight="325.0" fitWidth="347.0" layoutX="138.0" layoutY="38.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../../Images/471962_nemuli_catstronaut.png" />
</image>
</ImageView>
</children>
</AnchorPane>
Any clue what's happening? I am running on linux if that means anything.
file tree

Trouble applying CSS to programatically created Pane on MouseEvent

I have a stack pane that when the mouse enters, I create a pane, stick some text in it, and display it near the mouse.
StatisticsController.java
stackPane.setOnMouseEntered(event -> {
Pane pane = new Pane();
Text text = new Text("Example Text");
//Add the text to the pane and set it near the mouse
pane.getChildren().add(text);
pane.setLayoutY(event.getSceneY() - 50);
pane.setLayoutX(event.getSceneX() - 100);
//add the style class to give it a blueBG background
pane.getStyleClass().add("blueBG");
// add it too our root
getRoot().getChildren().add(pane);
});
As you can see below when the Pane and text appear when I roll over the stackPane (the black circle and question mark in the image).
However it doesn't have the blue background I'm looking for.
What I find strange is if instead of adding it to the root and I add it to an existing Vbox, it styles correctly (Here I roll over the same stackPane):
So:
//Styles Correctly
getInfoBox().getChildren().add(pane);
// Adds but does not style
getRoot().getChildren().add(pane);
Some things that may be worth noting:
1)getInfoBox() is a static getter. I have multiple controllers which extend a MasterController which has static instance variables to things all controllers would want to be able to access - like the infoBox, to display information.
2)The root is a BorderPane made in the Main class:
Main.java
//Create the root
BorderPane root = new BorderPane();
// I've cut it out to save space but this repeats 4 times to set
//an .fxml file for the top, left, right and bottom of the borderPane ///
FXMLLoader headerLoader = new FXMLLoader(getClass().getResource("/view/header.fxml"));
Parent headerRoot = headerLoader.load();
root.setTop(headerRoot);
//------------------------------------------//
//Set the static reference in the MasterController
MasterController.setRoot(root);
Scene scene = new Scene(root,1366,768);
primaryStage.setScene(scene);
primaryStage.show();
3) The css files are all declared in the .fxml files using Scene Builder. The top, bottom, left, right .fxml files all have the same css (main.css). So does the statistic.fxml which is loaded on button click into the center (you can see in the image)
Suggestion: Could it be because the CSS is not defined for the Scene or BorderPane itself, it only applies to nodes added into the borderPane sections? If so how would I go about letting the enter Scene/Stage use the same css and would that negate adding in the css to each .fxml?
Edit:
When adding this code to the main to apply the CSS to the scene:
Scene scene = new Scene(root,1366,768);
scene.getStylesheets().add(getClass().getResource("/css/main.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
I get this:
So the CSS for the text is now working, but not for the Pane? I have no idea why this might happen.
// ---- REPRODUCTION -- //
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane bp = new BorderPane();
MasterController.setRoot(bp);
Parent left = FXMLLoader.load(getClass().getResource("left.fxml"));
Parent right = FXMLLoader.load(getClass().getResource("right.fxml"));
bp.setLeft(left);
bp.setRight(right);
Scene scene = new Scene(bp, 600, 400);
scene.getStylesheets().add(getClass().getResource("main.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MasterController.java
package sample;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
public class MasterController {
private static VBox rightBox;
private static BorderPane root;
public static VBox getRightBox() {
return rightBox;
}
public static void setRightBox(VBox rightBox) {
MasterController.rightBox = rightBox;
}
public static BorderPane getRoot() {
return root;
}
public static void setRoot(BorderPane root) {
MasterController.root = root;
}
}
LeftController.java
package sample;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import java.io.IOException;
public class LeftController extends MasterController {
#FXML
public void loadCenter(ActionEvent event) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("center.fxml"));
Parent center = loader.load();
getRoot().setCenter(center);
} catch (IOException e){
e.printStackTrace();
}
}
}
CenterController.java
package sample;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
public class CenterController extends MasterController {
#FXML
private VBox center;
public void initialize() {
Platform.runLater(this::build);
}
public void build() {
center.getChildren().add(new Text("loaded"));
StackPane stackPane = new StackPane();
Text text = new Text("ROLL OVER");
stackPane.getChildren().add(text);
center.getChildren().add(stackPane);
stackPane.setOnMouseEntered(event -> {
getRightBox().getChildren().clear();
Pane pane1 = new Pane();
Text exampleText1 = new Text("Example Text");
pane1.getStyleClass().add("blueBG");
Pane pane2 = new Pane();
Text exampleText2 = new Text("Example Text");
pane2.getStyleClass().add("blueBG");
pane1.getChildren().add(exampleText1);
pane2.getChildren().add(exampleText2);
pane1.setLayoutY(event.getSceneY() + 40);
pane1.setLayoutX(event.getSceneX() - 40);
getRoot().getChildren().add(pane1);
getRightBox().getChildren().add(pane2);
});
}
}
RightController.java
package sample;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.layout.VBox;
public class RightController extends MasterController {
#FXML
private VBox rightBox;
public void initialize() {
setRightBox(rightBox);
}
}
left.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.LeftController">
<children>
<Button mnemonicParsing="false" onAction="#loadCenter" prefHeight="38.0" prefWidth="200.0" text="Click me to load center" />
</children>
</VBox>
center.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="center" alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="200.0" stylesheets="#main.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.CenterController">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Center" />
</children>
</VBox>
right.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="rightBox" alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="200.0" stylesheets="#main.css" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.RightController">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Right Box" />
</children>
</VBox>
main.css
.blueBG {
-fx-background-color: aqua;
}
You should get the following result. You can see the pane is being styled correctly when added to the right, but not when added too the root.
BorderPane only applies layout to the center, left, right, top and bottom children. It's however the parent of a layout (or the scene in case of a root) that sets the size of a Region during a layout pass (or not, like in this case).
The result is that the style is applied to the Pane, but the size of the Pane remains 0. You can verify this by adding
pane1.resize(100, 100);
to the event handler to set the size of the pane.
You need to use a different kind of layout as parent for pane1's size to become non-empty or resize it yourself.

JavaFX TextField Get Clicked Item

I'm trying to load(WebEngine) links that are in a TextArea when you click on them. But I have no idea how to get the clicked item.
This is something i tried:
area.setOnMouseClicked(event -> {
WebController.getEngine().load((String) event.getSource());
});
event.getSource() will be the TextArea in your case.
So your code would either be
area.setOnMouseClicked(event -> WebController.getEngine().load(((TextArea) event.getSource()).getText()));
or simpler:
area.setOnMouseClicked(event -> WebController.getEngine().load(area.getText()));
Edit - Simple TextFlow example:
#Override
public void start(Stage primaryStage) {
TextFlow textFlow = new TextFlow();
textFlow.setOnMouseClicked(ev -> {
if(ev.getTarget() instanceof Text) {
Text clicked = (Text) ev.getTarget();
System.out.println(clicked);
}
});
IntStream.range(0, 500).mapToObj(Integer::toString).map(Text::new).forEach(textFlow.getChildren()::add);
BorderPane borderpane = new BorderPane(textFlow);
borderpane.setPadding(new Insets(20));
Scene scene = new Scene(borderpane, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
If anyone wanna see what it turned out here is my code im using:
package com.ekko.history;
import java.net.URL;
import java.util.Objects;
import java.util.ResourceBundle;
import com.ekko.WebController;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
public class HistoryController implements Initializable {
#FXML
private TextFlow textFlow;
#Override
public void initialize(URL location, ResourceBundle resources) {
textFlow.getChildren().clear();
textFlow.setOnMouseClicked(ev -> {
if(ev.getTarget() instanceof Text) {
Text clicked = (Text) ev.getTarget();
WebController.getEngine().load(clicked.getText());
}
});
HistoryClient.getHistory().stream().filter(Objects::nonNull).forEach(s -> {
Text text = new Text(s + "\n");
textFlow.getChildren().add(text);
});
HistoryClient.getHistory().clear();
}
}
And this is the FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<ScrollPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ekko.history.HistoryController">
<content>
<TextFlow fx:id="textFlow" prefHeight="400.0" prefWidth="600.0" />
</content>
</ScrollPane>
Thanks to eckig for helping me!

Categories