Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(native): add initial tray-support for macOS #972

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/Examples.re
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
36 changes: 36 additions & 0 deletions examples/Tray.re
Original file line number Diff line number Diff line change
@@ -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;
},
);
},
);

<Center>
<Text
text="You should see two items in your tray-bar. One image and one with
the text \"Hello Revery!\""
/>
</Center>;
};

let render = () => make();
7 changes: 7 additions & 0 deletions src/Native/ReveryCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/Native/Revery_Native.re
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 29 additions & 0 deletions src/Native/Tray.re
Original file line number Diff line number Diff line change
@@ -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;
38 changes: 38 additions & 0 deletions src/Native/Tray.rei
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
type t;
lessp marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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;
4 changes: 2 additions & 2 deletions src/Native/dune
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions src/Native/image_cocoa.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "config.h"
#ifdef USE_COCOA
#include <stdio.h>

#import <Cocoa/Cocoa.h>

void *revery_makeImageFromAbsolutePath_cocoa(const char *imagePath) {
NSString *nsImagePath =
[NSString stringWithCString:imagePath encoding:NSUTF8StringEncoding];

NSImage *nsImage = [[NSImage alloc]initWithContentsOfFile:nsImagePath];

return nsImage;
}

#endif
85 changes: 85 additions & 0 deletions src/Native/tray.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <stdio.h>

#include <caml/alloc.h>
#include <caml/callback.h>
#include <caml/memory.h>
#include <caml/mlvalues.h>

#include "caml_values.h"

#include "config.h"
#ifdef USE_WIN32
#include "ReveryWin32.h"
#elif USE_COCOA
#include "ReveryCocoa.h"
#import <Cocoa/Cocoa.h>
#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];
lessp marked this conversation as resolved.
Show resolved Hide resolved
[(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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: is this still needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually, I don't know 🤔 Most likely I followed another example, but looking at the context, I was under the impression that it is, but this is your forte!

    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);
lessp marked this conversation as resolved.
Show resolved Hide resolved

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
}
21 changes: 21 additions & 0 deletions src/Native/tray_cocoa.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "config.h"
#ifdef USE_COCOA
#include <stdio.h>

#import <Cocoa/Cocoa.h>

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