bind two ToggleButtons to a BooleanProperty - java

I have two ToggleButtons and I would like them both to be bound to a Boolean property so that when one ToggleButton is selected, the other ToggleButton isn't, and the BooleanProperty is true, and vice versa.
Here's what I tried.
FXML file:
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="my.package.MainController" styleClass="Tool" fx:id="root">
<HBox spacing="10">
<fx:define>
<ToggleGroup fx:id="modeToggleGroup"/>
</fx:define>
<ToggleButton fx:id="manualModeBtn" text="Manual Mode" selected="true" toggleGroup="$modeToggleGroup"/>
<ToggleButton fx:id="automaticModeBtn" text="Automatic Mode" toggleGroup="$modeToggleGroup"/>
</HBox>
<!-- other stuff -->
</VBox>
Controller file:
public class MainController {
#FXML
private ToggleButton manualModeBtn;
#FXML
private ToggleButton automaticModeBtn;
private BooleanProperty isAutomaticMode;
public void initialize() {
isAutomaticMode = new SimpleBooleanProperty();
automaticModeBtn.selectedProperty.bindBidirectional(isAutomaticMode);
}
}
The ToggleGroup ensures that neither button is selected at the same time, but I can still unselect both of them, which I don't want to be possible.
How do I bind the other ToggleButton to the opposite (i.e. not()) of the Boolean Property?

You could do this by using listeners:
public class MainController {
#FXML
private ToggleButton manualModeBtn;
#FXML
private ToggleButton automaticModeBtn;
public void initialize() {
manualModeBtn.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> automaticModeBtn.setSelected(! isNowSelected));
automaticModeBtn.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> manualModeBtn.setSelected(! isNowSelected));
}
}
Note that RadioButton has almost this functionality already (in the case of a radio button, you can't "deselect" it), so you could simply use RadioButtons instead. Note this question, if it is just a case of how they look.

This can also be done by setting a listener on the ToggleGroup's selectedToggleProperty.
You first have to set an initial button.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ToggleButtonExperiments extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setTitle("HBox Experiment 1");
ToggleButton toggleButton1 = new ToggleButton("Left");
ToggleButton toggleButton2 = new ToggleButton("Right");
ToggleGroup toggleGroup = new ToggleGroup();
toggleButton1.setSelected(true);//set initial button!!!!!
toggleGroup.selectedToggleProperty().addListener((obs, oldTog, newTog) -> {
if (newTog == null) {
oldTog.setSelected(true);
}
});
toggleButton1.setToggleGroup(toggleGroup);
toggleButton2.setToggleGroup(toggleGroup);
HBox hbox = new HBox(toggleButton1, toggleButton2);
Scene scene = new Scene(hbox, 200, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}

Related

How to set text to a label on keyboard key press in JavaFX application?

I'm making JavaFX application with Scene Builder. I have Main Class that launch Controller with main window.
I have a label in the Controller class
I need to assign keyboard key press in main window.
Example: if I press '1' on keyboard the text of a label should be set as "key 1 is pressed.
If I press "2" on keyboard the text should be set as "key 2 is pressed"
How Can I do that?
I've tried to use this code in initialize method, but nothing happens:
category1.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
KeyCode kc = ke.getCode();
if((kc.equals(KeyCode.D))) {
category1.setText("Key D is pressed");
}
}
});
Main Class:
package src.card;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.*;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("resources/fxml/card.fxml"));
Scene scene = new Scene(root, 1600, 600);
primaryStage.setScene(scene);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setResizable(true);
primaryStage.getIcons().add(new Image("src/card/resources/logo-icon.png"));
primaryStage.show();
//adding resize and drag primary stage
ResizeHelper.addResizeListener(primaryStage);
//assign ALT+ENTER to maximize window
final KeyCombination kb = new KeyCodeCombination(KeyCode.ENTER, KeyCombination.CONTROL_DOWN);
scene.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (kb.match(event)) {
primaryStage.setMaximized(!primaryStage.isMaximized());
primaryStage.setResizable(true);
Controller cont = Context.getInstance().getController();
if (!primaryStage.isMaximized()) {
cont.getBtnFont().setPrefWidth(20);
cont.getBtnPalette().setPrefWidth(20);
cont.getBtnQuestCards().setPrefWidth(20);
cont.getBtnNonQuestCards().setPrefWidth(20);
} else if (primaryStage.isMaximized()){
cont.getBtnFont().setPrefWidth(50);
cont.getBtnPalette().setPrefWidth(50);
cont.getBtnQuestCards().setPrefWidth(50);
cont.getBtnNonQuestCards().setPrefWidth(50
);
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
Controller:
#FXML private Label category1
#FXML public void initialize(URL location, ResourceBundle resources) {
category1.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
KeyCode kc = ke.getCode();
if((kc.equals(KeyCode.D))) {
category1.setText("Key D is pressed");
}
}
});
//register Controller in Context Class
Context.getInstance().setController(this);
}
EDIT:
I've tried to use it in initialize method:
category1.getScene().setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
KeyCode kc = ke.getCode();
if((kc.equals(KeyCode.D))) {
category1.setText("Key D is pressed");
}
}
});
and I got errors:
javafx.fxml.LoadException:
/D:/IDEA%20Projects/CategoryCards/out/production/CategoryCards/src/card/resources/fxml/card.fxml
at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
If I remove getScene() from category1 - everything is going fine and application launch
It seems when I use getScene() it try to get fxml file from out folder, not my main folder, but how can I fix this?
You can do so by setting key press event(setOnKeyPressed) to parent of Label
instead of Scene. Trying to add event to Scene will give error because initialize will be called before creation of the Scene.
Let's say you have FXML file called sample.fxml and In that file you have GridPane having id root and a Label having id category1
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:controller="Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" fx:id="root">
<Label text="Category" fx:id="category1"/>
</GridPane>
And you have Controller class called Controller. To change label of category1 when any key is pressed, first you have to set category1 focus traversable by calling setFocusTraversable method on category1 and passing true. And then you have to add key press event on root(parent of category1), As shown below
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
#FXML private Label category1;
#FXML private GridPane root;
#Override
public void initialize(URL location, ResourceBundle resources){
category1.setFocusTraversable(true);
root.setOnKeyPressed(this::handle);
}
private void handle(KeyEvent keyEvent){
category1.setText("Key " + keyEvent.getCode().getName() + " is pressed");
}
}
And here is simple version of Main class
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Scene scene = new Scene(root, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
}

