Initial Tray support for Mac

This commit is contained in:
Lea Anthony 2022-04-30 12:00:00 +10:00
commit 3f82ceabba
No known key found for this signature in database
GPG key ID: 33DAF7BB90A58405
12 changed files with 178 additions and 18 deletions

1
.gitignore vendored
View file

@ -32,3 +32,4 @@ v2/test/kitchensink/frontend/package.json.md5
!v2/internal/ffenestri/windows/x64/WebView2Loader.dll
.idea/
v2/cmd/wails/internal/commands/initialise/templates/testtemplates/
v2/internal/frontend/desktop/darwin/test.xcodeproj

View file

@ -9,6 +9,7 @@
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
#import "message.h"
@implementation AppDelegate
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
@ -25,6 +26,7 @@
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
processNotification(0); // Notify Go
[NSApp activateIgnoringOtherApps:YES];
if ( self.startFullscreen ) {
NSWindowCollectionBehavior behaviour = [self.mainWindow collectionBehavior];

View file

@ -57,6 +57,10 @@ void AppendRole(void *inctx, void *inMenu, int role);
void SetAsApplicationMenu(void *inctx, void *inMenu);
void UpdateApplicationMenu(void *inctx);
/* Tray Menu */
void* NewNSStatusItem(const char* label);
void SetTrayMenu(void *nsStatusItem, void* nsMenu);
void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen);
void* AppendMenuItem(void* inctx, void* nsmenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID);
void AppendSeparator(void* inMenu);

View file

