So, here's an example fxml from yfiles developer's guide (not that important actually):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import com.yworks.yfiles.drawing.NodeTemplate?>
<NodeTemplate fx:id="templateNode" style="-fx-background-color: darkblue"
xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<VBox alignment="CENTER">
<Label fx:id="firstName" text="${templateNode.item.tag.firstName}"
textFill="${templateNode.styleTag.firstNameColor}" />
<Label fx:id="lastName" text="${templateNode.item.tag.lastName}"
textFill="${templateNode.styleTag.lastNameColor}" />
</VBox>
</NodeTemplate>
templateNode.item.tag is an object of Person class:
public class Person {
private final String firstName;
private final String lastName;
public Person(String firstName, String lastName){
this.firstName = firstName; this.lastName = lastName;
}
public String getFirstName() {return firstName;}
public String getLastName() {return lastName;}
}
Is it possible inside fxml to:
a) perform some view-logic (that's how i call it) inside fxml?
For example to make first label's text to be set to templateNode.item.tag.firstName if and only if it's length is > 10 and "whatever" otherwise?
b) at least specifically iterate over a collection from model?
Imagine templateNode.item.tag is a list of Person objects.
For example in pydjanvaFX (which is django-enhanced-templating inside javaFX, language i invented on the occasion of writing this question) language i can write something like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import com.yworks.yfiles.drawing.NodeTemplate?>
<NodeTemplate fx:id="templateNode" style="-fx-background-color: darkblue"
xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<VBox alignment="CENTER">
{% for i, model in enumerate(templateNode.item.tag) %}
<Label fx:id="firstName#${i}" text="${model.firstName}"
textFill="${templateNode.styleTag.firstNameColor}" />
<Label fx:id="lastName#${i}" text="${model.lastName}"
textFill="${templateNode.styleTag.lastNameColor}" />
{% endfor %}
</VBox>
</NodeTemplate>
You may want to read Introduction to FXML and about Scripting in FXML.
The <fx:script> tag allows a caller to import scripting code into or
embed script within a FXML file. Any JVM scripting language can be
used, including JavaScript, Groovy, and Clojure, among others. Script
code is often used to define event handlers directly in markup or in
an associated source file, since event handlers can often be written
more concisely in more loosely-typed scripting languages than they can
in a statically-typed language such as Java.
However, I strongly advise against it. You want to find compile time errors, not runtime errors.
A brief example about how a script could look like, a label is added dynamically:
<?xml version="1.0" encoding="UTF-8"?>
<?language javascript?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<VBox fx:id="root" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<children>
<Button text="Button" />
<Label text="Label" />
</children>
<fx:define>
<Label fx:id="addedLabel" text="Label" />
</fx:define>
<fx:script>
addedLabel.setText('Added Label');
root.getChildren().add( addedLabel);
java.lang.System.out.println( "Children: " + root.getChildren().size());
</fx:script>
</VBox>
I won't go any deeper into this or lists or whatever scripting you want to do, because seriously: don't do it this way! Sooner or later you'll run into problems.
Related
I have one container in an FXML file (let's call this the main container w/ main controller) and am trying to generate other FXML-created nodes and add them as children in the main container.
I don't think I can do this by nesting the FXML since the nodes are being added to a custom component that requires function calling. I thought I could load the nodes in the main controller's initialize but this leads to a stack overflow since the loading calls the initialize function recursively.
What's the best way to do this? I want to be able to have the main controller respond to and setup the nodes. I could
Main controller looks like:
public class MainController implements Initializable {
#FXML
private CardPane cardPane;
#Override
public void initialize(URL url, ResourceBundle rb) {
// setup panes
AnchorPane importPane = new AnchorPane();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ImportPane.fxml"));
fxmlLoader.setRoot(importPane);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
cardPane.addCard(importPane);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
CardPane is the custom component class where addCard adds a node (an implementation of CardLayout).
The FXML for ImportPane is pretty much empty right now. I just have a CSS styling for the root class so I can see how it's laying out.
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<fx:root id="AnchorPane" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" type="AnchorPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<stylesheets>
<URL value="#view.css" />
</stylesheets>
<children>
<VBox alignment="CENTER" layoutX="121.0" layoutY="38.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" styleClass="importPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</fx:root>
The FXML for the main component is:
<?xml version="1.0" encoding="UTF-8"?>
<?import cardPane.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" stylesheets="#PHASIQAnalyzerView.css" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="phasiqplateanalyzer.PHASIQAnalyzerController">
<children>
<VBox alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<HBox id="buttonBar" alignment="CENTER" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" />
<CardPane id="mainPane" fx:id="cardPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</children>
</AnchorPane>
remove fx:controller="phasiqplateanalyzer.PHASIQAnalyzerController"from the FXML and use the setController() method of an FXMLLoader object (no static method call for that...)
https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#setController-java.lang.Object-
FXMLLoader fxmlLoader =new FXMLLoader();
fxmlLoader.setController(yourControllerClass);
fxmlLoader.load("yourFxmlFile");
I am developing a simple app using javafx. I experience one problem. I want to populate my HBox layout with some data, and want to do this in my Controller class. I do not know how to refernce a method inside my Controller class to the element in my FXML file, so that as soon as my app starts, and main window loads, the HBox gets populated with data. Which attribute of HBox element should I use?
Here is some code:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.Controller" >
<children>
<HBox layoutX="386.0" layoutY="14.0" prefHeight="371.0" prefWidth="200.0" fx:id="hbox" />
</children>
</Pane>
Just modify the ObservableList returned by Pane.getChildren().
StackPane layoutY="70.0" prefHeight="479.0". I want to make the values (70.0) and (479.0) static in a Java file so I can use them for other files.
Is this possible?
If your constant is defined in a class:
public class SomeClass {
public static final double DEFAULT_HEIGHT = 479 ;
// ...
}
then you can access it in FXML as follows:
<StackPane>
<prefHeight>
<SomeClass fx:constant="DEFAULT_HEIGHT" />
</prefHeight>
</StackPane>
Make sure you have the appropriate import in the fxml file for the class you are using.
James_D showed you the way of doing it with a custom class. Another way of doing it in fxml is to define your own variables. But they are not shareable across files.
Instead of this
<StackPane layoutY="70.0" prefHeight="479.0">
You want to have
<StackPane layoutY="$variable" prefHeight="$variable">
You be able to do it like this
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" id="AnchorPane" prefHeight="200" prefWidth="320" fx:controller="javafxapplication22.FXMLDocumentController">
<fx:define>
<Double fx:id="layoutY" fx:value="70.0"/>
<Double fx:id="prefHeight" fx:value="479.0"/>
</fx:define>
<children>
<StackPane layoutY="$layoutY" prefHeight="$prefHeight"/>
<Pane layoutY="$layoutY" prefHeight="$prefHeight"/>
</children>
</AnchorPane>
This question already has answers here:
How to implement a NumberField in javaFX 2.0?
(8 answers)
Closed 9 years ago.
I'm new to Java and JavaFX, but I'm trying to build a pretty basic program. Below is a snippet of code I had originally placed in the On Key Pressed event of the TextField (it did of course have a different parameter type). However, the code didn't work there (i.e. the user was still allowed to enter characters), and after some Googling I found that maybe this code should be moved to the On Input Method Text Changed event of the TextField. But, I'm unable to determine how I can recover the key that was pressed and whether or not CTRL is down.
private void verifyKeyIsInteger(InputMethodEvent event) {
KeyCode code = event.getCode();
if (event.isControlDown() && (code.equals(KeyCode.C) || code.equals(KeyCode.X) || code.equals(KeyCode.V))) {
return;
}
else if (code.isDigitKey()) {
return;
}
event.consume();
}
This code is in my controller that is attached to the FXML file.
How can I ensure that user's can only input integers into the TextField?
EDIT
Here is my current FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="tutoringcalculator.MainFormController">
<children>
<Label layoutX="14.0" layoutY="14.0" text="Session Minutes:" />
<TextField fx:id="sessionMinutes" layoutX="100.0" layoutY="14.0" prefWidth="200.0" />
<Label layoutX="14.0" layoutY="34.0" text="Earnings:" />
<TextField fx:id="earnings" layoutX="100.0" layoutY="34.0" prefWidth="200.0" />
<Button fx:id="quitButton" layoutX="511.0" layoutY="14.0" minWidth="75.0" mnemonicParsing="false" onMouseClicked="#quitApplication" text="Quit" />
</children>
</AnchorPane>
If you need to track integer input, you can have a look at the implementation of IntegerField of JavaFX (a field which is used to access only integer values) :
IntegerField.java
IntegerFieldSkin.java
But this approach is not applicable, if CTRL is needed to be tracked.
I created this JavaFX dialog with Close button:
final int xSize = 300;
final int ySize = 280;
final Color backgroundColor = Color.WHITE;
final String text = "SQL Browser Version 1.0";
final Stage aboutDialog = new Stage();
aboutDialog.initModality(Modality.WINDOW_MODAL);
Button closeButton = new Button("Close");
closeButton.setAlignment(Pos.BOTTOM_CENTER);
closeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
aboutDialog.close();
}
});
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(new Text(text), closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
aboutDialog.setScene(aboutDialogScene);
aboutDialog.show();
I want to display the button at the bottom of the dialog. I used this to set the alignment:
closeButton.setAlignment(Pos.BOTTOM_CENTER); but for some reason the button is displayed at the center of the dialog. Can you tell me how I can fix this?
If you want to use a VBox for this, the method you are looking for is:
VBox.setVgrow(node, Priority.ALWAYS);
By default a VBox will just place children one under the other from the top left of where you place it. The children don't expand to fill all of the available vertical area, unless you set a Vgrow constraint on a child with an unbounded max height.
A few different ways you can get the layout you seek (there are others as well):
Use a StackPane instead of a VBox and align your button with StackPane.setAlignment(closeButton, Pos.BOTTOM_CENTER);
Use an AnchorPane instead of a VBox and set constraints on the AnchorPane appropriately.
Use a spring region which is an empty Region which expands to fill empty space.
Sample spring region:
Region topSpring = new Region();
Region bottomSpring = new Region();
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(topSpring, new Text(text), bottomSpring, closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
VBox.setVgrow(topSpring, Priority.ALWAYS);
VBox.setVgrow(bottomSpring, Priority.ALWAYS);
Calling closeButton.setAlignment(Pos.BOTTOM_CENTER); sets the alignment of things (text and graphic) within the closeButton, not the alignment of the closeButton within it's parent (which is what you really want).
For understanding how layout constraints work, SceneBuilder is a good tool to play around with and ScenicView can help debug layout issues in existing code.
Here are a few FXML samples of your layout that you can load up into SceneBuilder to see how the different layout options work.
All of the samples below can easily be written in plain java using the JavaFX API if you prefer. I wrote them in fxml as it makes the layouts easy to preview in SceneBuilder.
FXML sample using a StackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label text="SQL Browser Version 1.0" />
<Button mnemonicParsing="false" text="Button" StackPane.alignment="BOTTOM_CENTER" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</StackPane>
And the same thing with some spring regions:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Label text="SQL Browser Version 1.0" />
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
</VBox>
And the same thing with the label itself set to expand to fill empty space in the VBox:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label maxHeight="1.7976931348623157E308" text="SQL Browser Version 1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>