Disable Javafx TextField and Buttons after a fixed set of days

So i have created a Data Entry Application using Javafx and Ms Access, but i wanted to add a feature which i don't know how to implement. I want my application to run for 6 days only and after that all my buttons and textfields will be disabled.
when i click the start button it will setdisable(false) all my buttons and textfields and will start the day count and setdisable(true) Start button forever. I want to store this info in some hidden .txt file so that when the application closes it will remember the start button status and day count and also display the day no. in a label. At the end of 6th day all my buttons and textfields will be disabled forever.
This is my main class:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;
public class DataEntryMain extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("/application/UI.fxml"));
Scene scene = new Scene(root,1280,700);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
This is my Controller class:
package application;
import java.sql.SQLException;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class DataEntryController {
int rn;
#FXML
private JFXTextField t1, t2, t3, t4 , t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15;
#FXML
private JFXButton start;
#FXML
private JFXButton lock;
#FXML
private JFXButton save;
#FXML
private JFXButton update;
#FXML
private Label resultConsole;
#FXML
private JFXTextField searchRowNo;
#FXML
private TableView<DataEntry> tabView;
#FXML
private TableColumn<DataEntry, Integer> rowNumber;
#FXML
private TableColumn<DataEntry, String> c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15;
#FXML
private void insertData(ActionEvent event) throws ClassNotFoundException, SQLException{
try {
DataEntryModel.insertData(t1.getText(), t2.getText(), t3.getText(), t4.getText(), t5.getText(), t6.getText(), t7.getText(), t8.getText(), t9.getText(), t10.getText(), t11.getText(), t12.getText(), t13.getText(), t14.getText(), t15.getText());
resultConsole.setText("Data Added Successfully!");
}
catch(SQLException e) {
System.out.println("Exception Occer In Insertion "+e);
e.printStackTrace();
throw e;
}
}
#FXML
private void updateData(ActionEvent event) throws ClassNotFoundException, SQLException{
rn = Integer.parseInt(searchRowNo.getText());
try {
DataEntryModel.updateData(rn, t1.getText(), t2.getText(), t3.getText(), t4.getText(), t5.getText(), t6.getText(), t7.getText(), t8.getText(), t9.getText(), t10.getText(), t11.getText(), t12.getText(), t13.getText(), t14.getText(), t15.getText());
resultConsole.setText("Data Updated Successfully!");
}
catch(SQLException e) {
System.out.println("Exception Occure In Update "+e);
throw e;
}
}
#FXML
private void intialize()throws Exception{
rowNumber.setCellValueFactory(cellData -> cellData.getValue().getRNo().asObject());
c1.setCellValueFactory(cellData -> cellData.getValue().getText1());
c2.setCellValueFactory(cellData -> cellData.getValue().getText2());
c3.setCellValueFactory(cellData -> cellData.getValue().getText3());
c4.setCellValueFactory(cellData -> cellData.getValue().getText4());
c5.setCellValueFactory(cellData -> cellData.getValue().getText5());
c6.setCellValueFactory(cellData -> cellData.getValue().getText6());
c7.setCellValueFactory(cellData -> cellData.getValue().getText7());
c8.setCellValueFactory(cellData -> cellData.getValue().getText8());
c9.setCellValueFactory(cellData -> cellData.getValue().getText9());
c10.setCellValueFactory(cellData -> cellData.getValue().getText10());
c11.setCellValueFactory(cellData -> cellData.getValue().getText11());
c12.setCellValueFactory(cellData -> cellData.getValue().getText12());
c13.setCellValueFactory(cellData -> cellData.getValue().getText13());
c14.setCellValueFactory(cellData -> cellData.getValue().getText14());
c15.setCellValueFactory(cellData -> cellData.getValue().getText15());
ObservableList<DataEntry> deList = DataEntryModel.getAllRecords();
populateTable(deList);
}
private void populateTable(ObservableList<DataEntry> deList) {
tabView.setItems(deList);
}
}
I have 3 other classes which i have used for database operations and i don't think that's relevant here.This is how the application looks
Make a seperate Thread use TimeUnit.DAYS.sleep(days); in it then run the thing that closes the inputs. You can just disable the entire pane, no need for individual buttons, or make a stack pane with a message on top of everything and show it after x amount of days. You also need to interact with the view from the other thread with Platform.runLater and you need to close the thread if the Window gets closed manually with an onCloseRequest event on your window.
EDIT:
Main
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) throws Exception {
primaryStage = new Stage();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Main.fxml"));
Parent root = loader.load();
var controller = (MainController) loader.getController();
primaryStage.setScene(new Scene(root));
primaryStage.setOnCloseRequest(event -> {
controller.stopThread();
});
primaryStage.show();
}
}
Main Controller
public class MainController {
#FXML
private TextField tfTest;
#FXML
private BorderPane borderPane;
private LeKillerThread thread;
#FXML
public void btStartClick(ActionEvent actionEvent) {
//save time to use in Thread? Does not do that now.
}
public void initialize() {
thread = new LeKillerThread(borderPane);
thread.start();
}
public void stopThread() {
this.thread.interrupt();
}
}
Thread
public class LeKillerThread extends Thread {
private Node nodeToKill;
public LeKillerThread(Node nodeToKill) {
this.nodeToKill = nodeToKill;
}
#Override
public void run() {
try {
//not gonna test this with days...
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> {
this.nodeToKill.setDisable(true);
});
}
}
Main.fxml
<BorderPane fx:id="borderPane"
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="MainController"
prefHeight="400.0" prefWidth="600.0">
<center>
<VBox alignment="CENTER">
<TextField fx:id="tfTest"></TextField>
<Button text="start" onAction="#btStartClick"></Button>
</VBox>
</center>
</BorderPane>
I did not test this for days... but it works. If you want to keep track of the Time then you got to save the time when you press start and load it next time you launch the program and calculate the difference to right when you start the thread to sleep that amount of time instead of a fixed amount. It may be wise to sleep for lower periods of time in a loop though and not a week. So you have a chance to test if the Window somehow disappeared without telling you (the Thread) about it. It should not usually be a issue thought, only if your Window outright crashes.

