linux & mac support, refactoring on minimize
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
# racket-tray
|
||||
|
||||
A small system tray API for Racket.
|
||||
A small cross-platform system tray API for Racket GUI applications.
|
||||
|
||||
Version 0.1.1 implements Windows directly through the Win32 API. It uses the
|
||||
native `HWND` of an existing Racket `frame%` or `dialog%`, `Shell_NotifyIconW`,
|
||||
and `SetWindowSubclass`. No additional native DLL is required.
|
||||
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
|
||||
@@ -28,35 +32,106 @@ and `SetWindowSubclass`. No additional native DLL is required.
|
||||
(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"
|
||||
show-frame
|
||||
(list tray-action 'open)
|
||||
#:hide-on-minimize? #t))
|
||||
|
||||
(tray-set-menu!
|
||||
tray
|
||||
(list
|
||||
(list "Open" show-frame)
|
||||
(list 'open "Open")
|
||||
'separator
|
||||
(list "Exit"
|
||||
(λ ()
|
||||
(tray-close tray)
|
||||
(exit)))))
|
||||
(list 'exit "Exit")))
|
||||
|
||||
(send frame show #t)
|
||||
```
|
||||
|
||||
`mk-tray` and `tray-set-icon!` accept both Windows `.ico` files and `.png`
|
||||
files. PNG transparency is preserved when the image is converted to a native
|
||||
Windows tray icon.
|
||||
## Action and menu model
|
||||
|
||||
With `#:hide-on-minimize? #t`, minimizing the associated window hides it from
|
||||
the taskbar while keeping its native window handle alive. Closing a window to
|
||||
the tray does not need a separate tray API: override `frame%`'s `on-close` and
|
||||
call `(send this show #f)`.
|
||||
The third argument of `mk-tray` is mandatory and has the form:
|
||||
|
||||
`tray-set-menu!` accepts a `popup-menu%` object directly, a simple menu list,
|
||||
or `#f` to remove the context menu.
|
||||
```racket
|
||||
(list callback default-action-id)
|
||||
```
|
||||
|
||||
At the moment non-Windows platforms report that the operation is unsupported.
|
||||
`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.
|
||||
|
||||
Reference in New Issue
Block a user