Change dragView during drag with JavaFX - javafx

-- EDIT
I just found out that it is not possible, but is there anything else I can do to generate visuals like I want?
I'm using JavaFX to set a dragView on the DragBoard, but I can't seem to find a way to change that image during a drag operation.
I've tried to use setDragView(MyImage) when the drag over event was fired, but that didn't work.
Is this at all possible, or should I find another way?
Code below:
// Called when drag starts
public void handleBlockDragDetected(PuzzleBlockView puzzleBlockView, PuzzleBlock puzzleBlock, GridPane blockPane)
{
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.put(GameController.BLOCK_FORMAT, puzzleBlock);
Dragboard dragboard = blockPane.startDragAndDrop(TransferMode.MOVE);
dragboard.setDragViewOffsetX(20);
dragboard.setDragViewOffsetY(20);
dragboard.setDragView(puzzleBlockView.getSnapshot());
dragboard.setContent(clipboardContent);
blockPane.setVisible(false);
draggingBlock = puzzleBlockView;
}
// Called on dragOver detected
public void handleBlockDragOver(DragEvent event)
{
Dragboard dragboard = event.getDragboard();
if (dragboard.hasContent(BLOCK_FORMAT) && draggingBlock != null) {
dragboard.setDragView(new Image(getClass().getResource("/img/flag.png").toString()));
event.acceptTransferModes(TransferMode.MOVE);
}
}

Related

JavaFX custom dialog set Layout of node

We created a Custom Dialog without an FXML file. We are using JavaFX 8.
The dialog loads and functions as expected but we can not move the Buttons and the TextField to enhance the styling.
We have tried to use tf.setLayoutY(50) this has no effect.
We used this tf.setPromptText("This Works ?") and it works.
We would rather not use css to accomplish this styling.
And we will consider a FXML file if we can keep the two event handlers that force data to be entered in the TextField.
So the question is: How to style this Custom Dialog?
The code is a mess as it includes some concepts we tried:
public void CustomDialog() {
Dialog dialog = new Dialog<>();
dialog.setResizable(false);
final Window window = dialog.getDialogPane().getScene().getWindow();
stage = (Stage) window;
stage.setMinHeight(600);
stage.setMinWidth(400);
TextField tf = new TextField();
tf.setLayoutX(10);
tf.setLayoutY(50);
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
dialog.getDialogPane().getChildren().add(tf);
dialog.getDialogPane().setContent(tf);
// Create an event filter that consumes the action if the text is empty
EventHandler<ActionEvent> filter = event -> {
if (tf.getText().isEmpty()) {
event.consume();
}
};
// lookup the buttons
ButtonBase okButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.OK);
Button cancelButton = (Button) dialog.getDialogPane().lookupButton(ButtonType.CANCEL);
// add the event-filter
okButton.addEventFilter(ActionEvent.ACTION, filter);
cancelButton.addEventFilter(ActionEvent.ACTION, filter);
stage.setOnCloseRequest(event -> {
if (tf.getText().isEmpty()) {
event.consume();
}
}
//Scene scene = new Scene(root);
//dialogStage.setScene(scene);
dialog.initModality(Modality.APPLICATION_MODAL);
//dialogStage.setAlwaysOnTop(true);
//dialogStage.setResizable(false);
tf.setPromptText("This Works ?");
tf.requestFocus();// This does not work
dialog.showAndWait();
}
Grendel we enhanced your answer so anyone who comes by and sees the code you posted in your question will understand as you said it was a mess
Your posted answer was real old school but less work perhaps than building a FXML file
Besides it is good to know some old school tricks
public void NewDialog(){
Label lblAmt = new Label("Enter Amount");
Button btnOK = new Button("OK");
TextField txtAmt = new TextField();
AnchorPane secondaryLayout = new AnchorPane();
secondaryLayout.setStyle("-fx-border-color:red;-fx-border-width:10px; -fx-background-color: lightblue;");
secondaryLayout.getChildren().addAll(lblAmt,btnOK,txtAmt);
lblAmt.setLayoutX(30);
lblAmt.setLayoutY(30);
txtAmt.setLayoutX(164);
txtAmt.setLayoutY(25);
txtAmt.setMaxWidth(116);
btnOK.setLayoutX(190);
btnOK.setLayoutY(100);
btnOK.setStyle("-fx-font-size: 18px;-fx-font-weight: bold;");
lblAmt.setStyle("-fx-font-size: 18px;-fx-font-weight: bold;");
txtAmt.setStyle("-fx-font-size: 18px;-fx-font-weight: bold;");
Scene secondScene = new Scene(secondaryLayout, 300, 180);
EventHandler<ActionEvent> filter = event -> {
if(txtAmt.getText().isEmpty()) {
event.consume();
}
};
// New window (Stage)
Stage newWindow = new Stage();
newWindow.initStyle(StageStyle.UNDECORATED);
//newWindow.initModality(Modality.APPLICATION_MODAL);
newWindow.setResizable(false);
newWindow.setTitle("Second Stage");
newWindow.setScene(secondScene);
btnOK.addEventHandler(ActionEvent.ACTION,filter);
btnOK.setOnAction(evt -> {
String str = txtAmt.getText();
System.out.println("################ str "+str);
if(txtAmt.getText().equals("")) {
evt.consume();
txtAmt.requestFocus();
}else{
newWindow.close();
}
});
newWindow.setOnCloseRequest(event -> {
if(txtAmt.getText().isEmpty()) {
event.consume();
}
});
txtAmt.requestFocus();
newWindow.showAndWait();
}

