diff --git a/examples/Examples.re b/examples/Examples.re
index abf3f34ae..11e363e3c 100644
--- a/examples/Examples.re
+++ b/examples/Examples.re
@@ -85,6 +85,7 @@ let examples = [
render: _ => NativeMenuExample.render(),
source: "NativeMenuExample.re",
},
+ {name: "Native: Tray", render: _w => Tray.render(), source: "Tray.re"},
{
name: "Native: Inputs",
render: _ => NativeInputExample.render(),
diff --git a/examples/Tray.re b/examples/Tray.re
new file mode 100644
index 000000000..69cbb750a
--- /dev/null
+++ b/examples/Tray.re
@@ -0,0 +1,36 @@
+open Revery;
+open Revery.UI;
+open Revery.UI.Components;
+
+let%component make = () => {
+ let%hook () =
+ Hooks.effect(
+ OnMount,
+ () => {
+ let trayImage =
+ Native.Tray.make(
+ ~imagePath=Environment.getAssetPath("outrun-logo.png"),
+ (),
+ );
+
+ let trayText =
+ Native.Tray.make() |> Native.Tray.setTitle(~text="Hello Revery!");
+
+ Some(
+ () => {
+ trayImage |> Native.Tray.remove;
+ trayText |> Native.Tray.remove;
+ },
+ );
+ },
+ );
+
+
+
+ ;
+};
+
+let render = () => make();
diff --git a/src/Native/ReveryCocoa.h b/src/Native/ReveryCocoa.h
index f058f9de1..2a7528308 100644
--- a/src/Native/ReveryCocoa.h
+++ b/src/Native/ReveryCocoa.h
@@ -19,6 +19,13 @@ void revery_setIconProgress_cocoa(void* dt, double progress);
void revery_setIconProgressIndeterminate_cocoa(void *dt);
void revery_hideIconProgress_cocoa(void* ip);
+/* Tray */
+void *revery_setTrayTitle_cocoa(void *nsStatusItem, const char *titleText);
+void revery_removeStatusItem_cocoa(void* nsStatusItem);
+
+/* Image functions */
+void *revery_makeImageFromAbsolutePath_cocoa(const char *image_path_v);
+
/* Open functions */
int revery_openURL_cocoa(const char *url_string);
int revery_openFile_cocoa(const char *path_string);
diff --git a/src/Native/Revery_Native.re b/src/Native/Revery_Native.re
index 014cc8bcd..5b9430e09 100644
--- a/src/Native/Revery_Native.re
+++ b/src/Native/Revery_Native.re
@@ -5,6 +5,7 @@ module Notification = Notification;
module Shell = Shell;
module Locale = Locale;
module Gtk = Gtk;
+module Tray = Tray;
module Menu = Menu;
module Input = Input;
module Window = Window;
diff --git a/src/Native/Tray.re b/src/Native/Tray.re
new file mode 100644
index 000000000..7378af283
--- /dev/null
+++ b/src/Native/Tray.re
@@ -0,0 +1,29 @@
+%import
+"config.h";
+
+type t;
+
+open {
+ external c_make: (~imagePath: string=?, unit) => t =
+ "revery_makeTrayHandle";
+ external c_setTitle: (t, ~text: string) => t = "revery_setTrayTitle";
+ external c_remove: t => unit = "revery_removeTrayItem";
+ };
+
+%if
+defined(USE_COCOA);
+
+let make = (~imagePath=?, ()) => {
+ let trayHandle = c_make(~imagePath?, ());
+ Gc.finalise(NSObject.release, trayHandle);
+ trayHandle;
+};
+
+[%%else];
+
+let make = c_make;
+
+[%%endif];
+
+let setTitle = c_setTitle;
+let remove = c_remove;
diff --git a/src/Native/Tray.rei b/src/Native/Tray.rei
new file mode 100644
index 000000000..5888c9770
--- /dev/null
+++ b/src/Native/Tray.rei
@@ -0,0 +1,38 @@
+type t;
+
+/**
+ * make
+ *
+ * Takes an optional [imagePath] which is an absolute path to a in image-file.
+ * Returns a newly created Tray.t;
+ *
+ * Examples:
+ * Tray.make(~imagePath="/absolute/path/to/image.png", ()) |> ignore;
+ * Tray.make(~imagePath=Environment.getAssetPath("some_asset_image.png", ()) |> ignore;
+ * let tray = Tray.make();
+ */
+let make: (~imagePath: string=?, unit) => t;
+
+/**
+ * setTitle
+ *
+ * Takes a [title] of string and sets the tray's text to it.
+ * Returns the updated tray item.
+ * *
+ * Examples:
+ * tray |> Tray.setTitle(~text="Hello Revery!") |> ignore;
+ * let tray = Tray.make() |> Tray.setTitle(~text="Hello Revery!");
+ * Tray.setTitle(tray, ~text="Hello World!") |> ignore;
+ */
+let setTitle: (t, ~text: string) => t;
+
+/**
+ * remove
+ *
+ * Given a [t] removes the tray item from the tray.
+ * *
+ * Example:
+ * tray |> Tray.remove;
+ * Tray.remove(tray);
+ */
+let remove: t => unit;
diff --git a/src/Native/dune b/src/Native/dune
index 55d4289f8..1f5571923 100644
--- a/src/Native/dune
+++ b/src/Native/dune
@@ -12,8 +12,8 @@
(language c)
(names Revery_Native dialog dialog_cocoa dialog_win32 dialog_gtk
notification notification_cocoa environment environment_mac
- environment_linux environment_windows icon icon_cocoa icon_win32 shell
- shell_cocoa shell_gtk shell_win32 locale locale_cocoa locale_win32 menu
+ environment_linux environment_windows icon icon_cocoa icon_win32 image_cocoa shell
+ shell_cocoa shell_gtk shell_win32 tray tray_cocoa locale locale_cocoa locale_win32 menu
menu_cocoa input input_cocoa window window_cocoa utilities ReveryGtk
ReveryGtk_Widget ReveryAppDelegate ReveryAppDelegate_func ReveryNSObject
ReveryNSView ReveryNSViewCoords ReveryMenuItemTarget ReveryButtonTarget
diff --git a/src/Native/image_cocoa.c b/src/Native/image_cocoa.c
new file mode 100644
index 000000000..fed24481c
--- /dev/null
+++ b/src/Native/image_cocoa.c
@@ -0,0 +1,16 @@
+#include "config.h"
+#ifdef USE_COCOA
+#include
+
+#import
+
+void *revery_makeImageFromAbsolutePath_cocoa(const char *imagePath) {
+ NSString *nsImagePath =
+ [NSString stringWithCString:imagePath encoding:NSUTF8StringEncoding];
+
+ NSImage *nsImage = [[NSImage alloc]initWithContentsOfFile:nsImagePath];
+
+ return nsImage;
+}
+
+#endif
diff --git a/src/Native/tray.c b/src/Native/tray.c
new file mode 100644
index 000000000..4724f00b3
--- /dev/null
+++ b/src/Native/tray.c
@@ -0,0 +1,85 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#include "caml_values.h"
+
+#include "config.h"
+#ifdef USE_WIN32
+#include "ReveryWin32.h"
+#elif USE_COCOA
+#include "ReveryCocoa.h"
+#import
+#elif USE_GTK
+#include "ReveryGtk.h"
+#endif
+
+#include "utilities.h"
+
+CAMLprim value revery_makeTrayHandle(value vImagePath) {
+ CAMLparam1(vImagePath);
+ CAMLlocal1(result);
+
+#ifdef USE_COCOA
+ NSStatusItem *statusItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength];
+ [(NSObject *)statusItem retain];
+
+ if (vImagePath != Val_none) {
+ const char *imagePath = String_val(Some_val(vImagePath));
+
+ NSImage *nsImage = revery_makeImageFromAbsolutePath_cocoa(imagePath);
+
+ statusItem.button.image = nsImage;
+
+ UNUSED(imagePath);
+ }
+
+ result = revery_wrapPointer(statusItem);
+
+ CAMLreturn(result);
+#elif USE_WIN32
+ result = caml_alloc(sizeof(NULL), Abstract_tag);
+#else
+ result = caml_alloc(sizeof(NULL), Abstract_tag);
+#endif
+ CAMLreturn(result);
+}
+
+CAMLprim value revery_setTrayTitle(value vTrayHandle, value vTitle) {
+ CAMLparam2(vTrayHandle, vTitle);
+ CAMLlocal1(result);
+#ifdef USE_COCOA
+ void* statusItem = revery_unwrapPointer(vTrayHandle);
+
+ const char *title = String_val(vTitle);
+
+ revery_setTrayTitle_cocoa(statusItem, title);
+
+ result = revery_wrapPointer(statusItem);
+
+ CAMLreturn(result);
+#elif USE_WIN32
+ result = caml_alloc(sizeof(NULL), Abstract_tag);
+#else
+ result = caml_alloc(sizeof(NULL), Abstract_tag);
+#endif
+ CAMLreturn(result);
+}
+
+void revery_removeTrayItem(value vTrayHandle) {
+ CAMLparam1(vTrayHandle);
+#ifdef USE_COCOA
+ void* statusItem = revery_unwrapPointer(vTrayHandle);
+
+ revery_removeStatusItem_cocoa(statusItem);
+
+ CAMLreturn0;
+#elif USE_WIN32
+ CAMLreturn0;
+#else
+ CAMLreturn0;
+#endif
+}
diff --git a/src/Native/tray_cocoa.c b/src/Native/tray_cocoa.c
new file mode 100644
index 000000000..2c9b4fb8f
--- /dev/null
+++ b/src/Native/tray_cocoa.c
@@ -0,0 +1,21 @@
+#include "config.h"
+#ifdef USE_COCOA
+#include
+
+#import
+
+void *revery_setTrayTitle_cocoa(NSStatusItem* statusItem, const char *titleText) {
+ NSString *nsTitle =
+ [NSString stringWithCString:titleText encoding:NSUTF8StringEncoding];
+
+ statusItem.button.image = NULL;
+ statusItem.button.title = nsTitle;
+
+ return statusItem;
+}
+
+void revery_removeStatusItem_cocoa(NSStatusItem* statusItem) {
+ [[NSStatusBar systemStatusBar] removeStatusItem: statusItem];
+}
+
+#endif