@ -270,6 +270,21 @@ void AppendRole(void *inctx, void *inMenu, int role) {
[menu appendRole :ctx :role];
}
void* NewNSStatusItem(const char* label) {
NSString *_label = safeInit(label);
NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
NSStatusItem *result = [[statusBar statusItemWithLength:NSVariableStatusItemLength] retain];
[result button].title = _label;
return result;
}
void SetTrayMenu(void *nsStatusItem, void* nsMenu) {
ON_MAIN_THREAD(
[(NSStatusItem*)nsStatusItem setMenu:(NSMenu *)nsMenu];
)
}
void* NewMenu(const char *name) {
NSString *title = @"";
if (name != nil) {

View file

@ -3,15 +3,6 @@
package darwin
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
#import <Foundation/Foundation.h>
#import "Application.h"
#import "WailsContext.h"
#include <stdlib.h>
*/
import "C"
import (
"bytes"
@ -31,14 +22,33 @@ import (
"github.com/wailsapp/wails/v2/internal/frontend"
"github.com/wailsapp/wails/v2/internal/frontend/assetserver"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/options"
)
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
#import <Foundation/Foundation.h>
#import "Application.h"
#import "WailsContext.h"
#include <stdlib.h>
*/
import "C"
const startURL = "wails://wails/"
type NotificationType uint8
const (
ApplicationDidFinishLaunching NotificationType = 0
)
var messageBuffer = make(chan string, 100)
var requestBuffer = make(chan *request, 100)
var callbackBuffer = make(chan uint, 10)
var notificationBuffer = make(chan NotificationType, 10)
type Frontend struct {
@ -57,15 +67,22 @@ type Frontend struct {
mainWindow *Window
bindings *binding.Bindings
dispatcher frontend.Dispatcher
trayMenus map[*menu.TrayMenu]*NSTrayMenu
applicationDidFinishLaunching bool
notificationCallbacks map[NotificationType][]func()
trayMenusBuffer []*menu.TrayMenu
}
func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.Logger, appBindings *binding.Bindings, dispatcher frontend.Dispatcher) *Frontend {
result := &Frontend{
frontendOptions: appoptions,
logger: myLogger,
bindings: appBindings,
dispatcher: dispatcher,
ctx: ctx,
frontendOptions: appoptions,
logger: myLogger,
bindings: appBindings,
dispatcher: dispatcher,
ctx: ctx,
trayMenus: make(map[*menu.TrayMenu]*NSTrayMenu),
notificationCallbacks: make(map[NotificationType][]func()),
}
result.startURL, _ = url.Parse(startURL)
@ -88,10 +105,17 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger.
go result.startMessageProcessor()
go result.startCallbackProcessor()
go result.startNotificationsProcessor()
result.registerNotificationCallback(ApplicationDidFinishLaunching, result.processTrayMenus)
return result
}
func (f *Frontend) registerNotificationCallback(notificationType NotificationType, callback func()) {
f.notificationCallbacks[notificationType] = append(f.notificationCallbacks[notificationType], callback)
}
func (f *Frontend) startMessageProcessor() {
for message := range messageBuffer {
f.processMessage(message)
@ -110,6 +134,11 @@ func (f *Frontend) startCallbackProcessor() {
}
}
}
func (f *Frontend) startNotificationsProcessor() {
for notification := range notificationBuffer {
f.handleNotification(notification)
}
}
func (f *Frontend) WindowReload() {
f.ExecJS("runtime.WindowReload();")
@ -393,6 +422,11 @@ func processMessage(message *C.char) {
messageBuffer <- goMessage
}
//export processNotification
func processNotification(notification NotificationType) {
notificationBuffer <- notification
}
//export processURLRequest
func processURLRequest(ctx unsafe.Pointer, url *C.char, method *C.char, headers *C.char, body unsafe.Pointer, bodyLen C.int) {
var goBody []byte
@ -413,3 +447,19 @@ func processURLRequest(ctx unsafe.Pointer, url *C.char, method *C.char, headers
func processCallback(callbackID uint) {
callbackBuffer <- callbackID
}
func (f *Frontend) handleNotification(notification NotificationType) {
switch notification {
case ApplicationDidFinishLaunching:
f.applicationDidFinishLaunching = true
for _, callback := range f.notificationCallbacks[notification] {
go callback()
}
}
}
func (f *Frontend) processTrayMenus() {
for _, trayMenu := range f.trayMenusBuffer {
f.mainWindow.TrayMenuAdd(trayMenu)
}
}

View file

@ -20,6 +20,20 @@ import (
"github.com/wailsapp/wails/v2/pkg/menu/keys"
)
func NewNSTrayMenu(context unsafe.Pointer, trayMenu *menu.TrayMenu) *NSTrayMenu {
c := NewCalloc()
defer c.Free()
theMenu := NewNSMenu(context, "")
processMenu(theMenu, trayMenu.Menu)
title := c.String(trayMenu.Label)
nsStatusItem := C.NewNSStatusItem(title)
C.SetTrayMenu(nsStatusItem, theMenu.nsmenu)
return &NSTrayMenu{
context: context,
nsStatusItem: nsStatusItem,
}
}
type NSMenu struct {
context unsafe.Pointer
nsmenu unsafe.Pointer

View file

@ -20,6 +20,7 @@ void processMessageDialogResponse(int);
void processOpenFileDialogResponse(const char*);
void processSaveFileDialogResponse(const char*);
void processCallback(int);
void processNotification(int);
#ifdef __cplusplus
}

View file

@ -0,0 +1,36 @@
package darwin
/*
#cgo CFLAGS: -x objective-c
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit
#import <Foundation/Foundation.h>
#import "Application.h"
#import "WailsContext.h"
#include <stdlib.h>
*/
import "C"
import (
"unsafe"
"github.com/wailsapp/wails/v2/pkg/menu"
)
func (f *Frontend) TrayMenuAdd(trayMenu *menu.TrayMenu) {
if f.applicationDidFinishLaunching == false {
f.trayMenusBuffer = append(f.trayMenusBuffer, trayMenu)
return
}
nsTrayMenu := f.mainWindow.TrayMenuAdd(trayMenu)
f.trayMenus[trayMenu] = nsTrayMenu
}
type NSTrayMenu struct {
context unsafe.Pointer
nsStatusItem unsafe.Pointer // NSStatusItem
}
func (w *Window) TrayMenuAdd(trayMenu *menu.TrayMenu) *NSTrayMenu {
return NewNSTrayMenu(w.context, trayMenu)
}

View file

@ -112,6 +112,10 @@ func NewWindow(frontendOptions *options.App, debugMode bool) *Window {
result.SetApplicationMenu(frontendOptions.Menu)
}
if frontendOptions.TrayMenu != nil {
result.TrayMenuAdd(frontendOptions.TrayMenu)
}
return result
}

View file

@ -101,4 +101,7 @@ type Frontend interface {
// Browser
BrowserOpenURL(url string)
// Tray Menu
TrayMenuAdd(trayMenu *menu.TrayMenu)
}

View file

@ -1,7 +1,18 @@
package menu
import (
"context"
"log"
goruntime "runtime"
)
type TrayMenuAdd interface {
TrayMenuAdd(menu *TrayMenu)
}
// TrayMenu are the options
type TrayMenu struct {
ctx context.Context
// Label is the text we wish to display in the tray
Label string
@ -27,7 +38,7 @@ type TrayMenu struct {
Tooltip string
// Callback function when menu clicked
//Click Callback `json:"-"`
Click Callback
// Disabled makes the item unselectable
Disabled bool
@ -41,3 +52,21 @@ type TrayMenu struct {
// OnClose is called when the Menu is closed
OnClose func()
}
func NewTrayMenu(ctx context.Context) *TrayMenu {
return &TrayMenu{
ctx: ctx,
}
}
func (t *TrayMenu) Show() {
result := t.ctx.Value("frontend")
if result == nil {
pc, _, _, _ := goruntime.Caller(1)
funcName := goruntime.FuncForPC(pc).Name()
log.Fatalf("invalid context at '%s'", funcName)
}
println("\n\n\n\nFWEFWEFWFE")
result.(TrayMenuAdd).TrayMenuAdd(t)
}

View file

@ -56,9 +56,10 @@ type App struct {
//ContextMenus []*menu.ContextMenu
//TrayMenus []*menu.TrayMenu
Windows *windows.Options
Mac *mac.Options
Linux *linux.Options
Windows *windows.Options
Mac *mac.Options
Linux *linux.Options
TrayMenu *menu.TrayMenu
}
type RGBA struct {