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