JavaFx SceneBuilder 2.0 not resolving custom controls - java

I have a custom JavaFx control that renders in my application. But, I can't get SceneBuilder to understand it.
I have CustomTextField.java / CustomTextField.fxml. CustomTextField inherits from UserControl, as defined here, but my scene builder problem happens with any custom control that I create.
First, I had to change my import statement to be a wildcard. From
<import sample.CustomTextField>
to
<import sample.*>
otherwise, scenebuilder threw an exception / showed a stacktrace indicating that the source file couldn't be found. I have no idea why this was necessary, but it seemed to work, so I kept moving.
I had read that you to specify a scenebuilder-classpath-element in the fxml file as well. So, i tried every combination I could think of:
<?scenebuilder-classpath-element ../../bin?>
<?scenebuilder-classpath-element ../../out?>
<?scenebuilder-classpath-element ../../../out?>
<?scenebuilder-classpath-element ./?>
<?scenebuilder-classpath-element ../../../../../target/classes?>
My issue is that the custom control does not render in Scene Builder. SElecting it in the hiearchy tree, it indicates "Selection contains unresolved reference". If I can't drag/drop the custom control around, that's acceptable. However, I really want to render this in Scene Builder and lay out other stuff.
I am using IntelliJ IDEA 14, and Scene Builder 2.0
sample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import sample.*?>
<?scenebuilder-classpath-element ../../bin?>
<?scenebuilder-classpath-element ../../out?>
<?scenebuilder-classpath-element ../../../out?>
<?scenebuilder-classpath-element ./?>
<?scenebuilder-classpath-element ../../../../../target/classes?>
<Pane 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">
<children>
<Button fx:id="topButton" layoutY="2.0" mnemonicParsing="false" text="Button Top" />
<CustomTextField fx:id="myCustomTextField" layoutX="1.0" layoutY="40.0" />
<Button layoutX="1.0" layoutY="125.0" mnemonicParsing="false" text="Button Bot" />
</children>
</Pane>

I'm able to load custom classes. We got it to work by building a .jar with the custom classes, then in SceneBuilder, go to the settings icon next to searching the library and import custom library. Then find your .jar. Maybe you could do the same thing with .class files?
Here's the import statement in the .fxml:
<?import com.rlsciences.autoredact.view.ItemsTableView?>
Here's where I'm using it in the .fxml:
<ItemsTableView id="searchResultsTableView" fx:id="searchResultsTableView" fixedCellSize="20.0" styleClass="compact-table" VBox.vgrow="ALWAYS">
Here's the class definition:
package com.rlsciences.autoredact.view;
public class ItemsTableView<S> extends TableView<S>

Related

JavaFX and Scene Builder clip scene edges despite specifying USE_COMPUTED_SIZE

I'm using Scene Builder (v11.0.0) to create FXML files for scenes in JavaFX (v12) but, despite instructing all containers to USE_COMPUTED_SIZE for the preferred widths and heights, the rendered scenes (as seen in Scene Builder and also when run as a JavaFX application which loads those FXML files) are being clipped at the right and bottom edges so that bits of nodes are chopped off.
And in Scene Builder it seems that the renderer must know that the scene won't fit the allowed bounds because the editor shows blue boundary markers which are clearly some way beyond the rendered rectangle.
View in Scene Builder
The view in Scene Builder shows that more space is needed at the bottom in order to give the buttons sufficient space (their bottom edge, and the lower edge of the TitledPane is missing). And more space is needed at the right in order to fit the right edges of the DatePicker and TitledPane. The blue boundary markers show clearly where the actual content ends, so it's not clear why the display area is being calculated to be several pixels shorter than this.
View of running Java application
Once the FXML files are used to populate a window in a JavaFX application, the same thing is seen: the calculated size for the window is a number of pixels too few to fit the whole scene correctly.
If the blue boundary markers have correctly been calculated to show that extra display area width and height are needed, how do I tell the FXML to require this additional space when rendering?
Is this a known bug/limitation in Scene Builder, FXML, or JavaFX. Or is there something more I need to do beyond just selecting USE_COMPUTED_SIZE for the preferred dimensions?
In order to make this explicit, see the example FXML below which displays the problem illustrated.
scene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox alignment="BASELINE_RIGHT">
<children>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</content>
</TitledPane>
</children>
</VBox>
subscene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
<DatePicker />
</children>
</VBox>
This does appear to be a bug in JavaFX, specifically DatePicker, as this simple example can reproduce the problem:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(new DatePicker());
// Problem shows up when using USE_COMPUTED_SIZE (the default) as well
root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
root.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Resulting in:
Note: It does not seem to matter what parent the DatePicker is put in. Nor does the problem appear with other controls.
A workaround to this issue appears to be calling Window.sizeToScene() after calling show(). I don't understand why that would make a difference, but it does. Unfortunately, this will only help in the real application, not in Scene Builder.

Kotlin FXML file controller