Context menu on TableRow<Object> does not show up on first right click

So I followed this example on using context menu with TableViews from here. I noticed that using this code
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
does not show up on first right click on a row with values. I need to right click on that row again for the context menu to show up. I also tried this code(which is my first approach, but not using it anymore because I've read somewhere that that guide is the best/good practice for anything related about context menu and tableview), and it displays the context menu immediately
if (row.getItem() != null) {
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}
but my problem with this code is it throws a NullPointerException whenever i try to right click on a row that has no data.
What could I possibly do to prevent NullPointerException while having the context menu show up immediately after a right click? In my code, I also have a code that a certain menu item in the context menu will be disabled based on the property of the myObject binded to row, that's why i need the context menu to pop up right away.
I noticed this too with the first block of code. Even if the property of myObject has already changed, it still has a menu item enabled/disabled unless I right click on that row again. I hope that you could help me. Thank you!
Here is a MCVE:
public class MCVE_TableView extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane myBorderPane = new BorderPane();
TableView<People> myTable = new TableView<>();
TableColumn<People, String> nameColumn = new TableColumn<>();
TableColumn<People, Integer> ageColumn = new TableColumn<>();
ContextMenu rowMenu = new ContextMenu();
ObservableList<People> peopleList = FXCollections.observableArrayList();
peopleList.add(new People("John Doe", 23));
nameColumn.setMinWidth(100);
nameColumn.setCellValueFactory(
new PropertyValueFactory<>("Name"));
ageColumn.setMinWidth(100);
ageColumn.setCellValueFactory(
new PropertyValueFactory<>("Age"));
myTable.setItems(peopleList);
myTable.getColumns().addAll(nameColumn, ageColumn);
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>();
row.setOnContextMenuRequested((event) -> {
People selectedRow = row.getItem();
rowMenu.getItems().clear();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
if (selectedRow != null) {
if (selectedRow.getAge() > 100) {
sampleMenuItem.setDisable(true);
}
rowMenu.getItems().add(sampleMenuItem);
}
else {
event.consume();
}
/*if (row.getItem() != null) { // this block comment displays the context menu instantly
rowMenu.show(row, event.getScreenX(), event.getScreenY());
}
else {
// do nothing
}*/
// this requires the row to be right clicked 2 times before displaying the context menu
row.contextMenuProperty().bind(Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(rowMenu)
.otherwise((ContextMenu)null));
});
return row;
});
myBorderPane.setCenter(myTable);
Scene scene = new Scene(myBorderPane, 500, 500);
primaryStage.setTitle("MCVE");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main (String[] args) {
launch(args);
}
}
Here is the People Class
public class People {
SimpleStringProperty name;
SimpleIntegerProperty age;
public People(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = new SimpleIntegerProperty(age);
}
public SimpleStringProperty NameProperty() {
return this.name;
}
public SimpleIntegerProperty AgeProperty() {
return this.age;
}
public String getName() {
return this.name.get();
}
public int getAge() {
return this.age.get();
}
}
Edit: MCVE added
Edit2: Updated the MCVE. Still requires to be right-clicked twice before the contextMenu pops up
Below's a code snippet as a quick demonstration of how-to/where-to instantiate and configure a per-row ContextMenu. It
creates a ContextMenu/MenuItem for each TableRow at the row's instantiation time
creates a conditional binding that binds the menu to the row's contextMenuProperty if not empty (just the same as you did)
configures the contextMenu in an onShowing handler, depending on the current item (note: no need for a guard against null, because the conditional binding will implicitly guarantee to not show the the menu in that case)
The snippet:
myTable.setRowFactory(tv -> {
TableRow<People> row = new TableRow<>() {
ContextMenu rowMenu = new ContextMenu();
MenuItem sampleMenuItem = new MenuItem("Sample Button");
{
rowMenu.getItems().addAll(sampleMenuItem);
contextMenuProperty()
.bind(Bindings
.when(Bindings.isNotNull(itemProperty()))
.then(rowMenu).otherwise((ContextMenu) null));
rowMenu.setOnShowing(e -> {
People selectedRow = getItem();
sampleMenuItem.setDisable(selectedRow.getAge() > 100);
});
}
};
return row;
});

How to handle wrong drop location in JavaFx?

In my GUI I create some dices (ImageView) and I can drag and drop them in one specific GridPane. When I drop a dice into the specific GridPane, the dice disappears from the initial location and it moves to the right position. This works fine only if I choose the right drop location.
The problem is how can I manage the wrong drop location?
Actually if I drop a dice in a wrong location (like outside the Gridpane) the dice disappears like it was moved to the right position.
I want to restore the dice to the original location if the dice isn't placed to the GridPane.
Is there a method can help me to check if I drop into the right location? Or something can prevent to drop into the wrong location?
You can check the transferMode property of the DragEvent passed to the onDragDone event:
dragSource.setOnDragDone(evt -> {
if (evt.getTransferMode() == null) {
System.out.println("drag aborted");
} else {
System.out.println("drag successfully completed");
}
});
Note: this requires you to mark the drag gesture as completed in the onDragDropped event handler using setDropCompleted. Example:
#Override
public void start(Stage primaryStage) {
Button source = new Button("Not dragged yet");
Button target = new Button("target");
HBox root = new HBox(20, source, target);
source.setOnDragDetected(evt -> {
Dragboard db = source.startDragAndDrop(TransferMode.COPY);
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
});
source.setOnDragDone(evt -> {
source.setText(evt.getTransferMode() == null ? "failure" : "success");
});
target.setOnDragOver(evt -> {
if (evt.getDragboard().hasString()) {
evt.acceptTransferModes(TransferMode.COPY);
evt.consume();
}
});
target.setOnDragDropped(evt -> {
String value = evt.getDragboard().getString();
target.setText(value);
evt.setDropCompleted(true);
evt.consume();
});
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

Deleting a file(s) through FileChooser?

I have a JavaFX app using JRE1.8.0 u40. I converted my Swing JFileChooser Open and Save to the newer JavaFX FileChooser Open and Save, Windows7 style Dialog box. But I have not found an equivalent JavaFX FileChooser method to replace the JFileChooser method I'm using for deleting a file(s) as shown below:
public static void deleteFile() throws IOException {
JFileChooser fileDialog = new JFileChooser("C:\\ProgramData\\L1 Art Files\\");
File[] selectedFiles;
fileDialog.setSelectedFiles(null);
// Set frame properties
fileDialog.setDialogTitle("Delete Pixel Art File(s)");
//fileDialog.setLayout(new FlowLayout());
fileDialog.setSize(400, 400);
fileDialog.setVisible(true);
fileDialog.setMultiSelectionEnabled(true); // Allow multiple selection
fileDialog.setVisible(true);
int option = fileDialog.showDialog(null, "Delete");
if (option != JFileChooser.APPROVE_OPTION)
return; //user canceled the
selectedFiles = fileDialog.getSelectedFiles();
if (selectedFiles != null) { //ask the user to replace this file
int response = JOptionPane.showConfirmDialog(null, "Are you sure want to delete this file?",
"Confirm Delete",
JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE);
if (response != JOptionPane.YES_OPTION) return;
}
for (File f : selectedFiles) {
Files.delete(f.toPath());
}
Is there a similar solution to the above for JavaFX using FileChooser or do I use the showOpenDialog(null) with setTitle("Delete Pixel Art File")?
You can easily perform that task using javafx like below :
#FXML
private void onDeleteAction(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Your_title_here");
List<File> selectedFiles = fileChooser.showOpenMultipleDialog(null);
if (selectedFiles != null) {
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Warning !");
alert.setContentText("Are you sure you want to delete these files ?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK) {
for (File selectedFile : selectedFiles) {
selectedFile.delete();
}
}
} else {
System.out.println("Error Selection");
}
}
The above code was very helpful and works best with the other suggestion of checking for null and adding throws IOException to the deleteFile() method.

TreeTableView drag and drop

I'm using a TreeTableView to display the content of a tree. The sorting order in the tree is manual, and I want to be able to drag and drop items.
How can I drag and drop items in a TreeTableView?
One way is to us a 'treeTableView.setRowFactory'. In the 'call' method, you create a row, to which you attach the 'onDragDetected', 'onDragDropped' etc. See example below.
// Create the root, RowContainer is your class contianing row attributes
TreeItem<RowContainer> rootTIFX = new TreeItem<RowContainer>(rowContainerRoot);
// Add leaves under your root.
...
// Create the row factory
treeTableView.setRowFactory(new Callback<TreeTableView, TreeTableRow<RowContainer>>() {
#Override
public TreeTableRow<RowContainer> call(final TreeTableView param) {
final TreeTableRow<RowContainer> row = new TreeTableRow<RowContainer>();
row.setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// drag was detected, start drag-and-drop gesture
TreeItem<RowContainer> selected = (TreeItem<RowContainer>) treeTableView.getSelectionModel().getSelectedItem();
// to access your RowContainer use 'selected.getValue()'
if (selected != null) {
Dragboard db = treeTableView.startDragAndDrop(TransferMode.ANY);
// create a miniature of the row you're dragging
db.setDragView(row.snapshot(null, null));
// Keep whats being dragged on the clipboard
ClipboardContent content = new ClipboardContent();
content.putString(selected.getValue().getName());
db.setContent(content);
event.consume();
}
}
});
row.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
// data is dragged over the target
Dragboard db = event.getDragboard();
if (event.getDragboard().hasString()){
event.acceptTransferModes(TransferMode.MOVE);
}
event.consume();
}});
row.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (event.getDragboard().hasString()) {
if (!row.isEmpty()) {
// This is were you do your magic.
// Move your row in the tree etc
// Here is two examples of how to access
// the drop destination:
int dropIndex = row.getIndex();
TreeItem<RowContainer> droppedon = row.getTreeItem();
success = true;
}
}
event.setDropCompleted(success);
event.consume();
}});
return row;
}
});

Resources