JavaFX Accelerator using Numeric keys (Ctrl+1) not cross plateform? - javafx

I have a JavaFX project that has a menu bar, and keyboard shortcuts assigned to the menu buttons. These shortcuts works fine, until I try to use the numeric keys (1234567890). I'm trying to assign shortcuts to tabs, so that Ctrl+1 selects the first tab, Ctrl+2 the second, and so on. Theses work fine on Windows, but the Ctrl+1 and Ctrl+0 shorcuts don't work on macOS, and not a single one works on Linux (Mint 21.1).
All the keyboards are french (Azerty).
I tried to override the fxml accelerator in the window controller initalizer, using KeyCode.SOFTKEY_1, DIGIT1 and AMPERSAND, but none of them work (see HelloControler.java)
In a new JavaFX project:
hello-view.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.Menu?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.example.minrepex.HelloController">
<MenuBar fx:id="mainMenuBar">
<Menu text="Vue">
<MenuItem text="Manatee" accelerator="Ctrl+1" fx:id="button1"/>
<MenuItem text="Manatee" accelerator="Ctrl+2" fx:id="button2"/>
<MenuItem text="Manatee" accelerator="Ctrl+3" fx:id="button3"/>
<MenuItem text="Manatee" accelerator="Ctrl+4" fx:id="button4"/>
<MenuItem text="Manatee" accelerator="Ctrl+5" fx:id="button5"/>
<MenuItem text="Manatee" accelerator="Ctrl+6" fx:id="button6"/>
<MenuItem text="Manatee" accelerator="Ctrl+7" fx:id="button7"/>
<MenuItem text="Manatee" accelerator="Ctrl+8" fx:id="button8"/>
<MenuItem text="Manatee" accelerator="Ctrl+9" fx:id="button9"/>
<MenuItem text="Manatee" accelerator="Ctrl+0" fx:id="button0"/>
</Menu>
</MenuBar>
</VBox>
HelloController.java:
package com.example.minrepex;
import javafx.fxml.FXML;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
public class HelloController {
private final boolean isMacos;
#FXML private MenuBar mainMenuBar;
#FXML private MenuItem button1;
#FXML private MenuItem button2;
#FXML private MenuItem button3;
#FXML private MenuItem button4;
#FXML private MenuItem button5;
#FXML private MenuItem button6;
#FXML private MenuItem button7;
#FXML private MenuItem button8;
#FXML private MenuItem button9;
#FXML private MenuItem button0;
public HelloController() {
final String os = System.getProperty("os.name");
this.isMacos = (os != null && os.startsWith("Mac"));
}
#FXML
public void initialize() {
if (isMacos){
// Use macOS menu bar
mainMenuBar.setUseSystemMenuBar(true);
}
button1.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button1.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button2.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button3.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button4.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button5.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button6.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button7.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button8.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button9.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
// button0.setAccelerator(new KeyCodeCombination(KeyCode.AMPERSAND, KeyCombination.CONTROL_DOWN));
}
}
HelloApplication.java (app)
package com.example.minrepex;
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(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Main.java
package com.example.minrepex;
public class Main {
public static void main(String[] args) {
HelloApplication.main(args);
}
}
What is going on ?

Related

Can't seem to programmatically select TextField from TableCell (TextFieldTableCell-like) when adding a row to TableView (JavaFX 8)

In JavaFX 8, I have created a custom TableCell with a TextField to get more control than with a "regular" TextFieldTableCell. What I want to achieve is to have that TextField automatically selected when an Add button is clicked (so the user doesn't have to click on that TextField or Tab to it after the Add-Record button is fired. Unfortunately, I only manage to select the row/cell but not the underlying TextField (and the startEdit() method doesn't seem to be reached).
Here is my code (Main FX Application code, POJO, FXML code created in Scene Builder, Controller code, and CustomTextFieldTableCell class):
Main.java
package test;
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 {
#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();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
POJO
package test;
import javafx.beans.property.SimpleStringProperty;
class TestObject {
private final SimpleStringProperty field1 = new SimpleStringProperty();
private final SimpleStringProperty field2 = new SimpleStringProperty();
public SimpleStringProperty field1Property() {
return field1;
}
public void setField1(String string) {
field1.set(string);
}
public SimpleStringProperty field2Property() {
return field2;
}
public void setField2(String string) {
field2.set(string);
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="425.0" prefWidth="517.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.171" fx:controller="test.FXMLDocumentController">
<children>
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<TableView fx:id="tableView" layoutX="14.0" layoutY="52.0" prefHeight="359.0" prefWidth="489.0" AnchorPane.bottomAnchor="14.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="52.0">
<columns>
<TableColumn fx:id="col1" prefWidth="150.0" text="C1" />
<TableColumn fx:id="col2" minWidth="0.0" prefWidth="141.0" text="C2" />
</columns>
</TableView>
<Button fx:id="btnAdd" layoutX="14.0" layoutY="14.0" onAction="#handleAdd" text="_Add" />
</children>
</AnchorPane>
FXMLDocumentController.java
package test;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
public class FXMLDocumentController implements Initializable {
#FXML
private Button btnAdd;
#FXML
private TableView<TestObject> tableView;
#FXML
private TableColumn<TestObject, String> col1;
#FXML
private TableColumn<TestObject, String> col2;
#FXML
private void handleAdd(ActionEvent event) {
TestObject testObject = new TestObject();
tableView.getItems().add(testObject);
tableView.edit(tableView.getItems().indexOf(testObject), col1);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
col1.setCellValueFactory(cellData -> cellData.getValue().field1Property());
col1.setCellFactory((final TableColumn<TestObject, String> column) -> new CustomTextFieldTableCell());
col1.setOnEditStart((TableColumn.CellEditEvent<TestObject, String> event) -> {
//Inside code doesn't seem to ever get reached
System.out.println("Edit started");
});
col2.setCellValueFactory(cellData -> cellData.getValue().field2Property());
col2.setCellFactory((final TableColumn<TestObject, String> column) -> new TableCell<>());
}
}
CustomTextFieldTableCell.java
package test;
import javafx.event.ActionEvent;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
public class CustomTextFieldTableCell extends TableCell<TestObject, String> {
private final TextField textField;
public CustomTextFieldTableCell() {
textField = new TextField();
textField.setOnAction((ActionEvent event) -> {
commitEdit(textField.getText());
event.consume();
});
textField.prefWidthProperty().bind(prefWidthProperty().subtract(3));
}
/** {#inheritDoc} */
#Override
public void startEdit() {
if (!isEditable()
|| getTableView() == null
|| getTableColumn() == null
|| getTableView() != null && !getTableView().isEditable()
|| getTableColumn() != null && !getTableColumn().isEditable()) {
return;
}
super.startEdit();
if (isEditing()) {
/*
textField.setText(getItem().trim());
this.setText(textField.getText());
*/
//Platform.runLater(() -> textField.requestFocus());
textField.requestFocus();
}
}
/** {#inheritDoc} */
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (empty) {
setGraphic(null);
} else {
textField.setText(item == null ? "" : item.trim());
setGraphic(textField);
}
}
}
What visually occurs:
What I need my code to do is to have caret placed inside the TextField or TextField focused, but this doesn't occur with my code right now.
Any idea what I am missing out?

Setting menu items css font style has no visible effect

I would like to change the MenuItem's text to bold when the user clicks on it. To manage to do this I'm using setStyle, but unfortunately it has no visible effect. The css makes no effect when the user selects the MenuItem option. The css styling only has an effect in initialize method.
I created a minimal reproducible example for the problem:
MAIN:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public final class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
Scene scene = new Scene(root, 850.0, 650.0);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
MAIN FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane prefHeight="475.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="MainController">
<top>
<MenuBar BorderPane.alignment="CENTER">
<menus>
<Menu fx:id="menu" mnemonicParsing="false" text="Switch Chart">
<items>
<MenuItem text="MenuItem1" />
<MenuItem onAction="#setFontBold" text="MenuItem2" />
</items>
</Menu>
</menus>
</MenuBar>
</top>
</BorderPane>
MAIN CONTROLLER:
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
public class MainController implements Initializable {
#FXML private Menu menu;
private List<MenuItem> menuItems;
#Override
public void initialize(URL location, ResourceBundle resources) {
menuItems = menu.getItems();
menuItems.get(0).setStyle("-fx-font-weight: bold");
}
public void setFontBold() {
menuItems.get(1).setStyle("-fx-font-weight: bold");
System.out.println(menuItems.get(1).getStyle());
System.out.println("Font set to bold in menuitem with index 1 has no effect...");
}
}
So I get the following css effects, even after selecting the first indexed menu item (so onAction=#setFontBold not changes anything visually).
No bold effect. - only if you set the style in initialize()
I'm curious about why it makes no difference? It definitely sets the style to bold, but it is displayed as a regular font even after changing it to bold.
EDIT: JavaFX version: 8.0.211-b10
If I would have to stick to javafx 8 and switching was the requirement, I would have used RadioMenuItem. It does the job as expected, but does not make MenuItem text bold.
From official docs, I found following excerpt that would be useful.
ToggleGroup toggleGroup = new ToggleGroup();
RadioMenuItem radioItem1 = new RadioMenuItem("Option 1");
radioItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println("radio toggled");
}
});
radioItem1.setToggleGroup(toggleGroup);
RadioMenuItem radioItem2 = new RadioMenuItem("Option 2");
radioItem.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println("radio toggled");
}
});
radioItem2.setToggleGroup(toggleGroup);