BorderPane not updating despite links between FXML <-> Controller <-> Subclass of Application

Relevant Files: (apologies on any formatting, made many attempts to make it work)
If the files are not sufficient, the repository is here: https://github.com/TheeNinja/StockLookupTool
stock_lookup_tool_main.fxml
<BorderPane fx:id="borderPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.theeninja.stocklookuptool.gui.StockLookupToolApplicationController">
<top>
...
</top>
<left />
<center />
</BorderPane>
stock_information_center.fxml
<GridPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="me.theeninja.stocklookuptool.gui.selection.StockSearchSelectionController"
prefHeight="400.0" prefWidth="600.0"
fx:id="stockInformationCenter">
</GridPane>
favorite_stocks_sidebar.fxml
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.theeninja.stocklookuptool.gui.selection.StockSearchSelectionController" fx:id="verticalStockList">
...
</VBox>
StockLookupTool.java (Class with the start method)
public class StockLookupTool extends Application {
....
#Override
public void start(Stage stage) throws Exception {
...
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/fxml/stock_lookup_tool_main.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 250, 300);
stage.setTitle(APPLICATION_TITLE);
stage.setScene(scene);
stage.setFullScreen(true);
stage.show();
}
....
}
StockLookupToolApplicationController (controller for stock_lookup_tool_main.fxml)
public class StockLookupToolApplicationController {
...
#FXML public Label stockSearchLabel;
#FXML public Label newsLabel;
#FXML public Label settings;
#FXML public BorderPane borderPane;
#FXML public HBox topApplicationNavigation;
#FXML
public void handleStockSearchSelection() {
logger.log(Level.INFO, "Setting GUI View to Stock Search");
setView(StockSearchSelectionController.getInstance());
}
private void setView(Selection selection) {
borderPane.setLeft(selection.getLeft());
borderPane.setCenter(selection.getCenter());
borderPane.setRight(selection.getRight());
borderPane.setBottom(selection.getBottom());
}
}
StockSearchSelectionController.java (controller for both stock_information_center.fxml and favorite_stocks_sidebar.fxml)
public class StockSearchSelectionController implements Selection {
#FXML public Label favoriteStocksLabel;
#FXML public TextField addFavoriteStockInput;
#FXML public VBox verticalStockList;
#FXML public HBox addFavoriteStockInputContainer;
#FXML public GridPane stockInformationCenter;
#FXML
public void handleFavoriteStockInput(KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.ENTER) {
...
// makes visual changes to both stockInformationCenter AND verticalStockList, hence I need access to both fxml files (which is why this controls both).
}
}
#Override
public Node getLeft() {
return verticalStockList;
}
#Override
public Node getCenter() {
return stockInformationCenter;
}
}
More information:
The interface Selection has the methods getLeft() and getCenter() (among other irrelevant ones). Both of these returns Node. In stock_lookup_tool_main.fxml, in the top portion of the BorderPane I have a button (cut out in the provided snippet) that, when pressed, calls handleStockSearchSelection() (this method call indeed happens, I have verified it with a logger).
Now there is a controller, that controls both stock_information_center.fxml and favorite_stocks_sidebar.fxml. This controller implements Selection, and in turn, implements getLeft() and getCenter(). getLeft() returns the VBox variable
that correlates to favorite_stocks_sidebar.fxml, while getCenter() returns the GridPane variable that correlates to stock_information_center.fxml. When handleStockSearchSelection() is called, the left part of the borderPane object is set to the VBox, and the center is set to the GridPane. All these method calls/actions have been verified to occur. However, there is no visual change to the scene.
My question is: Why are these changes to borderPane not implemented visually in the scene? I have established a link between the scene <-> stock_lookup_tool_main.fxml through setting the loader's location. I have also established a link between stock_lookup_tool_main.fxml <-> its controller, hence they should share changes. I have modified the border pane in stock_lookup_tool_main.fxml in the controller by calling setLeft() and setCenter() on the variable (same name as the ID, borderPane). Yet, the scene visually does not change.