I am trying to make basic desktop app using Kotlin and JavaFX(TornadoFX), but I am stuck on setting up controller for FXML file.
I am following guide from edvin on git LINK to git.
The program should increment number in label whenewer is the button clicked.
The problem is..
When I try to use FXML file shown below, I can't use onAction="#increment" to "connect" that file with a controller.
This will compile, but there is no way to call increment function from controller.kt file. Also there is error saying "No controller specified for top level element" ..
Whenever I try to specify the controller by using fx:controller=view.Controller the code will not even compile, showing error:
javafx.fxml.LoadException: Controller value already specified.
Could someone please help me out?
Here is FXML file:
?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<BorderPane xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1">
<padding>
<Insets top="20" right="20" bottom="20" left="20"/>
</padding>
<center>
<VBox alignment="CENTER" spacing="10">
<Label text="0">
<font>
<Font size="20"/>
</font>
</Label>
<Button text="Click to increment" onAction="#increment"/>
</VBox>
</center>
</BorderPane>
Here is MyApp.kt (main file):
package app
import javafx.stage.Stage
import tornadofx.*
import view.MainView
class MyApp: App(MainView::class){
override val primaryView = MainView::class
override fun start(stage: Stage) {
stage.minHeight = 400.0
stage.minWidth = 600.0
super.start(stage)
}
}
Here is MainView.kt (view):
package view
import javafx.scene.layout.BorderPane
import tornadofx.*
class MainView : View() {
override val root: BorderPane by fxml("/views/MainViewFXML.fxml" )
}
Here is Controller.kt (this should be used to control FXML file actions):
package view
class Controller {
fun increment(){
//code
}
}
In TornadoFX, the View IS the controller. Think of View subclasses as the View Controller. React to UI events in the View, and pass business logic off to a ViewModel or a Controller subclass.
Place your increment function in MainView and it will be called :) Remove the fx:controller=view.Controller attribute.

The controller 'AdminSceneController' has no event slot 'logout' - JavaFX Error

I am trying to create a simple button, but I am getting this stupid error and it doesn't make any sense.
Here is my Admin Scene FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="437.0" prefWidth="582.0" stylesheets="#application.css" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="appController.AdminSceneController">
<children>
<Separator layoutX="-14.0" layoutY="101.0" prefHeight="10.0" prefWidth="601.0" />
<Label layoutX="14.0" layoutY="29.0" text="admin panel">
<font>
<Font name="Book Antiqua" size="28.0" />
</font>
</Label>
<Button layoutX="174.0" layoutY="32.0" mnemonicParsing="false" style="-fx-background-radius: 100px;" text="+" textFill="#369033" />
<Button fx:id="logoutButton" layoutX="14.0" layoutY="65.0" mnemonicParsing="false" onAction="#logout" prefHeight="3.0" prefWidth="81.0" styleClass="logout" stylesheets="#application.css" text="(logout)" textFill="#070707" />
<Button layoutX="387.0" layoutY="392.0" mnemonicParsing="false" prefHeight="31.0" prefWidth="158.0" text="Delete" />
<ListView layoutX="223.0" layoutY="106.0" prefHeight="327.0" prefWidth="128.0" />
</children>
</Pane>
And this is my AdminSceneController.java
package appController;
import appDesign.PhotoAlbum;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
public class AdminSceneController {
public class MainSceneController {
#FXML
Button logoutButton;
#FXML
public void logout(ActionEvent event) throws Exception {
PhotoAlbum.primaryStage.show();
((Node)(event.getSource())).getScene().getWindow().hide();
}
}
}
I get a warning from Eclipse saying:
The controller 'AdminSceneController' has no event slot 'logout'
And when I run the program, I get the error:
javafx.fxml.LoadException: Error resolving onAction='#logout', either the event handler is not in the Namespace or there is an error in the script.
/C:/Users/Peter/Documents/GitHub/PhotoAlbum40/bin/appDesign/AdminPanelScene.fxml:19
Can anybody help?
Your AdminSceneController does not have a logout method, your class MainSceneController does though.
Remove the line
public class MainSceneController {
and the closing }and it should work.
You have to understand, that an inner class (MainSceneController) is not the same class as the enclosing class (AdminSceneController). By using fx:controller="appController.AdminSceneController" in the fxml a instance of AdminSceneController is created. This class however does not contain a single method or field. This causes the error.
Furthermore note that the FXMLLoader does not allow you to create non-static inner classes. If you want the FXMLLoader to create your controller instance, you have to make MainSceneController static and use fx:controller="appController.AdminSceneController$MainSceneController".
Ways around this would be specifying a controllerFactory or creating the controller instance yourself:
FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
AdminSceneController enclosingInstance = new AdminSceneController(); // or any other way to get your hands on a instance of the enclosing class
// specify controller instance used yourself
loader.setController(enclosingInstance.new MainSceneController());
...
loader.load()
Which requires you to remove the fx:controller attribute from the fxml.
Of course you could also simply move the field / method to a top level class...

Loading data in HBox layout, FXML

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().

Setting VBox.vgrow on a child element of <fx:root> in JavaFX 8

I have the following .fxml file:
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="javafx.com/fxml">
<Pane VBox.vgrow="ALWAYS">
<!-- ... -->
</Pane>
</fx:root>
However, there is an error at VBox.vgrow="ALWAYS", since <fx:root ... is not exactly a VBox. How can I do this in FXML (no Java)?
Edit: The error in my IDE shows "Attribute VBox.vgrow is not allowed here", and the error the Java application gives is "VBox.vgrow is not a valid attribute."
I had neglected to display all my imports of the .fxml file (which only had javafx.scene.layout.Pane).
In order for that specific .fxml file to not give an error (see edit), the import javafx.scene.layout.VBox also had to be added, as VBox.* cannot be used on any element unless VBox is imported.
The correct .fxml file is:
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="javafx.com/fxml">
<Pane VBox.vgrow="ALWAYS">
<!-- ... -->
</Pane>
</fx:root>

Categories