Javafx Issue in dragging selected text from TextArea

I have strange problem while dragging selected text from TextArea. The text is properly selected but when iam dragging to be place on the target position, the selection of text is changed, it reduce the selection for 2-3 characters randomly.
Here is the complete class :
public class DnDMainController extends Application {
ClipboardContent cb = new ClipboardContent();
ObservableList<String> list = FXCollections.observableArrayList();
#FXML
private TextArea sourceText;
#FXML
private ListView<String> listView;
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/main/DnD.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Simple Drag and Drop ExampleGame");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#FXML
void _detectDrag(MouseEvent event) {
Dragboard db = sourceText.startDragAndDrop(TransferMode.COPY);
cb.putString(sourceText.getSelectedText());
db.setContent(cb);
event.consume();
}
#FXML
void _dragExited(DragEvent event) {
String st = event.getDragboard().getString();
if (!(list.contains(st.trim()))) {
list.add(st);
listView.getItems().addAll(list);
}}
}
[![gif for DnD Issue][1]][1]
I have tried the same on TextField and it is working perfectly on TextField. But unfortunatelly i can not use the TextField due to large string of text. I dont know what Iam doing wrong...
FXML CODE:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="549.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.DnDMainController">
<children>
<TextArea fx:id="sourceText" layoutY="273.0" onDragDetected="#_detectDrag" prefHeight="127.0" prefWidth="550.0" text="There was once a velveteen rabbit, and in the beginning he was really splendid. He was fat and bunchy, as a rabbit should be; his coat was spotted brown and white, he had real thread whiskers, and his ears were lined with pink sateen. On Christmas morning, when he sat wedged in the top of the Boy’s stocking, with a sprig of holly between his paws, the effect was charming." wrapText="true">
<font>
<Font size="19.0" />
</font></TextArea>
<ListView fx:id="listView" layoutY="40.0" onDragExited="#_dragExited" onMouseClicked="#_callContext" prefHeight="200.0" prefWidth="516.0" />
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="-2.0" layoutY="2.0" prefHeight="38.0" prefWidth="550.0" text="List of Words" textAlignment="CENTER" />
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="7.0" layoutY="240.0" prefHeight="32.0" prefWidth="542.0" text="Story" textAlignment="CENTER" />
</children>
</AnchorPane>```
I ran into a few problems trying to implement this in a straightforward way. I had to create a toggle that would allow me to select text and then drag the text. During the drag, I noticed that it would not grab all of the selected text unless the drag was started at the end of the selected text. I fixed that by grabbing the selected text after it was selected and the toggle mode is changed to drag mode. MCVE below.
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Parent;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Test.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Simple Drag and Drop ExampleGame");
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="583.0" prefWidth="851.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javaapplication12.TestController">
<children>
<ListView fx:id="listView" layoutX="326.0" layoutY="14.0" onDragDropped="#dragDropped" onDragEntered="#dragEntered" onDragOver="#dragOver" prefHeight="200.0" prefWidth="200.0" />
<TextArea fx:id="textArea" layoutX="224.0" layoutY="274.0" onDragDetected="#dragDetected" prefHeight="247.0" prefWidth="426.0" text="There was once a velveteen rabbit, and in the beginning he was really splendid. He was fat and bunchy, as a rabbit should be; his coat was spotted brown and white, he had real thread whiskers, and his ears were lined with pink sateen. On Christmas morning, when he sat wedged in the top of the Boy’s stocking, with a sprig of holly between his paws, the effect was charming." wrapText="true" />
<ToggleButton fx:id="tbtnDragMode" layoutX="44.0" layoutY="26.0" mnemonicParsing="false" onAction="#handleTbtnDragMode" text="Select Text Mode" />
</children>
</AnchorPane>
Controller
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleButton;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
public class TestController implements Initializable {
#FXML TextArea textArea;
#FXML ListView listView;
#FXML ToggleButton tbtnDragMode;
ObservableList<String> list = FXCollections.observableArrayList();
String selectedText = "";
#Override
public void initialize(URL url, ResourceBundle rb) {
listView.setItems(list);
list.add("test");
}
#FXML private void handleTbtnDragMode(ActionEvent actionEvent)
{
if(tbtnDragMode.isSelected())
{
System.out.println("Drag Mode On");
tbtnDragMode.setText("Drag Mode");
selectedText = textArea.getSelectedText().isBlank() ? "" : textArea.getSelectedText();
}
else {
System.out.println("Drag Mode Off");
tbtnDragMode.setText("Select Text Mode");
}
}
#FXML
private void dragDetected(MouseEvent event) {
System.out.println("drag detected 1");
System.out.println(tbtnDragMode.isSelected());
if(tbtnDragMode.isSelected())
{
System.out.println("drag detected 2");
/* drag was detected, start a drag-and-drop gesture*/
/* allow any transfer mode */
Dragboard db = textArea.startDragAndDrop(TransferMode.ANY);
/* Put a string on a dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(selectedText);
db.setContent(content);
event.consume();
}
}
#FXML
private void dragEntered(DragEvent event) {
System.out.println("dragEntered");
event.consume();
}
#FXML
private void dragDropped(DragEvent event)
{
System.out.println("Drag dropped");
/* data dropped */
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
list.add(db.getString());
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
}
#FXML
private void dragDone(DragEvent event) {
/* the drag and drop gesture ended */
/* if the data was successfully moved, clear it */
System.out.println("drag done");
if (event.getTransferMode() == TransferMode.MOVE) {
//clear textarea selection
System.out.println("drag done");
}
event.consume();
}
#FXML
private void dragExited(DragEvent event) {
System.out.println("dragExited");
event.consume();
}
#FXML
private void dragOver(DragEvent event) {
/* data is dragged over the target */
/* accept it only if it is not dragged from the same node
* and if it has a string data */
System.out.println(event.getGestureSource() + " - " + event.getTarget());
if (event.getGestureSource() != event.getTarget() &&
event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
}
}

How to update a scene without refreshing the scene after receiving data from another class/controller

I'm working on a personal weather project that contains Main.fxml, MainController.java, EnterCityDocument.fxml, and EnterCityDocumentController.java.
Main.fxml: contains a border-pane and at its center it has a ListView that displays names of cities. It also has a "Add" Button to open a new modal window(EnterCityDocument.fxml) to add a city to its ListView.
EnterCityDocument.fxml: has a listView that contains names of cities and a "Select" button to select a city the user wants to display in Main.fxml. When the user clicks the "Select" Button, the modal window (EnterCityDocument.fxml) closes and the Main.fxml continues to run.
MainController.java is the parent class of EnterCityDocumentController.java.
I've been looking for passing data from the child class(EnterCityDocumentController.java) to the parent class (MainController.java) and found a way to do it, but all of the methods i've found require to refresh the MainController.java class whenever the user selects a city in EnterCityDocumentController.java.
Is there a way to update the Main.fxml without refreshing the main scene when a new city is added into the listView of the Main.fxml?
Hope my question is clear enough. If you need a further explanation/code, please let me know!
All you need to do is arrange for the new city from the dialog to be added to the list view's backing list. Since you are using showAndWait() this is very easy: just define a method in the EnterCityDocumentController class that returns the new city, and call it after showAndWait():
public class EnterCityDocumentController {
#FXML
private TextField cityNameField ;
// other fields, etc...
#FXML
private void okButtonPressed() {
// just close the window:
cityNameField.getScene().getWindow().hide();
}
public City getUserCity() {
return new City(cityNameField.getText());
}
}
Then in the main controller:
public class MainController {
#FXML
private ListView<City> listView ;
// handler method:
#FXML
public void addNewCity() throws IOException {
FXMLLoader loader = new FXMLLoader(EnterCityDocumentController.class.getResource("EnterCityDocument.fxml"));
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
stage.setScene(scene);
stage.showAndWait();
listView.getItems().add(controller.getUserCity());
}
}
Here is a SSCCE:
app/Main.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.MainController">
<center>
<ListView fx:id="listView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
<bottom>
<Button mnemonicParsing="false" onAction="#addNewElement" text="Add..." BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
</BorderPane.margin>
</Button>
</bottom>
</BorderPane>
app/AddNewElement.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane hgap="5.0" vgap="5.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.AddNewElementController">
<rowConstraints>
<RowConstraints />
<RowConstraints />
</rowConstraints>
<columnConstraints>
<ColumnConstraints halignment="CENTER" />
<ColumnConstraints halignment="CENTER" />
</columnConstraints>
<children>
<TextField fx:id="textField" GridPane.columnSpan="2" />
<Button defaultButton="true" mnemonicParsing="false" onAction="#ok" text="OK" GridPane.rowIndex="1" />
<Button cancelButton="true" mnemonicParsing="false" onAction="#cancel" text="Cancel" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</GridPane>
app/AddNewElementController.java:
package app;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AddNewElementController {
#FXML
private TextField textField ;
private boolean approved ;
public boolean isApproved() {
return approved ;
}
public String getUserText() {
return isApproved() ? textField.getText() : null ;
}
#FXML
private void cancel() {
approved = false ;
hide();
}
#FXML
private void ok() {
approved = true ;
hide();
}
private void hide() {
textField.getScene().getWindow().hide();
}
}
app/MainController.java:
package app;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#FXML
private ListView<String> listView ;
#FXML
private void addNewElement() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNewElementController.class.getResource("AddNewElement.fxml"));
Parent root = loader.load();
AddNewElementController controller = loader.getController();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(listView.getScene().getWindow());
stage.setScene(scene);
stage.showAndWait();
if (controller.isApproved()) {
listView.getItems().add(controller.getUserText());
}
}
}
app/Main.java:
package app;
import java.io.IOException;
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 {
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("Main.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Another, very slightly different approach, is to pass the listview's backing list to the second controller. Then the second controller can add the item directly to the list (and the list view will automatically update, as before). This approach can be used even if you are not using showAndWait() (it doesn't rely on the code blocking until the user dismisses the window).
package app;
import java.util.List;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class AddNewElementController {
#FXML
private TextField textField ;
private List<String> itemList ;
public void setItemList(List<String> itemList) {
this.itemList = itemList ;
}
#FXML
private void cancel() {
hide();
}
#FXML
private void ok() {
itemList.add(textField.getText());
hide();
}
private void hide() {
textField.getScene().getWindow().hide();
}
}
and
package app;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
#FXML
private ListView<String> listView ;
#FXML
private void addNewElement() throws IOException {
FXMLLoader loader = new FXMLLoader(AddNewElementController.class.getResource("AddNewElement.fxml"));
Parent root = loader.load();
AddNewElementController controller = loader.getController();
controller.setItemList(listView.getItems());
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(listView.getScene().getWindow());
stage.setScene(scene);
stage.showAndWait();
}
}

javafx : pass parameter to the eventhandler function in fxml file

Is there a way to call the event handler method from the fxml file which holds parameter? please find the files:
My Fxml Looks like:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.cognizant.iotworkbench.View.AddImageController">
<children>
<Label layoutX="270.0" layoutY="14.0" text="Add Sensor" />
<BorderPane layoutY="-1.0" prefHeight="400.0" prefWidth="602.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<left>
<HBox fx:id="source" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="sourceimg" fitHeight="114.0" fitWidth="142.0" onDragDetected="#setUpGestureSource" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="file:Images/Project.png" />
</image>
</ImageView>
</children>
</HBox>
</left>
<right>
<HBox fx:id="target" onDragDropped="#setUpGestureTarget" onDragOver="#setUpGestureTarget" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</right>
</BorderPane>
</children>
</AnchorPane>
My controller class looks like
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
/**
* FXML Controller class
*
*/
public class AddImage implements Initializable,ControlledScreen {
ScreensController myController;
/**
* Initializes the controller class.
*/
#FXML
final HBox target=new HBox();
#FXML
final HBox source=new HBox();
#FXML
final ImageView sourceimg=new ImageView();
#FXML
public void setUpGestureSource()
{
System.out.println("source");
source.setOnDragDetected(new EventHandler <MouseEvent>() {
#Override
public void handle(MouseEvent event) {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow MOVE transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.COPY);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
Image sourceImage = sourceimg.getImage();
content.putImage(sourceImage);
db.setContent(content);
event.consume();
}
});
}
#FXML
public void setUpGestureTarget(){
System.out.println("target");
target.setOnDragOver(new EventHandler <DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
if(db.hasImage()){
event.acceptTransferModes(TransferMode.COPY);
}
event.consume();
}
});
target.setOnDragDropped(new EventHandler <DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
if(db.hasImage()){
insertImage(db.getImage(), target);
event.setDropCompleted(true);
}else{
event.setDropCompleted(false);
}
event.consume();
}
});
}
void insertImage(Image i, HBox hb){
ImageView iv = new ImageView();
iv.setImage(i);
setUpGestureSource();
hb.getChildren().add(iv);
}
#Override
public void setScreenParent(ScreensController screenParent) {
myController=screenParent;
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
Drag and Drop event is not getting fired. I dont know what's wrong with the code. Please help
All methods that are annotated with #FXML may or may not have a parameter. If you need a parameter, you can directly pass the specific Event as a parameter to the method.
Let us take an example and say you have an onDragOver event added for a HBox in the fxml.
<HBox fx:id="targetBox" onDragOver="#setupGestureTarget">
...
</HBox>
Then you may have a method which does the needful inside the controller class.
#FXML
public void setupGestureTarget(DragEvent event) {
Dragboard db = event.getDragboard();
if(db.hasImage()){
event.acceptTransferModes(TransferMode.COPY);
}
event.consume();
}
If you need the source node for which the method was called, you can get an access to it by getSource() of the event, then type-caste it to Node.
public void setupGestureTarget(DragEvent event) {
...
((Node)event.getSource()).setId("myHbox");
System.out.println(targetBox.getId()); // Prints - myHbox
}
Note : For using methods specific to HBox, you can type-caste it to a HBox instead of a Node.
EDIT - As per user comments and updates
There are multiple issues with your approach. Let me address them all.
1.Values to the fields annotated with #FXML are injected while the FXML is getting loaded and should not carry new keyword.
#FXML
final HBox target=new HBox();
should be replaced by
#FXML
final HBox target;
2.As I have already stated your method can have a parameter which defines the Event. So the methods setUpGestureSource() and setUpGestureTarget() can be defined as :
#FXML
public void setUpGestureSource(DragEvent event) {
...
}
#FXML
public void setUpGestureTarget(DragEvent event) {
...
}
3.The method setUpGestureSource is called when a drag-event occurs on the ImageView, so you don't need to add another EventHandler for it.
#FXML
public void setUpGestureSource(MouseEvent event) {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
* allow MOVE transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.COPY);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
Image sourceImage = sourceimg.getImage();
content.putImage(sourceImage);
db.setContent(content);
event.consume();
}
Similarly, change the other method as well.

Resources