racket-tray
A small cross-platform system tray API for Racket GUI applications.
Version 0.1.1 contains native backends for Windows, Linux and macOS:
- Windows uses
Shell_NotifyIconWandSetWindowSubclass. - Linux uses Ayatana AppIndicator and GTK3.
- macOS uses AppKit
NSStatusItemandNSMenuthrough Racket's Objective-C FFI.
The public API and symbolic menu actions are the same on every platform.
#lang racket/gui
(require racket-tray)
(define tray-frame%
(class frame%
(super-new [label "Tray example"]
[width 400]
[height 250])
;; Close [X] to the tray instead of destroying the frame.
(define/override (on-close)
(send this show #f))))
(define frame (new tray-frame%))
(define (show-frame)
(send frame show #t)
(when (send frame is-iconized?)
(send frame iconize #f)))
(define (tray-action action)
(case action
[(open)
(show-frame)]
[(exit)
(tray-close tray)
(exit)]))
(define tray
(mk-tray frame
"example.png"
(list tray-action 'open)
#:hide-on-minimize? #t))
(tray-set-menu!
tray
(list
(list 'open "Open")
'separator
(list 'exit "Exit")))
(send frame show #t)
Action and menu model
The third argument of mk-tray is mandatory and has the form:
(list callback default-action-id)
callback accepts one symbol. default-action-id is a symbol that must also
occur in the menu installed by tray-set-menu!.
A menu contains (list action-id label) entries and separators:
(list
(list 'open "Open")
'separator
(list 'exit "Exit"))
Choosing a menu item calls the callback with its action identifier. In the
example above, choosing Open calls (tray-action 'open) and choosing Exit
calls (tray-action 'exit).
Direct tray activation is platform dependent:
- Windows: a normal activation invokes the configured default action. The context-menu gesture opens the tray menu.
- Linux with Ayatana AppIndicator 0.6 or newer: a primary activation can invoke the configured default action. The context-menu gesture opens the menu.
- Linux with Ayatana AppIndicator 0.5.x: primary activation opens the menu; the older library has no primary-activation callback.
- macOS: the native status item opens its menu. Menu selections invoke the
symbolic callback. The default id remains part of the common API but is not
invoked directly by an
NSStatusItemwith an attached menu.
Hide on minimize
#:hide-on-minimize? is implemented in the platform-independent Racket layer.
It periodically checks frame%'s public is-iconized? method and hides the
frame when it becomes iconized. No Win32, GTK or AppKit minimize hook is used.
Closing a frame to the tray is separate because Racket already has the public
on-close callback. Override it and call (send this show #f) as shown above.
Icons
example.png is suitable on all three supported platforms. Windows also
accepts .ico; PNG alpha transparency is converted to a native HICON by the
Windows backend. Linux passes an absolute image path to AppIndicator. macOS
loads the image with NSImage.
Linux runtime dependency
Linux requires the Ayatana AppIndicator GTK3 runtime library. racket-tray
loads the native shared library dynamically. If it is missing, mk-tray
reports the dependency and the appropriate package commands instead of
exposing a raw FFI loader error.
Debian/Ubuntu:
sudo apt install libayatana-appindicator3-1
Fedora:
sudo dnf install libayatana-appindicator-gtk3
Arch Linux:
sudo pacman -S libayatana-appindicator
No additional native dependency is required by the Windows or macOS backend.