mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Initial Tray support for Mac
This commit is contained in:
parent
0a9ae0dff3
commit
3f82ceabba
12 changed files with 178 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -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
|
||||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ void processMessageDialogResponse(int);
|
|||
void processOpenFileDialogResponse(const char*);
|
||||
void processSaveFileDialogResponse(const char*);
|
||||
void processCallback(int);
|
||||
void processNotification(int);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
36
v2/internal/frontend/desktop/darwin/traymenu.go
Normal file
36
v2/internal/frontend/desktop/darwin/traymenu.go
Normal 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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -101,4 +101,7 @@ type Frontend interface {
|
|||
|
||||
// Browser
|
||||
BrowserOpenURL(url string)
|
||||
|
||||
// Tray Menu
|
||||
TrayMenuAdd(trayMenu *menu.TrayMenu)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue