proton_tray

Cross-platform native tray helpers for MoonBit.

tray
desktop
native
windows
macos
linux
moon add moonbit-community/proton_tray@0.1.16
Download zip
Version
0.1.16
License
Apache-2.0
Last updated
3 days ago
Downloads
6
README

#moonbit-community/proton_tray

Cross-platform native tray helpers for MoonBit.

Native tray events are queued with a fixed bound, so long-running apps should call pump() and drain_events() regularly.

#Example

guard @proton_tray.is_supported() else {
return
}

let tray = @proton_tray.create(
identifier="com.example.demo",
tooltip="MoonBit tray demo",
)

match tray {
Ok(tray) => {
match
tray.set_menu([
@proton_tray.TrayMenuItem::normal(id="show", label="Show"),
@proton_tray.TrayMenuItem::separator(),
@proton_tray.TrayMenuItem::checkbox(id="launch", label="Launch", checked=true),
@proton_tray.TrayMenuItem::submenu(
label="More",
items=[
@proton_tray.TrayMenuItem::normal(id="settings", label="Settings"),
],
),
]) {
Ok(_) => ()
Err(error) => println("set_menu skipped: \{error}")
}
match tray.show() {
Ok(_) =>
match tray.pump() {
Ok(_) =>
for event in tray.drain_events() {
println(event.event_name())
}
Err(error) => println(error)
}
Err(error) => println(error)
}
tray.destroy()
}
Err(error) => println(error)
}

#
Platform

pub enum Platform {
Windows
Linux
Macos
Unknown
} derive(Eq, ToJson,
Debug
)

Desktop platform reported by the native tray backend.

Windows, Linux, and Macos identify a backend this package knows how to drive. Unknown is returned when the native stub cannot map the host operating system to a supported variant, which typically also means tray creation will fail. Use this to branch platform-specific setup or to enrich diagnostics; the value is derived from current_platform().
impl Show for Platform

#
Tray

pub struct Tray {
// private fields
}

Represents a system tray handle created by this package.

A Tray tracks the user-visible state that the MoonBit layer believes is active, including whether the tray is currently visible, which tooltip is being shown, and which icon path was last requested. The underlying native resources are released by calling destroy().

#
Tray::destroy

fn Tray::destroy(self : Tray) -> Unit

Releases the underlying native resources and turns the handle into a no-op object that rejects later operations.

Calling destroy() more than once is safe; repeated calls are ignored after the first teardown has marked the handle as destroyed.

After destruction the tray becomes permanently unusable: show(), hide(), set_tooltip(), set_icon(), set_menu(), and pump() will all return errors instead of touching the native backend again.

Native tray operations, including destroy(), must run on the thread that called create(); macOS already required this, and it is now enforced on all platforms.

#
Tray::drain_events

fn Tray::drain_events(self : Tray) -> Array[TrayEvent]

Drains all currently queued tray events.

For native trays, this polls the backend event queue until it is empty. For simulated trays used in tests, this returns and clears the MoonBit-side event queue. Destroyed trays return an empty array.

#
Tray::hide

fn Tray::hide(self : Tray) -> Result[Bool, String]

Hides the tray icon while keeping the handle valid for later show() calls.

The returned boolean reflects the post-call visibility state, so successful calls resolve to Ok(false) whether the tray is native or simulated.

This is safe to call even when the tray is already hidden; the handle stays valid and can be shown again later. Destroyed trays still reject the call with an error.

#
Tray::icon

fn Tray::icon(self : Tray) -> String?

Returns the last icon path requested through create() or set_icon().

#
Tray::identifier

fn Tray::identifier(self : Tray) -> String

Returns the stable identifier associated with this tray handle.

#
Tray::is_visible

fn Tray::is_visible(self : Tray) -> Bool

Returns whether this tray handle is currently believed to be visible.

#
Tray::menu_items

fn Tray::menu_items(self : Tray) -> Array[TrayMenuItem]

