Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Last active November 13, 2023 14:54
Show Gist options
  • Star 97 You must be signed in to star a gist
  • Fork 25 You must be signed in to fork a gist
  • Save jewelsea/e231e89e8d36ef4e5d8a to your computer and use it in GitHub Desktop.
Save jewelsea/e231e89e8d36ef4e5d8a to your computer and use it in GitHub Desktop.
Demonstrate using the System Tray (AWT) to control a JavaFX application.
import javafx.application.*;
import javafx.geometry.Pos;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.net.URL;
import java.text.*;
import java.util.*;
// Java 8 code
public class JavaFXTrayIconSample extends Application {
// one icon location is shared between the application tray icon and task bar icon.
// you could also use multiple icons to allow for clean display of tray icons on hi-dpi devices.
private static final String iconImageLoc =
"http://icons.iconarchive.com/icons/scafer31000/bubble-circle-3/16/GameCenter-icon.png";
// application stage is stored so that it can be shown and hidden based on system tray icon operations.
private Stage stage;
// a timer allowing the tray icon to provide a periodic notification event.
private Timer notificationTimer = new Timer();
// format used to display the current time in a tray icon notification.
private DateFormat timeFormat = SimpleDateFormat.getTimeInstance();
// sets up the javafx application.
// a tray icon is setup for the icon, but the main stage remains invisible until the user
// interacts with the tray icon.
@Override public void start(final Stage stage) {
// stores a reference to the stage.
this.stage = stage;
// instructs the javafx system not to exit implicitly when the last application window is shut.
Platform.setImplicitExit(false);
// sets up the tray icon (using awt code run on the swing thread).
javax.swing.SwingUtilities.invokeLater(this::addAppToTray);
// out stage will be translucent, so give it a transparent style.
stage.initStyle(StageStyle.TRANSPARENT);
// create the layout for the javafx stage.
StackPane layout = new StackPane(createContent());
layout.setStyle(
"-fx-background-color: rgba(255, 255, 255, 0.5);"
);
layout.setPrefSize(300, 200);
// this dummy app just hides itself when the app screen is clicked.
// a real app might have some interactive UI and a separate icon which hides the app window.
layout.setOnMouseClicked(event -> stage.hide());
// a scene with a transparent fill is necessary to implement the translucent app window.
Scene scene = new Scene(layout);
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
}
/**
* For this dummy app, the (JavaFX scenegraph) content, just says "hello, world".
* A real app, might load an FXML or something like that.
*
* @return the main window application content.
*/
private Node createContent() {
Label hello = new Label("hello, world");
hello.setStyle("-fx-font-size: 40px; -fx-text-fill: forestgreen;");
Label instructions = new Label("(click to hide)");
instructions.setStyle("-fx-font-size: 12px; -fx-text-fill: orange;");
VBox content = new VBox(10, hello, instructions);
content.setAlignment(Pos.CENTER);
return content;
}
/**
* Sets up a system tray icon for the application.
*/
private void addAppToTray() {
try {
// ensure awt toolkit is initialized.
java.awt.Toolkit.getDefaultToolkit();
// app requires system tray support, just exit if there is no support.
if (!java.awt.SystemTray.isSupported()) {
System.out.println("No system tray support, application exiting.");
Platform.exit();
}
// set up a system tray icon.
java.awt.SystemTray tray = java.awt.SystemTray.getSystemTray();
URL imageLoc = new URL(
iconImageLoc
);
java.awt.Image image = ImageIO.read(imageLoc);
java.awt.TrayIcon trayIcon = new java.awt.TrayIcon(image);
// if the user double-clicks on the tray icon, show the main app stage.
trayIcon.addActionListener(event -> Platform.runLater(this::showStage));
// if the user selects the default menu item (which includes the app name),
// show the main app stage.
java.awt.MenuItem openItem = new java.awt.MenuItem("hello, world");
openItem.addActionListener(event -> Platform.runLater(this::showStage));
// the convention for tray icons seems to be to set the default icon for opening
// the application stage in a bold font.
java.awt.Font defaultFont = java.awt.Font.decode(null);
java.awt.Font boldFont = defaultFont.deriveFont(java.awt.Font.BOLD);
openItem.setFont(boldFont);
// to really exit the application, the user must go to the system tray icon
// and select the exit option, this will shutdown JavaFX and remove the
// tray icon (removing the tray icon will also shut down AWT).
java.awt.MenuItem exitItem = new java.awt.MenuItem("Exit");
exitItem.addActionListener(event -> {
notificationTimer.cancel();
Platform.exit();
tray.remove(trayIcon);
});
// setup the popup menu for the application.
final java.awt.PopupMenu popup = new java.awt.PopupMenu();
popup.add(openItem);
popup.addSeparator();
popup.add(exitItem);
trayIcon.setPopupMenu(popup);
// create a timer which periodically displays a notification message.
notificationTimer.schedule(
new TimerTask() {
@Override
public void run() {
javax.swing.SwingUtilities.invokeLater(() ->
trayIcon.displayMessage(
"hello",
"The time is now " + timeFormat.format(new Date()),
java.awt.TrayIcon.MessageType.INFO
)
);
}
},
5_000,
60_000
);
// add the application tray icon to the system tray.
tray.add(trayIcon);
} catch (java.awt.AWTException | IOException e) {
System.out.println("Unable to init system tray");
e.printStackTrace();
}
}
/**
* Shows the application stage and ensures that it is brought ot the front of all stages.
*/
private void showStage() {
if (stage != null) {
stage.show();
stage.toFront();
}
}
public static void main(String[] args) throws IOException, java.awt.AWTException {
// Just launches the JavaFX application.
// Due to way the application is coded, the application will remain running
// until the user selects the Exit menu option from the tray icon.
launch(args);
}
}
@ericek111
Copy link

