Following this topic Context menu insight JavaFX Task I want to create Context Menu in JavaFX Task. I tested this code:
static private StringBuilder stringBuilder = new StringBuilder();
private static ContextMenu contextMenu;
private static CountDownLatch menuCreated = new CountDownLatch(1);
static synchronized void writeString(String s)
{
stringBuilder.append(s).append("\n");
}
public static BorderPane init(BorderPane bp) throws Exception
{
System.out.println("***** CALLED");
Task task = new Task()
{
#Override
protected Void call() throws Exception
{
writeString("Task started");
writeString(Thread.currentThread().getName() + " is fx thread: "
+ Platform.isFxApplicationThread());
Platform.runLater(new Runnable()
{
#Override
public void run()
{
writeString(Thread.currentThread().getName() + " is fx thread: "
+ Platform.isFxApplicationThread());
try
{
contextMenu = new ContextMenu();
contextMenu.setId("Test ID");
writeString("Created context menu");
menuCreated.countDown();
}
catch (Exception ex)
{
writeString(ex.getMessage());
ex.printStackTrace();
}
finally
{
writeString("Test");
}
}
});
writeString("Task finished");
return null;
}
};
new Thread(task).start();
MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("About");
}
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
MenuItem item3 = new MenuItem("Close");
item3.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
//flow.getChildren().remove(bp);
}
});
contextMenu.getItems().addAll(item1, item2, item3);
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
//contextMenu.hide();
System.out.println("*********************** Shown Context Menu ***!!!!!!!");
contextMenu.show(bp, event.getScreenX(), event.getScreenY());
event.consume();
}
});
bp.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
contextMenu.hide();
}
});
menuCreated.await();
return bp;
}
With this code I set Context Menu for BorderPane. When I click with the right mouse button I see the debug message *********************** Shown Context Menu ***!!!!!!! but there is no context menu. Can you help me to fix this code?
Related
I want to show progress bar while a functionality is running. What is the best way to show it? Basically I am building a program to send multiple mails on a single click. While sending the mail I want to show progress bar while sending the mails.
The best solution in this case is using a Task.
Example:
Task<Parent> yourTaskName = new Task<Parent>() {
#Override
public Parent call() {
// DO YOUR WORK
//method to set progress
updateProgress(workDone, max);
//method to set labeltext
updateMessage(message);
}
};
//ProgressBar
ProgressBar pBar = new ProgressBar();
//Load Value from Task
pBar.progressProperty().bind(yourTaskName.progressProperty());
//New Loading Label
Label statusLabel = new Label();
//Get Text
statusLabel.setText("Loading...");
//Layout
VBox root = new VBox(statusLabel, pBar);
//SetFill Width TRUE
root.setFillWidth(true);
//Center Items
root.setAlignment(Pos.CENTER);
//SetOnSucceeded methode
yourTaskName.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
System.out.println("Finish");
}
});
//Start Thread
Thread loadingThread = new Thread(yourTaskName);
loadingThread.start();
Hope this helps you.
P.S.: The code in the task run as a Thread...
I implemented what you want last time ,If you want to show progressIndicator or progressBar when sending is running ,try this part of code
senderThreadlive = new Thread(new Runnable() {
#Override
public void run() {
try {
Platform.runLater(new Runnable() {
#Override
public void run() {
ProgressIndicator WaitingSend=new ProgressIndicator();
WaitingSend.setProgress(ProgressIndicator.INDETERMINATE_PROGRESS);
WaitingBox.getChildren().add(WaitingSend);//this is an HBOX
SendMailButton.setDisable(true);
SendMailButton.setText("sending in progress");
}
});
//call Your method of sending
SimpleMail.EmailSender.sendEmail(MailSenderTxt.getText(), MotMailTxt.getText(), DestMailTxt.getText(), ObjetMailTxt.getText(), org.jsoup.Jsoup.parse(ContentMail.getHtmlText()).text());
Platform.runLater(new Runnable() {
#Override
public void run() {
WaitingSend.setProgress(0);
WaitingSend.setVisible(false);
SendMailButton.setDisable(false);
SendMailButton.setText("Send");
}
});
} catch (AuthenticationFailedException e) {
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your popUp here
}
});
} catch (SendFailedException e) {
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your popUp here
}
});
} catch (MessagingException e) {
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your popUp here
}
});
} catch (Exception ex) {
Platform.runLater(new Runnable() {
#Override
public void run() {
//Your popUp here
}
});
}
}
});
senderThreadlive.start();
Just use javafx.scene.control.ProgressBar
Documentation:
http://docs.oracle.com/javafx/2/ui_controls/progress.htm
I'm trying to show a context menu only when there is something selected in the list view.
So I called hide in its on showing event. However, this is not working. The context menu still shows up. Is this a bug, or not its intended use? Because JavaFX api seems to suggest hide() is suppose to do this.
Anyway this is the code.
ContextMenu menu = new ContextMenu();
menu.setOnShowing(new EventHandler<WindowEvent>() {
#Override
public void handle(final WindowEvent event) {
menu.hide();
}
});
It will probably work if you do
public void handle(final WindowEvent event) {
Platform.runLater(new Runnable() {
#Override
public void run() {
menu.hide();
}
});
}
but that really seems like a horrible hack.
Why not just set the context menu only if something is selected?
final ListView<T> listView = ... ;
final ContextMenu menu = new ContextMenu();
listView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<T>() {
#Override
public void changed(ObservableValue<? extends T> obs, T oldValue, T newValue) {
if (newValue == null) {
listView.setContextMenu(null);
} else {
listView.setContextMenu(menu);
}
}
});
(obviously replace T with whatever type your ListView is displaying).
private ContextMenu menu; private MenuItem deleteItem;
table.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {
#Override
public void handle(ContextMenuEvent event) {
if (table.getSelectionModel().getSelectedIndex() != -1) {
deleteItem.setVisible(true);
deleteItem.setText("delete: " + table.getSelectionModel().getSelectedItem().getName());
contextMenu.show(table, event.getScreenX(), event.getScreenY());
} else {
deleteItem.setVisible(false);
}
event.consume();
}
});
primaryStage.getScene().addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) { menu.hide(); }});
I have a static BorderPane with ContextMenu insight Task
Task task = new Task()
{
#Override
protected Void call() throws Exception
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
try
{
contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("About");
}
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
MenuItem item3 = new MenuItem("Close");
item3.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
}
});
contextMenu.getItems().addAll(item1, item2, item3);
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
contextMenu.show(bp, event.getScreenX(), event.getScreenY());
event.consume();
}
});
bp.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
contextMenu.hide();
}
});
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
}
}
});
return null;
}
};
new Thread(task).start();
I noticed that when I close the component which holds the BorderPane the Java Threads are not disposed they are still initialized into the memory. I'm not sure is this caused by the static BorderPane. After the Task is completed the Java Thread should be disposed. Any idea why is this happening?
The problem is not a Task, but the anonymous classes in your Runnable.
In the next piece of code:
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event) {
//...
}
});
you introduce an anonymous class extending EventHandler which holds inner link to a Runnable. To solve that you can use nested static class instead.
P.S.: Unfortunately you can't make anonymous class static in Java, see Is it possible to make anonymous inner classes in Java static?
I wan to call ContextMenu into EventHandler. THis code successfully works but I faced one issue.
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
ContextMenu contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("About");
}
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
MenuItem item3 = new MenuItem("Close");
item3.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
}
});
contextMenu.getItems().addAll(item1, item2, item3);
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
contextMenu.show(bp, event.getScreenX(), event.getScreenY());
event.consume();
}
});
bp.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
contextMenu.hide();
}
});
event.consume();
}
});
When I load Tab content with this event handler I have to click twice the first time in order to call the context menu. I suppose that the reason if this is the EventHandler into EventHandler. How I can edit the code so I can call the Context menu with one EventHandler?
Try this
bp.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>()
{
#Override
public void handle(ContextMenuEvent event)
{
ContextMenu contextMenu = new ContextMenu();
MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("About");
}
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
System.out.println("Preferences");
}
});
MenuItem item3 = new MenuItem("Close");
item3.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent e)
{
}
});
contextMenu.getItems().addAll(item1, item2, item3);
contextMenu.show(bp, event.getScreenX(), event.getScreenY());
bp.addEventHandler(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent event)
{
contextMenu.hide();
}
});
event.consume();
}
});
I want to create Pie chart with caption. When I move the mouse over the Pie chart the text is displayed very smoothly.
public void naiveAddData(String name, double value)
{
pieChartData.add(new Data(name, value));
caption = new Label();
caption.setTextFill(Color.DARKORANGE);
caption.setStyle("-fx-font: 24 arial;");
for (final Data data : chart.getData())
{
data.getNode().addEventHandler(MouseEvent.MOUSE_MOVED,
new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
caption.setTranslateX(e.getSceneX() + 15);
caption.setTranslateY(e.getSceneY());
caption.setText(String.valueOf(data.getPieValue()) + "%");
}
});
}
}
But when I move the mouse outside the Pie Chart the caption is still there. It's not removed. How I can fix this?
P.S I managed to solve the problem this way:
data.getNode().addEventHandler(MouseEvent.MOUSE_MOVED,
new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
caption.setTranslateX(e.getSceneX() + 15);
caption.setTranslateY(e.getSceneY());
caption.setText(String.valueOf(data.getPieValue()) + "%");
caption.setVisible(true);
}
});
data.getNode().addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
caption.setVisible(false);
}
});
}
Is there any better solution?
If you have the parent of the caption you can remove it from its parent on mouse exit and add it again on mouse enter.
Parent captionparent = ...
...
data.getNode().addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>()
{
#Override
public void handle(MouseEvent e)
{
captionparent.getChildren().remove(caption);
}
});
Here is a complete example using a Tooltip.
public class ChartingMain extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
PieChart pc = new PieChart();
pc.setTitle("Most popular programming languages");
ObservableList<PieChart.Data> pcdata = FXCollections.<PieChart.Data>observableArrayList();
pcdata.add(new PieChart.Data("C#", 20d));
pcdata.add(new PieChart.Data("Java",800d));
pcdata.add(new PieChart.Data("C/C++", 400d));
final double total = 20d + 800d + 400d;
pc.dataProperty().set(pcdata);
stage.setScene(new Scene(pc));
stage.show();
for(PieChart.Data data : pc.getData()){
final Tooltip t = new Tooltip(data.getName() + " : " + new DecimalFormat("#.##").format((data.getPieValue() / total) * 100) + " %");
data.getNode().setOnMouseEntered(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent me) {
if(me.getSource() instanceof Node){
Node sender = (Node) me.getSource();
Tooltip.install(sender, t);
sender.setEffect(new Glow(0.5));
}
}});
data.getNode().setOnMouseExited(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent me) {
if(me.getSource() instanceof Node){
Node sender = (Node) me.getSource();
sender.setEffect(null);
t.hide();
}
}});
}
}
}