Returns a clone of the last menu payload accepted by set_menu().

#
Tray::platform

fn Tray::platform(self : Tray) -> Platform

Returns the platform reported when this tray handle was created.

#
Tray::pump

fn Tray::pump(self : Tray, blocking? : Bool) -> Result[Bool, String]

Pumps one native tray loop iteration.

Call this from long-running native applications when the host platform needs event-loop progress from the tray backend. A return value of Ok(false) means the backend asked to stop processing. Simulated trays always return Ok(true) so unit tests can exercise state transitions without a native message loop.

Passing blocking=true lets the backend wait for work before returning; blocking=false performs at most one non-blocking iteration. Backend errors and calls on destroyed trays return Err(message).

#
Tray::set_icon

fn Tray::set_icon(self : Tray, icon : String?) -> Result[Bool, String]

Changes the tray icon path or resets it to the platform default when None is passed.

This updates the stored icon preference even for simulated trays used in tests, and the returned boolean mirrors whether the tray is visible after the change.

Pass Some(path) to request a specific icon file, or None to fall back to the backend's default tray icon. Successful calls return the current visible state; destroyed trays and backend failures return Err(message).

#
Tray::set_menu

fn Tray::set_menu(self : Tray, items : Array[TrayMenuItem]) -> Result[Bool, String]

Replaces the context menu shown for this tray icon.

The menu supports normal, separator, checkbox, and nested submenu items. Normal and checkbox items must have non-empty id and label values; item ids must be unique across the whole menu tree so click events can be routed reliably.

Successful calls return the current visible state. Unsupported operating systems, missing native menu backends, or invalid menu payloads return Err(message).

#
Tray::set_tooltip

fn Tray::set_tooltip(self : Tray, tooltip : String) -> Result[Bool, String]

Replaces the current tooltip text without changing visibility.

Platforms that cannot show a real tooltip may map this value to the nearest native concept available to the host desktop environment. The returned boolean mirrors whether the tray is visible after the update.

Hidden trays keep the new tooltip so the next show() call reuses it by default. Successful calls return the current visible state, while destroyed trays or backend failures return Err(message).

#
Tray::show

fn Tray::show(self : Tray, tooltip? : String?) -> Result[Bool, String]

Shows the tray icon and optionally replaces the tooltip in the same call.

Passing tooltip=Some(...) is the most efficient way to update the tooltip immediately before the tray becomes visible. The result reports the tray's visible state after the call, and invoking this on a simulated test tray updates only the MoonBit-side state.

Passing tooltip=None reuses the most recently stored tooltip. Successful calls always resolve to Ok(true). If the tray has already been destroyed, or if the native backend rejects the operation, this returns Err(message).

#
Tray::tooltip

fn Tray::tooltip(self : Tray) -> String

Returns the last tooltip requested through create(), show(), or set_tooltip().

#
Tray::visible

fn Tray::visible(self : Tray) -> Bool

Alias for is_visible().

#
TrayEvent

pub(all) enum TrayEvent {
Click
RightClick
DoubleClick
MenuItemClick(String)
} derive(Eq,
Debug
)

Event emitted by the native tray backend.

#
TrayEvent::event_name

fn TrayEvent::event_name(self : TrayEvent) -> String

Returns the wire-format event name for a tray event.

#
TrayEvent::item_id

fn TrayEvent::item_id(self : TrayEvent) -> String?

Returns the clicked menu item id for MenuItemClick events.

#
TrayMenuItem

pub(all) enum TrayMenuItem {
Normal(id~ : String, label~ : String, enabled~ : Bool)
Separator
Checkbox(id~ : String, label~ : String, checked~ : Bool, enabled~ : Bool)
Submenu(label~ : String, items~ : Array[TrayMenuItem], enabled~ : Bool)
} derive(Eq,
Debug
)

One tray context-menu item.

Use TrayMenuItem::normal, TrayMenuItem::separator, and TrayMenuItem::checkbox to build clickable items. Use TrayMenuItem::submenu to group nested items. Clickable item ids must be unique across the whole menu tree.