Isn't 5 years enough to implement it?

@oxodao
Copy link

oxodao commented Jan 9, 2018

Isn't 5 years enough to implement it?

I guess not

@kinsleykajiva
Copy link

any news on this issue yet?

Copy link

ghost commented Mar 28, 2018

Still no change... I'm sad.

@RafatRifaie
Copy link

The javafx team is lazy af

@jacob-faber
Copy link

I guess they abandoned javafx, that's why they open sourced it.

@1fexd
Copy link

1fexd commented Mar 14, 2020

Will this ever get implemented?

@tsathis
Copy link

tsathis commented Jun 19, 2020

Any good news?

@alilosoft
Copy link

6 years later???

@dustinkredmond
Copy link

I feel like someone should make a library for easily working with TrayIcons from JavaFX as the AWT API is not friendly to me as a JavaFX user. Guess that's another side project for me to do...

@dustinkredmond
Copy link

I feel like someone should make a library for easily working with TrayIcons from JavaFX as the AWT API is not friendly to me as a JavaFX user. Guess that's another side project for me to do...

I succeeded in making this marginally easier. Have a look at my repo "FXTrayIcon", particularly at the test application that I wrote. Still uses AWT, but abstracts away some of the nastiness. FXTrayIcon

@carterpeel
Copy link

I feel like someone should make a library for easily working with TrayIcons from JavaFX as the AWT API is not friendly to me as a JavaFX user. Guess that's another side project for me to do...

I succeeded in making this marginally easier. Have a look at my repo "FXTrayIcon", particularly at the test application that I wrote. Still uses AWT, but abstracts away some of the nastiness. FXTrayIcon

When can I come kiss you? @dustinkredmond

@dustinkredmond
Copy link

I feel like someone should make a library for easily working with TrayIcons from JavaFX as the AWT API is not friendly to me as a JavaFX user. Guess that's another side project for me to do...

I succeeded in making this marginally easier. Have a look at my repo "FXTrayIcon", particularly at the test application that I wrote. Still uses AWT, but abstracts away some of the nastiness. FXTrayIcon

When can I come kiss you? @dustinkredmond

Star the repo and that's even better than a kiss in my opinion. :D

@carterpeel
Copy link

I thought I already did!!! Thanks for reminding me to do it again. It didn't work the first time for whatever reason.

@thierrylafaye
Copy link

I feel like someone should make a library for easily working with TrayIcons from JavaFX as the AWT API is not friendly to me as a JavaFX user. Guess that's another side project for me to do...

I succeeded in making this marginally easier. Have a look at my repo "FXTrayIcon", particularly at the test application that I wrote. Still uses AWT, but abstracts away some of the nastiness. FXTrayIcon

When can I come kiss you? @dustinkredmond

Star the repo and that's even better than a kiss in my opinion. :D

And so I just "kissed" you! 🤣 Thanks so much for putting some effort into this workaround. If I had the knowledge, I would have contributed to this component which seems quite needed for a decent JavaFx control set.

@AmirAli-AZ
Copy link

No news yet??
Thanks also to @ dustinkredmond, I hope his project helps me

@tenglongwentian
Copy link

托盘图标显示后,点击桌面空白位置,托盘图标不关闭,如何处理?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment