How to make drag and drop action in JavaFX 2.2 with Swing? - javafx-2

I have a problem with drag and drop event on JFXPanel that on located JPanel. When i push drag message to DragBoard, javaFX part of application doesnt work anymore. I think its about swing event mechanizm but i am not sure. There is no problem with other events. It made me confused. Is there any solution to this problem? Thanks in advance.
public class MyScene extends Scene {
public MyScene(VBox vBoxMainLayout) {
super(vBoxMainLayout);
HBox hBox = new HBox();
hBox.setPrefSize(10000, 10000);
hBox.setSpacing(40);
Button buttonSource = new Button("Source");
buttonSource.setMinSize(60, 30);
buttonSource.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
Dragboard db = startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
String message = "Drag operatation is done";
content.putString(message);
db.setContent(content);
event.consume();
}
});
buttonSource.setOnDragDone(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
event.consume();
}
});
TextArea textAreaTarget = new TextArea();
textAreaTarget.setMinSize(200, 500);
hBox.getChildren().add(buttonSource);
hBox.getChildren().add(textAreaTarget);
vBoxMainLayout.getChildren().add(hBox);
}
}
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
VBox vBoxMainLayout = new VBox();
MyScene myScene = new MyScene(vBoxMainLayout);
JFrame frame = new JFrame();
JFXPanel arg0 = new JFXPanel();
arg0.setScene(myScene);
frame.getContentPane().add(arg0);
frame.setVisible(true);
}
}

It was a known deadlock in JavaFX 2.1 and pushed to 2.2 (thats what i learned from oracle ) but i guess it stil not solved.

Related

Display a ProgressIndicator during an async loading of ListView items

I am trying to display a ProgressIndicator while performing an async background ListView item loading. The behaviour that I desire is:
Before start loading the ListView items, display a ProgressIndicator with a indeterminate progress;
Asynchronously start loading the ListView items;
After the ListView items loading was finished, hide the ProgressIndicator.
Here is a ssce of my unsuccessful attempt:
public class AsyncLoadingExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<String>();
final ObservableList<String> listItems = FXCollections.observableArrayList();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
final Button button = new Button("Click me to start loading");
primaryStage.setTitle("Async Loading Example");
listView.setPrefSize(200, 250);
listView.setItems(listItems);
loadingIndicator.setVisible(false);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// I have hoped it whould start displaying the loading indicator (actually, at the end of this
// method execution (EventHandler.handle(ActionEvent))
loadingIndicator.setVisible(true);
// asynchronously loads the list view items
Platform.runLater(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2000l); // just emulates some loading time
// populates the list view with dummy items
while (listItems.size() < 10) listItems.add("Item " + listItems.size());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
loadingIndicator.setVisible(false); // stop displaying the loading indicator
}
}
});
}
});
VBox root = VBoxBuilder.create()
.children(
StackPaneBuilder.create().children(listView, loadingIndicator).build(),
button
)
.build();
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
}
In this example, the ListView items are loaded asynchronously. However, the ProgressIndicator do not show up. Still in this example, if I omit all the Platform.runLater(...) code, the ProgressIndicator shows up, but, of course, the ListView items are not loaded.
Thus, how can I achieve the desired behaviour?
Crferreira's self answer is perfectly fine.
This answer just demonstrates an alternate implementation that does not require the use of any Platform.runLater calls and instead uses a JavaFX Task (as well as Java 8 lambda syntax).
import javafx.application.Application;
import javafx.collections.*;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.*;
public class AsyncLoadingExample extends Application {
private void loadItems(final ObservableList<String> listItems, final ProgressIndicator loadingIndicator) {
if (loadingIndicator.isVisible()) {
return;
}
// clears the list items and start displaying the loading indicator at the Application Thread
listItems.clear();
loadingIndicator.setVisible(true);
// loads the items at another thread, asynchronously
Task listLoader = new Task<List<String>>() {
{
setOnSucceeded(workerStateEvent -> {
listItems.setAll(getValue());
loadingIndicator.setVisible(false); // stop displaying the loading indicator
});
setOnFailed(workerStateEvent -> getException().printStackTrace());
}
#Override
protected List<String> call() throws Exception {
final List<String> loadedItems = new LinkedList<>();
Thread.sleep(2000l); // just emulates some loading time
// populates the list view with dummy items
while (loadedItems.size() < 10) {
loadedItems.add("Item " + loadedItems.size());
}
return loadedItems;
}
};
Thread loadingThread = new Thread(listLoader, "list-loader");
loadingThread.setDaemon(true);
loadingThread.start();
}
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<>();
final ObservableList<String> listItems = FXCollections.observableArrayList();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
final Button button = new Button("Click me to start loading");
primaryStage.setTitle("Async Loading Example");
listView.setPrefSize(200, 250);
listView.setItems(listItems);
loadingIndicator.setVisible(false);
button.setOnAction(event -> loadItems(listItems, loadingIndicator));
VBox root = new VBox(
new StackPane(
listView,
loadingIndicator
),
button
);
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
The problem is that, at the presented example, I am misusing the Platform.runLater(...) method, and consequently, the JavaFX Application Thread.
As mentioned at the Platform.runLater() method documentation, this method
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future.
And, the JavaFX Application Thread is the thread from which the JavaFX scene graph can be accessed and modified by the developer code, visually reflecting the performed modifications.
Thus, when I start loading the ListView items from this thread, the UI becomes unresponsive (this is also stated here) until the loading is finished.
To solve the problem, the ListView items must be loaded at another thread and only the ListView update must be performed at Application Thread.
The above correction is presented in the following:
public class AsyncLoadingExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<String>();
final ObservableList<String> listItems = FXCollections.observableArrayList();
final ProgressIndicator loadingIndicator = new ProgressIndicator();
final Button button = new Button("Click me to start loading");
primaryStage.setTitle("Async Loading Example");
listView.setPrefSize(200, 250);
listView.setItems(listItems);
loadingIndicator.setVisible(false);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
final List<String> loadedItems = new LinkedList<String>();
// clears the list items and start displaying the loading indicator at the Application Thread
listItems.clear();
loadingIndicator.setVisible(true);
// loads the items at another thread, asynchronously
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(2000l); // just emulates some loading time
// populates the list view with dummy items
while (loadedItems.size() < 10) loadedItems.add("Item " + loadedItems.size());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// just updates the list view items at the
// Application Thread
Platform.runLater(new Runnable() {
#Override
public void run() {
listItems.addAll(loadedItems);
loadingIndicator.setVisible(false); // stop displaying the loading indicator
}
});
}
}
}).start();
}
});
VBox root = VBoxBuilder.create()
.children(
StackPaneBuilder.create().children(listView, loadingIndicator).build(),
button
)
.build();
primaryStage.setScene(new Scene(root, 200, 250));
primaryStage.show();
}
}

