diff --git a/exp/.gitignore b/exp/.gitignore index 90fb937c0..a57f29269 100644 --- a/exp/.gitignore +++ b/exp/.gitignore @@ -1,3 +1,4 @@ examples/kitchensink/kitchensink cmd/wails/wails /examples/systray/systray +/examples/window/window diff --git a/exp/examples/window/main.go b/exp/examples/window/main.go index 3c54679db..f5f4b52a7 100644 --- a/exp/examples/window/main.go +++ b/exp/examples/window/main.go @@ -12,13 +12,6 @@ import ( func main() { app := application.New() - // Create menu - appMenu := app.NewMenu() - appMenu.AddSubmenu("") - fileMenu := appMenu.AddSubmenu("File") - fileMenu.Add("New") - app.SetMenu(appMenu) - // Create window myWindow := app.NewWindow() myWindow.On(events.Mac.WindowDidBecomeMain, func() { diff --git a/exp/pkg/application/application.go b/exp/pkg/application/application.go index 58846b636..7facd4721 100644 --- a/exp/pkg/application/application.go +++ b/exp/pkg/application/application.go @@ -11,23 +11,33 @@ import ( "github.com/wailsapp/wails/exp/pkg/options" ) +var globalApplication *App + func init() { runtime.LockOSThread() } func New() *App { + if globalApplication != nil { + return globalApplication + } return NewWithOptions(nil) } func NewWithOptions(appOptions *options.Application) *App { + if globalApplication != nil { + return globalApplication + } if appOptions == nil { appOptions = options.ApplicationDefaults } - return &App{ + result := &App{ options: appOptions, applicationEventListeners: make(map[uint][]func()), systemTrays: make(map[uint]*SystemTray), } + globalApplication = result + return result } type platformApp interface { @@ -172,9 +182,8 @@ func (a *App) Run() error { go systray.Run() } - if a.ApplicationMenu != nil { - a.impl.setApplicationMenu(a.ApplicationMenu) - } + // set the application menu + a.impl.setApplicationMenu(a.ApplicationMenu) return a.impl.run() } diff --git a/exp/pkg/application/application_darwin.go b/exp/pkg/application/application_darwin.go index 2ed23ef8f..ecfccebdf 100644 --- a/exp/pkg/application/application_darwin.go +++ b/exp/pkg/application/application_darwin.go @@ -43,6 +43,81 @@ static void setApplicationMenu(void *menu) { [NSApp setMainMenu:menu]; } +// Get the application name +static char *getAppName(void) { + NSString *appName = [NSRunningApplication currentApplication].localizedName; + if( appName == nil ) { + appName = [[NSProcessInfo processInfo] processName]; + } + return strdup([appName UTF8String]); +} + +// Call the copy selector on the pasteboard +static void copyToPasteboard(char *text) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard clearContents]; + [pasteboard setString:[NSString stringWithUTF8String:text] forType:NSPasteboardTypeString]; +} + +// Call the paste selector on the pasteboard +static char *pasteFromPasteboard(void) { + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSString *text = [pasteboard stringForType:NSPasteboardTypeString]; + if( text == nil ) { + return NULL; + } + return strdup([text UTF8String]); +} + +// Call paste selector to paste text +static void paste(void) { + [NSApp sendAction:@selector(paste:) to:nil from:nil]; +} + +// Call copy selector to copy text +static void copy(void) { + [NSApp sendAction:@selector(copy:) to:nil from:nil]; +} + +// Call cut selector to cut text +static void cut(void) { + [NSApp sendAction:@selector(cut:) to:nil from:nil]; +} + +// Call selectAll selector to select all text +static void selectAll(void) { + [NSApp sendAction:@selector(selectAll:) to:nil from:nil]; +} + +// Call delete selector to delete text +static void delete(void) { + [NSApp sendAction:@selector(delete:) to:nil from:nil]; +} + +// Call undo selector to undo text +static void undo(void) { + [NSApp sendAction:@selector(undo:) to:nil from:nil]; +} + +// Call redo selector to redo text +static void redo(void) { + [NSApp sendAction:@selector(redo:) to:nil from:nil]; +} + +// Call startSpeaking selector to start speaking text +static void startSpeaking(void) { + [NSApp sendAction:@selector(startSpeaking:) to:nil from:nil]; +} + +// Call stopSpeaking selector to stop speaking text +static void stopSpeaking(void) { + [NSApp sendAction:@selector(stopSpeaking:) to:nil from:nil]; +} + +static void pasteAndMatchStyle(void) { + [NSApp sendAction:@selector(pasteAndMatchStyle:) to:nil from:nil]; +} + */ import "C" import ( @@ -56,22 +131,73 @@ type macosApp struct { applicationMenu unsafe.Pointer } -func (m macosApp) setApplicationMenu(menu *Menu) { +func (m *macosApp) setApplicationMenu(menu *Menu) { + if menu == nil { + // Create a default menu for mac + menu = m.createDefaultApplicationMenu() + } menu.Update() // Convert impl to macosMenu object m.applicationMenu = (menu.impl).(*macosMenu).nsMenu C.setApplicationMenu(m.applicationMenu) } -func (m macosApp) run() error { +func (m *macosApp) run() error { C.run() return nil } -func (m macosApp) destroy() { +func (m *macosApp) destroy() { C.destroyApp() } +func (m *macosApp) createDefaultApplicationMenu() *Menu { + // Create a default menu for mac + menu := NewMenu() + cAppName := C.getAppName() + defer C.free(unsafe.Pointer(cAppName)) + appName := C.GoString(cAppName) + appMenu := menu.AddSubmenu(appName) + appMenu.Add("Quit " + appName).SetAccelerator("CmdOrCtrl+q").OnClick(func(ctx *Context) { + globalApplication.Quit() + }) + editMenu := menu.AddSubmenu("Edit") + editMenu.Add("Undo").SetAccelerator("CmdOrCtrl+z").OnClick(func(ctx *Context) { + C.undo() + }) + editMenu.Add("Redo").SetAccelerator("CmdOrCtrl+Shift+z").OnClick(func(ctx *Context) { + C.redo() + }) + editMenu.AddSeparator() + editMenu.Add("Cut").SetAccelerator("CmdOrCtrl+x").OnClick(func(ctx *Context) { + C.cut() + }) + editMenu.Add("Copy").SetAccelerator("CmdOrCtrl+c").OnClick(func(ctx *Context) { + C.copy() + }) + editMenu.Add("Paste").SetAccelerator("CmdOrCtrl+v").OnClick(func(ctx *Context) { + C.paste() + }) + editMenu.Add("Paste and Match Style").SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+v").OnClick(func(ctx *Context) { + C.pasteAndMatchStyle() + }) + editMenu.Add("Delete").SetAccelerator("backspace").OnClick(func(ctx *Context) { + C.delete() + }) + editMenu.Add("Select All").SetAccelerator("CmdOrCtrl+a").OnClick(func(ctx *Context) { + C.selectAll() + }) + editMenu.AddSeparator() + speechMenu := editMenu.AddSubmenu("Speech") + speechMenu.Add("Start Speaking").SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+.").OnClick(func(ctx *Context) { + C.startSpeaking() + }) + speechMenu.Add("Stop Speaking").SetAccelerator("CmdOrCtrl+OptionOrAlt+Shift+,").OnClick(func(ctx *Context) { + C.stopSpeaking() + }) + return menu +} + func newPlatformApp(appOptions *options.Application) *macosApp { if appOptions == nil { appOptions = options.ApplicationDefaults diff --git a/exp/pkg/application/menu.go b/exp/pkg/application/menu.go index db7e20bc0..d6d920e81 100644 --- a/exp/pkg/application/menu.go +++ b/exp/pkg/application/menu.go @@ -11,6 +11,10 @@ type Menu struct { impl menuImpl } +func NewMenu() *Menu { + return &Menu{} +} + func (m *Menu) Add(label string) *MenuItem { result := newMenuItem(label) m.items = append(m.items, result) diff --git a/exp/pkg/application/menuitem.go b/exp/pkg/application/menuitem.go index 7eaad2833..4105fd221 100644 --- a/exp/pkg/application/menuitem.go +++ b/exp/pkg/application/menuitem.go @@ -130,7 +130,7 @@ func (m *MenuItem) handleClick() { func (m *MenuItem) SetAccelerator(shortcut string) *MenuItem { accelerator, err := parseAccelerator(shortcut) if err != nil { - println("ERROR: invalid accelerator", err) + println("ERROR: invalid accelerator:", err.Error()) return m } m.accelerator = accelerator