JavaFX word-wrap on a Treeview, TreeItem/TreeCell

I want to have a collapsible list so I'm using a TreeView, but longer strings are giving horizontal scrollbars instead of word wrapping. I've tried using the CSS property -fx-wrap-text on the .tree-cell class but unfortunately nothing seems to happen.
Are TreeCells not meant to span more than one line? What alternative is there if that's the case?
Here's an image of what it looks like now:
test.css
.tree-cell {
-fx-wrap-text: true;
-fx-text-fill: blue;
}
subjects.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.net.URL?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication1.FXMLDocumentController">
<Button fx:id="button" layoutX="182.0" layoutY="14.0" onAction="#handleButtonAction" text="Click Me!" />
<TextField fx:id="filterSubject" layoutX="21.0" layoutY="14.0" />
<TreeView fx:id="subjectList" editable="true" layoutX="14.0" layoutY="61.0" prefHeight="200.0" prefWidth="365.0" />
<stylesheets>
<URL value="#test.css" />
</stylesheets>
</AnchorPane>
subjects.txt - just pairs of text where the first is subject, and second is description.
1
Somewhat long string that I want to wordwrap.
2
Somewhat long string that I want to wordwrap.
3
Somewhat long string that I want to wordwrap.
FXMLDocumentController.java
package javafxapplication1;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TreeView;
import javafx.scene.control.TreeItem;
import java.io.*;
public class FXMLDocumentController implements Initializable {
#FXML
private TreeView<String> subjectList;
#FXML
private void handleButtonAction(ActionEvent event) {
System.out.println("You clicked me!");
}
private void getSubjects(){
TreeItem<String> subjectRoot = new TreeItem<String> ("Subject");
subjectRoot.setExpanded(true);
try {
BufferedReader fileReader = new BufferedReader(
new FileReader("subjects.txt")
);
String subject = null;
String description = null;
while((subject = fileReader.readLine()) != null) {
description = fileReader.readLine();
TreeItem<String> subjectTree = new TreeItem<String> (subject);
TreeItem<String> descriptionItem = new TreeItem<String> (description);
subjectTree.getChildren().add(descriptionItem);
subjectRoot.getChildren().add(subjectTree);
}
} catch(FileNotFoundException e) {
System.out.println("File not found");
} catch(IOException e) {
e.printStackTrace();
}
subjectList.setRoot(subjectRoot);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
getSubjects();
}
}
JavaFXApplication1.Java
package javafxapplication1;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication1 extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Actually the CSS class is working as you expect.
The problem is: The TreeView contains TreeCells. These TreeCells will wrap its text, in case of they will have not enough vertical space to grow vertically. As the TreeView has a built in "ScrollPane" which ones view-port will grow vertically, it will provide enouth space for the TreeCell instances in any case to grow, therefore the wrapping will never be enabled.
To avoid this you could set the cell factory to generate the TreeCell instances manually. And then you can bind the prefWidthProperty of these elements to the widthProperty of the TreeView.
Example
public class WrappedTreeView extends Application {
#Override
public void start(Stage stage) {
final Scene scene = new Scene(new Group(), 200, 400);
Group sceneRoot = (Group) scene.getRoot();
scene.getStylesheets().add(getClass().getResource("application.css").toString());
TreeItem<String> root = new TreeItem<>("Root");
root.setExpanded(true);
TreeItem<String> childNode1 = new TreeItem<>("I am a very long node - the first one -, my text must be wrapped! If it is not wrapped, it's a problem!");
TreeItem<String> childNode2 = new TreeItem<>("I am a very long node - the second one -, my text must be wrapped! If it is not wrapped, it's a problem!");
TreeItem<String> childNode3 = new TreeItem<>("I am a very long node - the third one -, my text must be wrapped! If it is not wrapped, it's a problem!");
root.getChildren().addAll(childNode1, childNode2, childNode3);
childNode2.getChildren().add(new TreeItem<>("And I am a very long embedded node, so my text must be wrapped!"));
TreeView<String> treeView = new TreeView<>(root);
treeView.setCellFactory(item -> {
TreeCell<String> treeCell = new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty)
setText(item);
else
setText("");
}
};
treeCell.prefWidthProperty().bind(treeView.widthProperty().subtract(5.0));
return treeCell;
});
treeView.setMaxWidth(200);
sceneRoot.getChildren().add(treeView);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
The content of application.css
.tree-cell {
-fx-wrap-text: true;
-fx-text-fill: blue;
}
And the generated TreeView:
You can do this by setting the tree cell's prefWidth value in a treecell cellfactory (along with the treecell's wraptext value to true), which will then stop the cell from expanding horizontally with a scrollPane.

