#lang scribble/manual @(require (for-label racket/base racket/class racket/gui/base racket-tray)) @title{Racket Tray} @author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]] @defmodule[racket-tray] Racket Tray provides a small cross-platform system tray API for Racket GUI applications. Version 0.1.1 contains native backends for Windows, Linux and macOS. The public API uses symbolic actions so application code does not depend on a platform-specific tray menu implementation. @section{Creating a Tray Icon} @defproc[(mk-tray [frame (is-a?/c top-level-window<%>)] [icon-file path-string?] [action-spec list?] [#:hide-on-minimize? hide-on-minimize? boolean? #f]) any/c]{ Creates a tray icon associated with @racket[frame]. A @racket[frame%] or @racket[dialog%] is suitable. @racket[action-spec] is mandatory and must contain exactly two values: @racket[(list callback default-action-id)]. @racket[callback] must accept one argument and @racket[default-action-id] must be a symbol. Every action generated by the tray is passed to @racket[callback] as a symbol. The default action must occur in the menu later installed with @racket[tray-set-menu!]. Windows maps normal tray activation to this action. Ayatana AppIndicator 0.6 or newer can do the same on Linux. Older AppIndicator 0.5.x implementations open the menu on primary activation instead. A native macOS status item with an attached menu also opens its menu instead of invoking the default action directly. When @racket[hide-on-minimize?] is true, Racket Tray periodically checks @method[frame% is-iconized?]. When the frame changes to the iconized state it is hidden with @racket[(send frame show #f)]. This implementation is entirely platform independent and does not use a Win32, GTK or AppKit minimize hook. The returned value represents the tray icon and is accepted by the other procedures in this library. } @section{Tray Actions and Menus} @defproc[(tray-set-menu! [tray any/c] [menu-spec list?]) void?]{ Sets the menu for @racket[tray]. An actionable entry has the form @racket[(list action-id label)], where @racket[action-id] is a symbol and @racket[label] is a string. @racket['separator] or @racket[#f] creates a separator. Action identifiers must be unique and the menu must contain the default action supplied to @racket[mk-tray]. For example: @racketblock[ (tray-set-menu! tray (list (list 'open "Open") 'separator (list 'exit "Exit"))) ] Choosing @racket["Open"] invokes the callback as @racket[(callback 'open)]. The same callback receives every other menu action. } @section{Changing and Closing a Tray Icon} @defproc[(tray-set-icon! [tray any/c] [icon-file path-string?]) void?]{ Replaces the image of @racket[tray]. PNG files can be used on all supported platforms. The Windows backend also accepts ICO files. } @defproc[(tray-close [tray any/c]) void?]{ Removes @racket[tray], stops its optional portable minimize watcher and releases the native resources owned by the active platform backend. Calling this procedure does not close the associated Racket window. } @section{Closing a Window to the Tray} Racket already provides @method[frame% on-close] for handling the close button of a frame. A tray application normally overrides it and hides the frame: @racketblock[ (define tray-frame% (class frame% (super-new) (define/override (on-close) (send this show #f)))) ] This is intentionally separate from @racket[#:hide-on-minimize?]. Closing a window already has a portable public Racket callback; minimizing does not. @section{Platform Behaviour} On Windows, the backend uses the native HWND returned by Racket GUI, @tt{Shell_NotifyIconW} and @tt{SetWindowSubclass}. A normal tray activation invokes the configured default action. No additional native library is needed. On Linux, the backend uses Ayatana AppIndicator and GTK3. AppIndicator 0.6 or newer provides primary activation, which Racket Tray maps to the default action. With AppIndicator 0.5.x, primary activation opens the menu instead. Menu choices have the same symbolic callback behaviour on both library versions. On macOS, the backend uses AppKit @tt{NSStatusItem}, @tt{NSStatusBarButton} and @tt{NSMenu} through Racket's Objective-C FFI. A status item with an attached menu opens that menu when activated. Menu choices invoke the common symbolic callback. No additional native library is needed. @section{Linux Runtime Dependency} The Linux backend requires the Ayatana AppIndicator GTK3 runtime library. If it cannot be loaded, @racket[mk-tray] reports the missing dependency and suggests the native package to install. Debian and Ubuntu use @tt{libayatana-appindicator3-1}. Fedora uses @tt{libayatana-appindicator-gtk3}. Arch Linux uses @tt{libayatana-appindicator}. @section{Example} The package contains @filepath{examples/simple.rkt}. It demonstrates symbolic tray actions, hiding a frame on minimize and close, restoring it from the tray, and terminating the application only through the @racket['exit] action.