Files
racket-tray/scrbl/racket-tray.scrbl
T

136 lines
5.1 KiB
Racket

#lang scribble/manual
@(require (for-label racket/base
racket/class
racket/gui/base
racket-tray))
@title{Racket Tray}
@author{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.