JavaFX: How to disable a button for a specific amount of time?

I want to disable a button for a specific time in JavaFX application. Is there any option to do this? If not, is there any work around for this?
Below is my code in application. I tried Thread.sleep, but i know this is not the good way to stop the user from clicking on next button.
nextButton.setDisable(true);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(delayTime),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
nextButton.setDisable(false);
}
}));
animation.setCycleCount(1);
animation.play();
You could use the simple approach of a thread that provides the relevant GUI calls (through runLater() of course):
new Thread() {
public void run() {
Platform.runLater(new Runnable() {
public void run() {
myButton.setDisable(true);
}
}
try {
Thread.sleep(5000); //5 seconds, obviously replace with your chosen time
}
catch(InterruptedException ex) {
}
Platform.runLater(new Runnable() {
public void run() {
myButton.setDisable(false);
}
}
}
}.start();
It's perhaps not the neatest way of achieving it, but works safely.
You could also be using the Timeline:
final Button myButton = new Button("Wait for " + delayTime + " seconds.");
myButton.setDisable(true);
final Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(delayTime),
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
myButton.setDisable(false);
}
}));
animation.setCycleCount(1);
animation.play();
The method to disable a JavaFX control is:
myButton.setDisable(true);
You can implement the time logic programmatically in any way you wish, either by polling a timer or by having this method invoked in response to some event.
If you have created this button instance through FXML in SceneBuilder, then you should assign the button an fx:id so that its reference is automatically injected into your controller object during the loading of the scene graph. This will make it easier for you to work with in your controller code.
If you have created this button programmatically, then you'll already have its reference available in your code.
Or you could use a Service and bind the running property to the disableProperty of the button do you want to disable.
public void start(Stage stage) throws Exception {
VBox vbox = new VBox(10.0);
vbox.setAlignment(Pos.CENTER);
final Button button = new Button("Your Button Name");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Thread.sleep(5000);//Waiting time
return null;
}
};
}
};
button.disableProperty().bind(service.runningProperty());
service.start();
}
});
vbox.getChildren().addAll(button);
Scene scene = new Scene(vbox, 300, 300);
stage.setScene(scene);
stage.show();
}
But the Timeline solution given by Uluk Biy, looks more elegant.

Stage is hidden when dialog is shown

