Java – Status menu using JavaFX on MacOS

Status menu using JavaFX on MacOS… here is a solution to the problem.

Status menu using JavaFX on MacOS

Is there a way to create a status menu with JavaFX? The documentation for JavaFX doesn’t seem to have anything like that.
enter image description here

The menu on the left is very simple :

MenuBar menuBar = new MenuBar();
menuBar.useSystemMenuBarProperty().set(true);

Menu menu = new Menu("java");
MenuItem item = new MenuItem("Test");
menu.getItems().add(item);
menuBar.getMenus().add(menu);

BorderPane borderPane = new BorderPane();
borderPane.setTop(menuBar);
primaryStage.setScene(new Scene(borderPane));
primaryStage.show();

Solution

Therefore, you can use java.awt.SystemTray to display the menu:

public static void showMenu(Image trayImage, String... items) {

if (!java.awt.SystemTray.isSupported())
        throw new UnsupportedOperationException("No system tray support, application exiting.");

java.awt.Toolkit.getDefaultToolkit();

java.awt.SystemTray tray = java.awt.SystemTray.getSystemTray();
    java.awt.TrayIcon trayIcon = new java.awt.TrayIcon(trayImage);
    java.awt.PopupMenu rootMenu = new java.awt.PopupMenu();

for (String item : items) rootMenu.add(new MenuItem(item));

trayIcon.setPopupMenu(rootMenu);

try {
        tray.add(trayIcon);
    } catch (Throwable e) {
        throw new RuntimeException("Unable to init system tray");
    }
}

SystemTray only supports images as root items, but can convert text to images:

static BufferedImage textToImage(String text) {
    return textToImage(text, java.awt.Font.decode(null), 13);
}

static BufferedImage textToImage(String Text, Font font, float size) {
    font = font.deriveFont(size);

FontRenderContext frc = new FontRenderContext(null, true, true);

LineMetrics lm = font.getLineMetrics(Text, frc);
    Rectangle2D r2d = font.getStringBounds(Text, frc);
    BufferedImage img = new BufferedImage((int) Math.ceil(r2d.getWidth()), (int) Math.ceil(r2d.getHeight()), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = img.createGraphics();
    g2d.setRenderingHints(RenderingProperties);
    g2d.setBackground(new Color(0, 0, 0, 0));
    g2d.setColor(Color.BLACK);

g2d.clearRect(0, 0, img.getWidth(), img.getHeight());
    g2d.setFont(font);
    g2d.drawString(Text, 0, lm.getAscent());
    g2d.dispose();

return img;
}

Final usage example:

public static void main(String[] args) {
    System.setProperty("apple.awt.UIElement", "true");
    showMenu(textToImage("Hello"), "Item - 1", "Item - 2");
}

The system property apple.awt.UIElement=true is useful when you need to get rid of the default java menu and cmd-tab icon, so your application behaves like a background.

Related Problems and Solutions