How to I get the Scene of another UI interface - Javafx 8

I am new to JavafX. I wanted to change the CSS file of my first GUI through the second one.
I have the following code:
Main1.java
package javafxapplication3.a;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main1 extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = null;
try {
root = FXMLLoader.load(getClass().getResource("/main1.fxml"));
} catch (Exception e) {
}
String css = Main1.class.getResource("/main1.css").toExternalForm();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().clear();
scene.getStylesheets().add(css);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("JCal");
primaryStage.show();
}
}
Main1Controller.java
package javafxapplication3.a;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
public class Main1Controller {
#FXML
private Button button1;
public void initialize() {
button1.setOnAction(value -> {
Stage primaryStage = new Stage();
Parent root = null;
try {
root = FXMLLoader.load(getClass().getResource("/main2.fxml"));
} catch (Exception e) {
}
Scene scene = new Scene(root, 400, 400);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("JCal");
primaryStage.show();
});
}
}
main1.fxml
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication3.a.Main1Controller">
<children>
<Button fx:id="button1" layoutX="271.0" layoutY="173.0" mnemonicParsing="false" text="Main-1" />
</children>
</AnchorPane>
main2.fxml
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication3.a.Main2Controller">
<children>
<Button fx:id="button" layoutX="271.0" layoutY="173.0" mnemonicParsing="false" text="Main-2" />
</children>
</AnchorPane>
In the FXML I have a Button called button1, when ever I click on it, it opens a new GUI which has another button in it called button. In the end what I wanted to do was that when ever I click on the second button i.e. button the colour of the button in the primary GUI should change should change.
I did try getting the controllers shown in this example, But this dint help me.
Do I need to create a second controller and create a new stage and scene all together? or is there any alternative way to it?
In the controller for your main2.fxml, provide a mechanism in it for setting an action to be executed when the button is pressed. For example:
public class Main2Controller {
#FXML
private Button button ;
private Runnable buttonAction = () -> {} ; // do nothing by default
public void setButtonAction(Runnable action) {
this.buttonAction = action ;
}
public void initialize() {
button.setOnAction(e -> buttonAction.run());
}
}
Now in your Main1Controller you can retrieve the controller when you load the FXML, and set the button action:
button1.setOnAction(value -> {
Stage primaryStage = new Stage();
Parent root = null;
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/main2.fxml"));
root = loader.load();
Main2Controller controller = loader.getController();
controller.setButtonAction(() -> {
// perform button action here...
});
} catch (Exception e) {
e.printStackTrace();
}
Scene scene = new Scene(root, 400, 400);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("JCal");
primaryStage.show();
});
An alternative approach is to let both controllers have access to the same observable state, e.g. an ObjectProperty<Color>. This approach might be better if you have a lot of actions in one controller that affect state elsewhere (you just bundle all the data into a single "model" class that you pass). This looks like:
public class Main1Controller {
private final ObjectProperty<Color> color = new SimpleObjectProperty<>();
#FXML
private Button button1;
public void initialize() {
color.addListener((obs, oldColor, newColor) -> {
String style = String.format("-fx-background-color: #%02x%02x%02x;",
(int) (newColor.getRed() * 255),
(int) (newColor.getGreen() * 255),
(int) (newColor.getBlue() * 255));
button1.setStyle(style);
});
button1.setOnAction(value -> {
Stage primaryStage = new Stage();
Parent root = null;
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/main2.fxml"));
root = loader.load();
Main2Controller controller = loader.getController();
controller.setColorModel(color);
} catch (Exception e) {
e.printStackTrace();
}
Scene scene = new Scene(root, 400, 400);
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.setTitle("JCal");
primaryStage.show();
});
}
}
and Main2Controller looks like
public class Main2Controller {
#FXML
private Button button ;
private ObjectProperty<Color> colorModel = new SimpleObjectProperty<>();
public void setColorModel(ObjectProperty<Color> color) {
this.colorModel = color ;
}
public void initialize() {
button.setOnAction(e -> {
colorModel.set(/* some color */);
});
}
}

Categories

Resources