I have this code which displays confirmation dialog to exit application.
public class DialogPanels
{
public void initClosemainAppDialog(final Stage primaryStage)
{
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent event)
{
event.consume(); // Do nothing on close request
// Dialog Stage init
final Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
// Frage - Label
Label label = new Label("Exit from the program");
// Button "Yes"
Button okBtn = new Button("Yes");
okBtn.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
//primaryStage.close();
//dialog.close();
//Platform.exit();
System.exit(0);
}
});
// Button "No"
Button cancelBtn = new Button("No");
cancelBtn.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
primaryStage.show();
dialog.close();
}
});
// Layout for the Button
HBox hbox = new HBox();
hbox.setSpacing(10);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(okBtn);
hbox.getChildren().add(cancelBtn);
// Layout for the Label and hBox
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
vbox.setSpacing(10);
vbox.getChildren().add(label);
vbox.getChildren().add(hbox);
// Stage
Scene scene = new Scene(vbox);
dialog.setScene(scene);
dialog.show();
}
});
}
}
The problem is that when close the main application the dialog box is displayed and the main stage is hidden. I want to display the dialog box in front of the main stage. Can you help me to correct this?
UPDATE
I tested this code, it's working but when the dialog is displayed the mainstage is not responsible(frozen). How I an make the mainstage responsible when I display dialog?
Consume the closing event and set the owner of the stage if you do not want to see another window when the windows are minimized:
#Override
public void handle(WindowEvent event)
{
event.consume(); // Do nothing on close request
// Dialog Stage init
final Stage dialog = new Stage();
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.initOwner(primaryStage);
// other stuff
}
});
You need to set the proper relationships between primaryStage and dialog stage. Here's a hint to get you going:
...
dialog.initOwner(primaryStage);
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.showAndWait();
You can find more information in Oracle's JavaFX 2 JavaDocs.
More example code (edit)
I'm using setOnHiding(..) instead of setOnCloseRequest(..):
stage.setOnHiding(new AskUserIfHeReallyWantsToQuitWindowHandler(stage));
I extracted your code into a seperate event handler class and fixed the issues I mentioned (sorry, I am little short on time right now):
public class AskUserIfHeReallyWantsToQuitWindowHandler implements EventHandler<WindowEvent> {
private final Stage primaryStage;
public AskUserIfHeReallyWantsToQuitWindowHandler(final Stage primaryStage) {
Objects.requireNonNull(primaryStage);
this.primaryStage = primaryStage;
}
#Override
public void handle(final WindowEvent event) {
event.consume();
final Stage dialog = new Stage();
final Button okBtn = new Button("Yes");
okBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent event) {
dialog.close();
primaryStage.close();
}
});
// Button "No"
final Button cancelBtn = new Button("No");
cancelBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(final ActionEvent event) {
dialog.close();
Platform.runLater(new Runnable() {
#Override
public void run() {
primaryStage.show();
}
});
}
});
// Layout for the Button
final HBox hbox = new HBox();
hbox.setSpacing(10);
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().add(okBtn);
hbox.getChildren().add(cancelBtn);
// Layout for the Label and hBox
final VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
vbox.setSpacing(10);
vbox.getChildren().add(new Label("Do your really want to exit?"));
vbox.getChildren().add(hbox);
// Stage
final Scene scene = new Scene(vbox);
dialog.setScene(scene);
dialog.initOwner(primaryStage);
dialog.initModality(Modality.NONE);
dialog.showAndWait();
}
}

JavaFx 2.2 & Fullscreen mode

Good day!
I try my first application JavaFx. I set the full-screen mode and press the button shows a dialog. When the dialog appears the main window loses its full-screen.
Code:
public class Test1 extends Application {
#Override
public void start(final Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Stage dialogStage = new Stage(StageStyle.UTILITY);
dialogStage.initModality(Modality.APPLICATION_MODAL);
dialogStage.setScene(new Scene(VBoxBuilder.create().
children(new Text("Hi"), new Button("Ok.")).
alignment(Pos.CENTER).padding(new Insets(5)).build()));
dialogStage.show();
System.out.println(dialogStage.getOwner()==primaryStage.getOwner());
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Rectangle2D r = Screen.getPrimary().getBounds();
Scene scene = new Scene(root, r.getWidth(), r.getHeight());
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
OS: Windows 7
You need to
dialogStage.initOwner(primaryStage);

ContextMenu (Popup) always on

I have a problem with a Context menu in JavaFx 2:it never disappers when I left click on the graph of the JFXPanel
Does anybody knows how to solve this problem?
Thanks
Here is my code
final ContextMenu cm = new ContextMenu();
MenuItem chartItem1 = new MenuItem("Chart Settings");
cm.getItems().add(chartItem1);
getScene().setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if(cm.isShowing()){
cm.hide();
}
if(mouseEvent.getButton() == MouseButton.SECONDARY)
{
cm.show(getScene().getRoot(), mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
}
});
chartItem1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
dialogs.ChartFormat cs = new dialogs.ChartFormat(null, true);
cs.setLocationRelativeTo(null);
cs.setVisible(true);
}
});
Reproduced the described behavior. Don't know the reason but you can use ContextMenu#hide():
final ContextMenu cm = new ContextMenu();
MenuItem menuItem = new MenuItem("Item 1");
menuItem.addEventHandler(EventType.ROOT, new EventHandler<Event>() {
#Override
public void handle(Event t) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel messagePane = new JPanel();
messagePane.add(new JLabel("label"));
JDialog jDialog = new JDialog();
jDialog.getContentPane().add(messagePane);
jDialog.pack();
jDialog.setVisible(true);
}
});
}
});
cm.getItems().add(menuItem);
scene.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
// if(cm.isShowing())
cm.hide();
if (mouseEvent.getButton() == MouseButton.SECONDARY) {
cm.show(lineChart, mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
}
});
Also you can check out these links:
http://pixelduke.wordpress.com/2011/12/11/popupmenu-in-javafx/
http://javafx-jira.kenai.com/browse/RT-17853
http://javafx-jira.kenai.com/browse/RT-14899
Adding sample code to your question would be more descriptive.

Resources