#
TrayMenuItem::checkbox

fn TrayMenuItem::checkbox(id~ : String, label~ : String, checked? : Bool, enabled? : Bool) -> TrayMenuItem

Builds a checkbox menu item.

#
TrayMenuItem::kind

Returns the item kind.

#
TrayMenuItem::normal

fn TrayMenuItem::normal(id~ : String, label~ : String, enabled? : Bool) -> TrayMenuItem

Builds a normal clickable menu item.

#
TrayMenuItem::separator

fn TrayMenuItem::separator() -> TrayMenuItem

Builds a separator menu item.

#
TrayMenuItem::submenu

fn TrayMenuItem::submenu(label~ : String, items~ : Array[TrayMenuItem], enabled? : Bool) -> TrayMenuItem

Builds a submenu containing nested menu items.

#
TrayMenuItemKind

pub(all) enum TrayMenuItemKind {
Normal
Separator
Checkbox
Submenu
} derive(Eq,
Debug
)

Kind of context-menu item supported by tray v1.

#
create

fn create(identifier? : String, icon? : String?, tooltip? : String) -> Result[Tray, String]

Creates a tray handle with an optional icon path and initial tooltip.

  • identifier should be a stable, non-empty id for the tray instance.
  • icon may be None to request the platform default tray icon.
  • tooltip becomes the initial hover text when the platform supports it.

The returned handle starts hidden, so callers can finish any last setup and then call show(). Empty or whitespace-only identifiers are normalized back to default_identifier(), and failures include the latest native error when the backend provides one.

On success this returns Ok(tray) with a live handle that can be shown, hidden, updated, pumped, and eventually destroyed. On failure this returns Err(message) with either the support-probe failure or the most recent backend creation error.

Native tray operations, including destroy(), must run on the thread that called create(); macOS already required this, and it is now enforced on all platforms.

Example

let tray = @proton_tray.create(icon=Some("/path/to/icon.png"), tooltip="My App").unwrap()
let _ = tray.show()
// ... run the application event loop, calling `tray.pump()` as needed ...
tray.destroy()

#
current_platform

fn current_platform() -> Platform

Returns the desktop platform detected by the native backend for the current process.

The value is computed by the native stub so it matches the operating system that actually builds and runs the package.

Use this to branch platform-specific setup code around tray creation or to surface clearer diagnostics in logs and error messages. When the backend cannot map the host operating system to a known variant, this returns Unknown.

#
default_identifier

fn default_identifier() -> String

Returns the default identifier used by create() when callers do not provide one.

The identifier is used as the native tray instance id on platforms that require one, and keeping it stable makes logs and diagnostics easier to follow.

Applications with a single tray icon can usually rely on this value as-is. Multi-tray applications may still prefer to provide their own stable, application-specific identifiers so native backends can distinguish instances consistently across runs.

#
ensure_supported

fn ensure_supported() -> Result[Unit, String]

Validates that the native backend is available before any tray is created.

Use this when an application wants to show an actionable startup error instead of deferring the failure until create(). The error string is produced by the native backend when available, so callers can surface a platform-specific explanation to users.

Returns Ok(()) when tray creation should be possible on the current machine. Returns Err(message) when the backend is missing, the desktop runtime is unavailable, or the platform is unsupported.

#
is_supported

fn is_supported() -> Bool

Returns whether the native backend can create tray instances on the current machine.

On Windows this is expected to be true. On other platforms, or when the required desktop runtime is unavailable, this returns false. This probe is side-effect free from the MoonBit caller's perspective and is useful for gating UI paths that would otherwise call create().

This function answers only whether tray support appears available right now; it does not allocate a tray handle or make one visible. If you need a human readable explanation for a false result, call ensure_supported() instead.

Example

test "is_supported probes capability without creating a tray" {
let _ : Bool = is_supported()
}

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io