# 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_NotifyIconW` and `SetWindowSubclass`. - Linux uses Ayatana AppIndicator and GTK3. - macOS uses AppKit `NSStatusItem` and `NSMenu` through Racket's Objective-C FFI. The public API and symbolic menu actions are the same on every platform. ```racket #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: ```racket (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: ```racket (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 `NSStatusItem` with 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: ```text sudo apt install libayatana-appindicator3-1 ``` Fedora: ```text sudo dnf install libayatana-appindicator-gtk3 ``` Arch Linux: ```text sudo pacman -S libayatana-appindicator ``` No additional native dependency is required by the Windows or macOS backend.