Hi,
it would be awesome if TornadoFX would support a tray icon.
Instead of closing, the app would be minimzed.
Hi! It's actually not so hard to do. You have to leverage AWT, since JavaFX doesn't support a Tray Icon yet. Here is a complete application that adds a Tray Icon with show/exit menu options and click-to-show:
class MyApp: App(MainView::class) {
private lateinit var trayIcon: TrayIcon
override fun start(stage: Stage) {
super.start(stage)
// Don't exit on window close (Optional)
Platform.setImplicitExit(false)
// Create tray icon
SwingUtilities.invokeLater { createTrayIcon() }
}
override fun stop() {
SwingUtilities.invokeLater { SystemTray.getSystemTray().remove(trayIcon) }
}
fun createTrayIcon() {
// Initialize AWT Toolkit
Toolkit.getDefaultToolkit()
// Load icon
val icon = ImageIO.read(MyApp::class.java.getResource("/icon.png"))
// Create tray icon, assign icons and actions
trayIcon = TrayIcon(icon).apply {
// Show app on tray icon click
addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
if (e.button == MouseEvent.BUTTON1 && e.clickCount == 1) {
Platform.runLater {
FX.primaryStage.show()
FX.primaryStage.toFront()
}
}
}
})
// Add a menu item to show the app and one to hide
popupMenu = PopupMenu("MyApp").apply {
add(MenuItem("Show...").apply {
addActionListener {
Platform.runLater {
FX.primaryStage.show()
FX.primaryStage.toFront()
}
}
})
add(MenuItem("Exit...").apply {
addActionListener {
Platform.runLater {
Platform.exit()
}
}
})
}
// Add the tray icon to the system tray
SystemTray.getSystemTray().add(this)
}
}
}
We decided to add formal builder support for TrayIcon, so now the above can be written like this:
class MyApp: App(MainView::class) {
override fun start(stage: Stage) {
super.start(stage)
trayicon(resources.stream("/icon.png")) {
setOnMouseClicked(fxThread = true) {
FX.primaryStage.show()
FX.primaryStage.toFront()
}
menu("MyApp") {
item("Show...") {
setOnAction(fxThread = true) {
FX.primaryStage.show()
FX.primaryStage.toFront()
}
}
item("Exit") {
setOnAction(fxThread = true) {
Platform.exit()
}
}
}
}
}
}
Notice that setOnAction and setOnMouseClicked takes an optional fxThread parameter, which when set to true will cause the action to be performed on the JavaFX Application Thread so you don't need to wrap the action in Platform.runLater when you want to execute JavaFX specific code.
You can try it out in 1.6.3-SNAPSHOT if you want, or just use the first example until the 1.6.3 release. The builders has multiple extra options, I will document them in the guide tomorrow.
Let me know if you run into any issues.
@edvin in your first example you explicitly call Platform.setImplicitExit(false) you dont in the second example. Does this mean Platform.setImplicitExit(false) is done by the new functionality?
Yes, the trayicon builder actually has an implicitExcit boolean parameter which defaults to false :)
Tobias, did you get a chance to test this? Are you happy with it?
@edvin
The second example looks fantastic! I will give it a try today or tomorrow and leave feedback here afterwards!
I made some more refinements so you can load the icon like this: trayicon(resources.stream("/icon.png"))
trayicon only seems to work in App related classes not in Workspace related classes?
That's correct. Would it make sense to make it available everywhere?
Closing the issue, as the implementation is complete :)
Most helpful comment
Closing the issue, as the implementation is complete :)