I am Making a Simple App using JavaFX UI, The app simply just do that:
has a systray icon, which when clicked shows a window, when clicked again hides it, on rightclick shows a menu with 1 "exit" item
I already Made the UI and put the App in the Sys Tray, but i can't show/hide it using Normal Actionlistener method, but i got this error:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
here is the Code:
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionListener;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!"); }
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
if (SystemTray.isSupported()) {
SystemTray tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().getImage("Germany-politcal-map.jpg");
PopupMenu popup = new PopupMenu();
MenuItem item = new MenuItem("Exit");
popup.add(item);
TrayIcon trayIcon = new TrayIcon(image, "Amr_Trial", popup);
ActionListener listener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent arg0) {
// TODO Auto-generated method stub
System.exit(0);
}
};
ActionListener listenerTray = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent arg0) {
// TODO Auto-generated method stub
primaryStage.hide();
}
};
trayIcon.addActionListener(listenerTray);
item.addActionListener(listener);
try{
tray.add(trayIcon);
}catch (Exception e) {
System.err.println("Can't add to tray");
}
} else {
System.err.println("Tray unavailable");
}
//
}
}
Wrap the code in the actionListener which calls back to JavaFX in Platform.runLater. This will execute the code which interfaces with the JavaFX system on the JavaFX application thread rather than trying to do it on the Swing event thread (which is what is causing you issues).
For example:
ActionListener listenerTray = new ActionListener() {
#Override public void actionPerformed(java.awt.event.ActionEvent event) {
Platform.runLater(new Runnable() {
#Override public void run() {
primaryStage.hide();
}
});
}
};
By default the application will shutdown when it's last window is hidden. To override this default behaviour, invoke Platform.setImplicitExit(false) before you show the first application Stage. You will then need to explicitly call Platform.exit() when you need the application to really shutdown.
I created a demo for using the AWT system tray within a JavaFX application.
You should only modify the javafx classes on the javafx thread, the listeners on the tray icon are likely to be running on the swing thread. You can do this by posting a runnable to Platform#runLater like so:
Platform.runLater(new Runnable() {
public void run() {
primaryStage.hide();
}
});
The system tray is not supported in JavaFX yet. You could track the progress on this task under the following JIRA issue: https://bugs.openjdk.java.net/browse/JDK-8090475
The issue also provides a workaround, which could be used in JavaFX 8 to get the basic support.
The feature is not planned for JavaFX 8, so it might be released in one of the following updates or even in JavaFX 9.
Shameless self-plug, but I developed a small wrapper library for JavaFX icons that use the SystemTray called FXTrayIcon.
It abstracts away all of the nasty AWT bits and eliminates having to guess which thread you should be running code on. It's available as a dependency on Maven Central.
I resolved your issue. JavaFX with AWT. I have one example of a application that shows and hides when you make left clic. i really hope works for you
import java.awt.AWTException;
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class MainApp2 extends Application {
int stateWindow = 1;
#Override
public void start(final Stage stage) throws Exception {
//Check the SystemTray is supported
if (!SystemTray.isSupported()) {
System.out.println("SystemTray is not supported");
return;
}
URL url = System.class.getResource("/image/yourImage.png");
Image image = Toolkit.getDefaultToolkit().getImage(url);
//image dimensions must be 16x16 on windows, works for me
final TrayIcon trayIcon = new TrayIcon(image, "application name");
final SystemTray tray = SystemTray.getSystemTray();
//Listener left clic XD
trayIcon.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent event) {
if (event.getButton() == MouseEvent.BUTTON1) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (stateWindow == 1) {
stage.hide();
stateWindow = 0;
} else if (stateWindow == 0) {
stage.show();
stateWindow = 1;
}
}
});
}
}
});
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.out.println("TrayIcon could not be added.");
}
stage.setTitle("Hello man!");
Button btn = new Button();
btn.setText("Say 'Hello man'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello man!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
stage.setScene(new Scene(root, 300, 250));
Platform.setImplicitExit(false);
stage.show();
}
/**
* The main() method is ignored in correctly deployed JavaFX application.
* main() serves only as fallback in case the application can not be
* launched through deployment artifacts, e.g., in IDEs with limited FX
* support. NetBeans ignores main().
*
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Related
I am now trying to make focus to FX FileDialog. When i click outside the dialog, dialog is outfocused. Its any way to make when i click outside, the dialog call any metod that make him visible (focused)? TY :)
I just tried any like this.
...focusedProperty().addListener((obs, oldVal, newVal) -> System.out.println(newVal ? "Focused" : "Unfocused"));
and maeby this way...
fileChooser.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent window)
{...
There is no way to handle focus using addEventHandler inside javafx.stage.FileChooser,
But you can simply use primaryStage.setAlwaysOnTop(true); to make your FileChooser always visible on top of other windows
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class JavaFXtest5 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Test");
FileChooser chooser = new FileChooser();
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.setAlwaysOnTop(true);
chooser.showOpenDialog(primaryStage);
primaryStage.setAlwaysOnTop(false);
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello Dialog!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I am currently working on a project that uses a framework that is based on swing. My goal is to add a JavaFX component to this project, but I am having some problems with modal dialogs.
Overall my problem can be simplified as follows -
Swing: I have a JFrame containing a button. When I press this button, I want to open a dialog containing some JavaFX code. This new dialog should block and always be on top of the frame.
JavaFX: Here I also have a button. When I press this button, I want to display an Alert which should block the Dialog (and therefore also the Swing JFrame).
Currently my solution is to use a JDialog containing a JFXPanel as "glue" so overall my logic is as follows
The JFrame contains a button that opens a new JDialog
This JDialog contains a JFXPanel which contains a JavaFX Scene containing a JavaFX Button
Pressing this JavaFX Button opens an Alert
The code is in the bottom of this post.
Currently the JDialog works as expected. It blocks the parent frame and is always on top of it.
The JavaFX Dialog on the other hand, doesn't work quite as expected. It does block the JavaFX Scene (I am not able to press the button multiple times), but it has some problems with the JDialog. These are -
It is not always on top of it. I am able to bring the JDialog on front of my alert
It doesn't block it. I am able to close the JDialog using the X
My own theory is that the problem is the relation between the JDialog and the JFXPanel. But I am not quite sure how to link them.
A quick and dirty example:
package test;
import java.awt.Dialog.ModalityType;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Modality;
import javafx.stage.Window;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
public class TestFXInSwing {
private static void createAndShowGUI() {
JFrame frame = new JFrame("JFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Open JDialog With JFXPanel");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.err.println("Opening JFXPanel");
JDialog dialog = new JDialog(frame, "JDialog");
final JFXPanel fxPanel = new JFXPanel();
dialog.setModal(true);
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
dialog.add(fxPanel);
Platform.setImplicitExit(false);
Platform.runLater(new Runnable() {
#Override
public void run() {
initFX(fxPanel);
}
private void initFX(JFXPanel fxPanel) {
Button btn = new Button("Open JavaFX Alert");
btn.setOnAction(new EventHandler<javafx.event.ActionEvent>() {
#Override
public void handle(javafx.event.ActionEvent t) {
System.err.println("Javafx button pressed");
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Javafx Alert");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("I have a great message for you!");
alert.initModality(Modality.APPLICATION_MODAL);
Field f;
try {
f = JFXPanel.class.getDeclaredField("stage");
f.setAccessible(true);
alert.initOwner((Window) f.get(fxPanel));
} catch (NoSuchFieldException ex) {
Logger.getLogger(TestFXInSwing.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(TestFXInSwing.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalArgumentException ex) {
Logger.getLogger(TestFXInSwing.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(TestFXInSwing.class.getName()).log(Level.SEVERE, null, ex);
}
alert.showAndWait();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 500, 500);
fxPanel.setScene(scene);
}
});
dialog.setVisible(true);
}
});
frame.getContentPane().add(button);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
I'm making an applet using JavaFX 8 (via a JFXPanel) and was planning on using a Popup to display an area with a TextField in it. This works as expected, until the application loses focus. After that, I'm unable to make the TextField regain focus so you can't type in it any more. I've tried calling requestFocus() on the TextField but that didn't seem to do anything. The problem can be seen in the simple example below:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.stage.Popup;
import javax.swing.*;
public class FXApplet extends JApplet {
protected Scene scene;
protected Group root;
#Override
public final void init() {
SwingUtilities.invokeLater(() -> initSwing());
}
private void initSwing() {
JFXPanel fxPanel = new JFXPanel();
add(fxPanel);
Platform.runLater(() -> {
initFX(fxPanel);
initApplet();
});
}
private void initFX(JFXPanel fxPanel) {
root = new Group();
scene = new Scene(root);
fxPanel.setScene(scene);
}
public void initApplet() {
Popup popup = new Popup();
popup.setAutoHide(false);
popup.getContent().add(new TextField());
popup.show(scene.getWindow());
}
}
My application has a JFrame and checks every x seconds if something changed. So I would like to hide my JFrame via setVisible(false) on a click on the close button and redisplay it when the icon in the dock (I'm using Mac OS, but it should work the same way with the Windows task bar) is clicked. You know: many applications do this temporary hiding.
Have you got any ideas how to do this? How to listen on these click events?
Here is a little sample, how to hide/open window in the tray.
import java.awt.Image;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
public class Test {
public static void main(String[] args) throws Exception {
final JFrame frm = new JFrame("Test");
Image im = Toolkit.getDefaultToolkit().getImage("c:\\icons\\icon1.png");
final TrayIcon tri = new TrayIcon(im);
tri.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frm.setVisible(true);
try {
SystemTray.getSystemTray().remove(tri);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
frm.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
try {
SystemTray.getSystemTray().add(tri);
} catch (Exception ex) {
ex.printStackTrace();
}
frm.setVisible(false);
}
});
frm.setSize(100, 100);
frm.setVisible(true);
}
}
Use the com.apple.eawt or java.awt.Desktop packages to listen to Events that occur when the application is closed, hidden or reactivated.
Particularly com.apple.eawt.AppReOpenedEvent is cast when the Dock Icon is clicked. When you handle the event with com.apple.eawt.AppReOpenedListener, set the frame visible again:
#Override
public void appReOpened(AppReOpenedEvent arg0) {
invalidate(); // Suppose these are optional, but make sure the layout is up to date
pack();
validate();
setVisible(true);
}
I am experiencing a problem with Swing that only occurs when the computer monitor is powered off, but my Swing application continues to run in the background. It seems that whenever the monitor is off, Swing/AWT cancels all painting operations, leading to a number of display issues in the GUI that are visible as soon as the monitor turns back on.
For example, when I turn off the monitor using a custom JNI function and subsequently open a simple message dialog, the message dialog is blank when the monitor turns back on:
But it paints correctly after the next repaint:
Is this the expected behavior of Swing? Is there a way to instruct Swing to continue drawing to the screen even if the monitor is powered off?
EDIT: Here is an SSCCE:
package test;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class App {
public static void main(String[] args) throws Throwable {
System.out.println("***** Please turn off the monitor in the next 70 seconds *****");
Thread.sleep(1000L * 70);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JOptionPane.showMessageDialog(null, "Test");
}
});
}
}
I am using 64-bit Windows 7 Home Premium SP1 and 64-bit Java 1.6.0_24.
EDIT 2: Here is another program with which I experience the effect of "canceled painting operations":
package test;
import static com.mycompany.Util.turnOffMonitors;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class DialogTest extends JDialog {
private final JLabel label;
public DialogTest() {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
label = new JLabel("Test", JLabel.CENTER);
label.setOpaque(true);
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(BorderLayout.CENTER, label);
this.setPreferredSize(new Dimension(200, 110));
pack();
setLocationRelativeTo(null);
setVisible(true);
Thread t = new Thread() {
#Override
public void run() {
turnOffMonitors();
try {
Thread.sleep(3000L);
} catch (InterruptedException ex) { }
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
label.setBackground(Color.YELLOW);
}
});
}
};
t.start();
}
public static void main(String[] args) throws Throwable {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DialogTest();
}
});
}
}
Before the monitor shuts off, I see:
With the monitor off, the label background color is changed to yellow in the background. I then move the mouse to turn the monitor back on. The dialog is visually unchanged. It is only after I force a repaint (by ALT-TABbing, for example) do I see the yellow:
EDIT 3: Reported to Oracle as Bug ID 7049597.
I then started the program and stopped
moving the mouse/typing. After one
minute, the screen turned off. I
waited another 20 seconds to move the
mouse. The monitor turned back on and
I saw a blank message dialog.
Using your example, I don't see this on my (non-Windows) platform. You might try the example below, which should alternate between WINDOW_ACTIVATED on wake and WINDOW_DEACTIVATED on sleep. If so, you could extend JDialog and repaint() in windowActivated().
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
/** #see http://stackoverflow.com/questions/6163606 */
public class DialogEventTest extends JDialog {
public DialogEventTest() {
this.setLayout(new GridLayout(0, 1));
this.add(new JLabel("Dialog event test.", JLabel.CENTER));
this.add(new JButton(new AbstractAction("Close") {
#Override
public void actionPerformed(ActionEvent e) {
DialogEventTest.this.setVisible(false);
DialogEventTest.this.dispatchEvent(new WindowEvent(
DialogEventTest.this, WindowEvent.WINDOW_CLOSING));
}
}));
}
private static class WindowHandler extends WindowAdapter {
#Override
public void windowActivated(WindowEvent e) {
System.out.println(e);
}
#Override
public void windowDeactivated(WindowEvent e) {
System.out.println(e);
}
}
private void display() {
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.addWindowListener(new WindowHandler());
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new DialogEventTest().display();
}
});
}
}
The problem probably has more to do with how it repaints when the screen comes on rather than what happens while it's off. You could check by running a screen recorder.