Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
Home
These are Java bindings for libayatana-appindicator and libappindicator-gtk3, created with jextract on Java 25. They make it possible to build appindicator tray icons for Java applications.

The icons used for the tray icon itself can be SVG, that scale and should look ok on all desktop environments.
After adding the Maven artifact to your code, you can simply define an appindicator tray icon like this:
A new method allows to set an entry in the tray menu insensitive:
Gtk.widgetSetSensitive(gtkMenuItem, false);A new public API eases use of the bindings:
packagecom.purejava.fxdemo;
importjavafx.application.Application;
importjavafx.fxml.FXMLLoader;
importjavafx.scene.Scene;
importjavafx.stage.Stage;
importorg.purejava.appindicator.AppIndicator;
importorg.purejava.appindicator.GObject;
importorg.purejava.appindicator.Gtk;
importorg.purejava.appindicator.GCallback;
importjava.io.IOException;
importjava.lang.foreign.Arena;
importstaticorg.purejava.appindicator.app_indicator_h.*;
publicclassHelloApplicationextendsApplication {
privatestaticfinalArenaARENA = Arena.global();
@Overridepublicvoidstart(Stagestage) throwsIOException {
FXMLLoaderfxmlLoader = newFXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scenescene = newScene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
publicstaticvoidmain(String[] args) {
try (vararena = Arena.ofConfined()) {
arenaAuto = Arena.ofAuto();
varindicator = AppIndicator.newIndicator("example-simple-client",
"indicator-message",
APP_INDICATOR_CATEGORY_APPLICATION_STATUS());
vargtkSeparator = Gtk.newMenuItem();
vargtkMenu = Gtk.newMenu();
vargtkSubmenu = Gtk.newMenu();
vargtkMenuItem = Gtk.newMenuItem();
Gtk.menuItemSetLabel(gtkMenuItem, "More");
vargtkSMenuItem = Gtk.newMenuItem();
Gtk.menuItemSetLabel(gtkSMenuItem, "Change icon");
vargtkSMenuItem1 = Gtk.newMenuItem();
Gtk.menuItemSetLabel(gtkSMenuItem1, "Quit");
Gtk.menuShellAppend(gtkSubmenu, gtkSMenuItem);
Gtk.menuShellAppend(gtkSubmenu, gtkSeparator);
Gtk.menuShellAppend(gtkSubmenu, gtkSMenuItem1);
Gtk.menuItemSetSubmenu(gtkMenuItem, gtkSubmenu);
GObject.signalConnectObject(gtkSMenuItem1, "activate", GCallback.allocate(newQuitCallback(), arenaAuto), gtkMenu, 0);
GObject.signalConnectObject(gtkSMenuItem, "activate", GCallback.allocate(newChangeIconCallback(indicator), arenaAuto), gtkMenu, 0);
Gtk.menuShellAppend(gtkMenu, gtkMenuItem);
Gtk.widgetShowAll(gtkMenu);
AppIndicator.setMenu(indicator, gtkMenu);
AppIndicator.setAttentionIcon(indicator, "indicator-messages-new");
AppIndicator.setStatus(indicator, APP_INDICATOR_STATUS_ACTIVE());
launch();
}
}
}packagecom.purejava.fxdemo;
importjavafx.application.Application;
importjavafx.fxml.FXMLLoader;
importjavafx.scene.Scene;
importjavafx.stage.Stage;
importorg.purejava.appindicator.GCallback;
importjava.io.IOException;
importjava.lang.foreign.Arena;
importstaticorg.purejava.appindicator.app_indicator_h.*;
publicclassHelloApplicationextendsApplication {
privatestaticfinalArenaARENA = Arena.global();
@Overridepublicvoidstart(Stagestage) throwsIOException {
FXMLLoaderfxmlLoader = newFXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scenescene = newScene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
publicstaticvoidmain(String[] args) {
try (vararena = Arena.ofConfined()) {
varindicator = app_indicator_new(arena.allocateUtf8String("example-simple-client"),
arena.allocateUtf8String("indicator-message"),
APP_INDICATOR_CATEGORY_APPLICATION_STATUS());
vargtkSeparator = gtk_menu_item_new();
vargtkMenu = gtk_menu_new();
vargtkSubmenu = gtk_menu_new();
vargtkMenuItem = gtk_menu_item_new();
gtk_menu_item_set_label(gtkMenuItem, arena.allocateUtf8String("More"));
vargtkSMenuItem = gtk_menu_item_new();
gtk_menu_item_set_label(gtkSMenuItem, arena.allocateUtf8String("Change icon"));
vargtkSMenuItem1 = gtk_menu_item_new();
gtk_menu_item_set_label(gtkSMenuItem1, arena.allocateUtf8String("Quit"));
gtk_menu_shell_append(gtkSubmenu, gtkSMenuItem);
gtk_menu_shell_append(gtkSubmenu, gtkSeparator);
gtk_menu_shell_append(gtkSubmenu, gtkSMenuItem1);
gtk_menu_item_set_submenu(gtkMenuItem, gtkSubmenu);
g_signal_connect_object(gtkSMenuItem1, arena.allocateUtf8String("activate"), GCallback.allocate(newQuitCallback(), ARENA), gtkMenu, 0);
g_signal_connect_object(gtkSMenuItem, arena.allocateUtf8String("activate"), GCallback.allocate(newChangeIconCallback(indicator), ARENA), gtkMenu, 0);
gtk_menu_shell_append(gtkMenu, gtkMenuItem);
gtk_widget_show_all(gtkMenu);
app_indicator_set_menu(indicator, gtkMenu);
app_indicator_set_attention_icon(indicator, arena.allocateUtf8String("indicator-messages-new"));
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE());
launch();
}
}
}packagecom.purejava.fxdemo;
importjavafx.application.Application;
importjavafx.fxml.FXMLLoader;
importjavafx.scene.Scene;
importjavafx.stage.Stage;
importorg.purejava.appindicator.GCallback;
importjava.io.IOException;
importjava.lang.foreign.Arena;
importjava.lang.foreign.SegmentScope;
importstaticorg.purejava.appindicator.app_indicator_h.*;
publicclassHelloApplicationextendsApplication {
privatestaticfinalSegmentScopeSCOPE = SegmentScope.global();
@Overridepublicvoidstart(Stagestage) throwsIOException {
FXMLLoaderfxmlLoader = newFXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scenescene = newScene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
publicstaticvoidmain(String[] args) {
try (vararena = Arena.openConfined()) {
varindicator = app_indicator_new(arena.allocateUtf8String("example-simple-client"),
arena.allocateUtf8String("indicator-message"),
APP_INDICATOR_CATEGORY_APPLICATION_STATUS());
vargtkSeparator = gtk_menu_item_new();
vargtkMenu = gtk_menu_new();
vargtkSubmenu = gtk_menu_new();
vargtkMenuItem = gtk_menu_item_new();
gtk_menu_item_set_label(gtkMenuItem, arena.allocateUtf8String("More"));
vargtkSMenuItem = gtk_menu_item_new();
gtk_menu_item_set_label(gtkSMenuItem, arena.allocateUtf8String("Change icon"));
vargtkSMenuItem1 = gtk_menu_item_new();
gtk_menu_item_set_label(gtkSMenuItem1, arena.allocateUtf8String("Quit"));
gtk_menu_shell_append(gtkSubmenu, gtkSMenuItem);
gtk_menu_shell_append(gtkSubmenu, gtkSeparator);
gtk_menu_shell_append(gtkSubmenu, gtkSMenuItem1);
gtk_menu_item_set_submenu(gtkMenuItem, gtkSubmenu);
g_signal_connect_object(gtkSMenuItem1, arena.allocateUtf8String("activate"), GCallback.allocate(newQuitCallback(), SCOPE), gtkMenu, 0);
g_signal_connect_object(gtkSMenuItem, arena.allocateUtf8String("activate"), GCallback.allocate(newChangeIconCallback(indicator), SCOPE), gtkMenu, 0);
gtk_menu_shell_append(gtkMenu, gtkMenuItem);
gtk_widget_show_all(gtkMenu);
app_indicator_set_menu(indicator, gtkMenu);
app_indicator_set_attention_icon(indicator, arena.allocateUtf8String("indicator-messages-new"));
app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE());
launch();
}
}
}Don't forget to add the appindicator module to your module-info.java:
modulecom.purejava.fxdemo {
requiresjavafx.controls;
requiresjavafx.fxml;
requiresorg.kordamp.bootstrapfx.core;
requiresorg.slf4j;
requiresorg.purejava.appindicator;
openscom.purejava.fxdemotojavafx.fxml;
exportscom.purejava.fxdemo;
}For wiring a tray icon menu item entry to an action, use the GCallback:
packagecom.purejava.fxdemo;
importorg.purejava.appindicator.GCallback;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
publicclassQuitCallbackimplementsGCallback.Function {
privatestaticfinalLoggerLOG = LoggerFactory.getLogger(QuitCallback.class);
@Overridepublicvoidapply() {
LOG.info("Hit Quit");
System.exit(0);
}
}Some notes on requirements to get appindicator support:
- libayatana-appindicator is a native library, that needs to be installed on the system. It is on most recent Linux desktop environments though. On Ubuntu, the package is named
libayatana-appindicator3-1, on Arch linux it's calledlibayatana-appindicator. - There is another native library, that's used as a fall back, if it is installed on the system: libappindicator. On Ubuntu, the package is named
libappindicator3-1, on Arch linux it's calledlibappindicator-gtk3. - Recent GNOME desktop environments by default do not allow the SystemTray icon to be shown. It's necessary to install the Appindicator Support plugin to show an appindicator icon at all