diff --git a/exp/examples/window/main.go b/exp/examples/window/main.go index f5f4b52a7..bd0ebdced 100644 --- a/exp/examples/window/main.go +++ b/exp/examples/window/main.go @@ -4,6 +4,8 @@ import ( _ "embed" "log" + "github.com/wailsapp/wails/exp/pkg/options" + "github.com/wailsapp/wails/exp/pkg/events" "github.com/wailsapp/wails/exp/pkg/application" @@ -13,7 +15,16 @@ func main() { app := application.New() // Create window - myWindow := app.NewWindow() + myWindow := app.NewWindowWithOptions(&options.Window{ + Title: "My Window", + Width: 800, + Height: 600, + URL: "https://www.google.com", + Mac: &options.MacWindow{ + //Backdrop: options.MacBackdropTranslucent, + TitleBar: options.TitleBarHiddenInset, + }, + }) myWindow.On(events.Mac.WindowDidBecomeMain, func() { println("Window did become main") }) diff --git a/exp/pkg/application/application_darwin.go b/exp/pkg/application/application_darwin.go index 4dd54d027..d749dc327 100644 --- a/exp/pkg/application/application_darwin.go +++ b/exp/pkg/application/application_darwin.go @@ -87,7 +87,7 @@ func (m *macosApp) getCurrentWindowID() uint { func (m *macosApp) setApplicationMenu(menu *Menu) { if menu == nil { // Create a default menu for mac - menu = m.createDefaultApplicationMenu() + menu = defaultApplicationMenu() } menu.Update() // Convert impl to macosMenu object @@ -104,17 +104,6 @@ func (m *macosApp) destroy() { C.destroyApp() } -func (m *macosApp) createDefaultApplicationMenu() *Menu { - // Create a default menu for mac - menu := NewMenu() - menu.AddRole(AppMenu) - menu.AddRole(FileMenu) - menu.AddRole(EditMenu) - menu.AddRole(ViewMenu) - - 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 20c09e239..fe90be88b 100644 --- a/exp/pkg/application/menu.go +++ b/exp/pkg/application/menu.go @@ -86,3 +86,14 @@ func (m *Menu) SetLabel(label string) { func (a *App) NewMenu() *Menu { return &Menu{} } + +func defaultApplicationMenu() *Menu { + menu := NewMenu() + menu.AddRole(AppMenu) + menu.AddRole(FileMenu) + menu.AddRole(EditMenu) + menu.AddRole(ViewMenu) + menu.AddRole(WindowMenu) + menu.AddRole(HelpMenu) + return menu +} diff --git a/exp/pkg/application/menuitem.go b/exp/pkg/application/menuitem.go index 0fe6409a7..5c4945953 100644 --- a/exp/pkg/application/menuitem.go +++ b/exp/pkg/application/menuitem.go @@ -122,6 +122,10 @@ func newRole(role Role) *MenuItem { return newServicesMenu() case SpeechMenu: return newSpeechMenu() + case WindowMenu: + return newWindowMenu() + case HelpMenu: + return newHelpMenu() case Hide: return newHideMenuItem() case HideOthers: @@ -164,6 +168,10 @@ func newRole(role Role) *MenuItem { return newZoomInMenuItem() case ZoomOut: return newZoomOutMenuItem() + case Minimize: + return newMinimizeMenuItem() + case Zoom: + return newZoomMenuItem() default: println("No support for role:", role) diff --git a/exp/pkg/application/menuitem_darwin.go b/exp/pkg/application/menuitem_darwin.go index b554da718..b26d9e7aa 100644 --- a/exp/pkg/application/menuitem_darwin.go +++ b/exp/pkg/application/menuitem_darwin.go @@ -323,12 +323,6 @@ static void showAll(void) { [[NSApplication sharedApplication] unhideAllApplications:nil]; } -// closeWindow closes the current window -static void closeWindow(void) { - [NSApp sendAction:@selector(performClose:) to:nil from:nil]; -} - - */ import "C" import ( @@ -391,40 +385,6 @@ func newMenuItemImpl(item *MenuItem) *macosMenuItem { return result } -func newAppMenu() *MenuItem { - appMenu := NewMenu() - appMenu.AddRole(About) - appMenu.AddSeparator() - appMenu.AddRole(ServicesMenu) - appMenu.AddSeparator() - appMenu.AddRole(Hide) - appMenu.AddRole(HideOthers) - appMenu.AddRole(UnHide) - appMenu.AddSeparator() - appMenu.AddRole(Quit) - subMenu := newSubMenuItem(globalApplication.Name()) - subMenu.submenu = appMenu - return subMenu -} - -func newEditMenu() *MenuItem { - editMenu := NewMenu() - editMenu.AddRole(Undo) - editMenu.AddRole(Redo) - editMenu.AddSeparator() - editMenu.AddRole(Cut) - editMenu.AddRole(Copy) - editMenu.AddRole(Paste) - editMenu.AddRole(PasteAndMatchStyle) - editMenu.AddRole(Delete) - editMenu.AddRole(SelectAll) - editMenu.AddSeparator() - editMenu.AddRole(SpeechMenu) - subMenu := newSubMenuItem("Edit") - subMenu.submenu = editMenu - return subMenu -} - func newSpeechMenu() *MenuItem { speechMenu := NewMenu() speechMenu.Add("Start Speaking"). @@ -549,7 +509,10 @@ func newCloseMenuItem() *MenuItem { return newMenuItem("Close"). SetAccelerator("CmdOrCtrl+w"). OnClick(func(ctx *Context) { - C.closeWindow() + currentWindow := globalApplication.GetCurrentWindow() + if currentWindow != nil { + currentWindow.Close() + } }) } @@ -635,3 +598,24 @@ func newZoomOutMenuItem() *MenuItem { } }) } + +func newMinimizeMenuItem() *MenuItem { + return newMenuItem("Minimize"). + SetAccelerator("CmdOrCtrl+M"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.GetCurrentWindow() + if currentWindow != nil { + currentWindow.Minimize() + } + }) +} + +func newZoomMenuItem() *MenuItem { + return newMenuItem("Zoom"). + OnClick(func(ctx *Context) { + currentWindow := globalApplication.GetCurrentWindow() + if currentWindow != nil { + currentWindow.Zoom() + } + }) +} diff --git a/exp/pkg/application/roles.go b/exp/pkg/application/roles.go index c36a8c45d..00f32148d 100644 --- a/exp/pkg/application/roles.go +++ b/exp/pkg/application/roles.go @@ -10,11 +10,14 @@ type Role uint // These constants need to be kept in sync with `v2/internal/frontend/desktop/darwin/Role.h` const ( - NoRole Role = iota - AppMenu Role = iota - EditMenu Role = iota - ViewMenu Role = iota - ServicesMenu Role = iota + NoRole Role = iota + AppMenu Role = iota + EditMenu Role = iota + ViewMenu Role = iota + WindowMenu Role = iota + ServicesMenu Role = iota + HelpMenu Role = iota + Hide Role = iota HideOthers Role = iota UnHide Role = iota @@ -39,7 +42,11 @@ const ( ZoomOut Role = iota ToggleFullscreen Role = iota - //MinimizeRole Role = + Minimize Role = iota + Zoom Role = iota + //Front Role = iota + //WindowRole Role = iota + //QuitRole Role = //TogglefullscreenRole Role = "togglefullscreen" //ViewMenuRole Role = "viewMenu" @@ -64,22 +71,6 @@ func newFileMenu() *MenuItem { return subMenu } -/* - { - label: 'View', - submenu: [ - { role: 'reload' }, - { role: 'forceReload' }, - { role: 'toggleDevTools' }, - { type: 'separator' }, - { role: 'resetZoom' }, - { role: 'zoomIn' }, - { role: 'zoomOut' }, - { type: 'separator' }, - { role: 'togglefullscreen' } - ] - }, -*/ func newViewMenu() *MenuItem { viewMenu := NewMenu() viewMenu.AddRole(Reload) @@ -95,3 +86,63 @@ func newViewMenu() *MenuItem { subMenu.submenu = viewMenu return subMenu } + +func newAppMenu() *MenuItem { + appMenu := NewMenu() + appMenu.AddRole(About) + appMenu.AddSeparator() + appMenu.AddRole(ServicesMenu) + appMenu.AddSeparator() + appMenu.AddRole(Hide) + appMenu.AddRole(HideOthers) + appMenu.AddRole(UnHide) + appMenu.AddSeparator() + appMenu.AddRole(Quit) + subMenu := newSubMenuItem(globalApplication.Name()) + subMenu.submenu = appMenu + return subMenu +} + +func newEditMenu() *MenuItem { + editMenu := NewMenu() + editMenu.AddRole(Undo) + editMenu.AddRole(Redo) + editMenu.AddSeparator() + editMenu.AddRole(Cut) + editMenu.AddRole(Copy) + editMenu.AddRole(Paste) + if runtime.GOOS == "darwin" { + editMenu.AddRole(PasteAndMatchStyle) + editMenu.AddRole(PasteAndMatchStyle) + editMenu.AddRole(Delete) + editMenu.AddRole(SelectAll) + editMenu.AddSeparator() + editMenu.AddRole(SpeechMenu) + } else { + editMenu.AddRole(Delete) + editMenu.AddSeparator() + editMenu.AddRole(SelectAll) + } + subMenu := newSubMenuItem("Edit") + subMenu.submenu = editMenu + return subMenu +} + +func newWindowMenu() *MenuItem { + menu := NewMenu() + menu.AddRole(Minimize) + menu.AddRole(Zoom) + subMenu := newSubMenuItem("Window") + subMenu.submenu = menu + return subMenu +} + +func newHelpMenu() *MenuItem { + menu := NewMenu() + menu.Add("Learn More").OnClick(func(ctx *Context) { + globalApplication.GetCurrentWindow().NavigateToURL("https://wails.io") + }) + subMenu := newSubMenuItem("Help") + subMenu.submenu = menu + return subMenu +} diff --git a/exp/pkg/application/window.go b/exp/pkg/application/window.go index 169c18892..64eb952b3 100644 --- a/exp/pkg/application/window.go +++ b/exp/pkg/application/window.go @@ -40,6 +40,9 @@ type windowImpl interface { resetZoom() zoomIn() zoomOut() + close() + zoom() + minimize() } type Window struct { @@ -347,3 +350,24 @@ func (w *Window) ZoomOut() { } w.impl.zoomOut() } + +func (w *Window) Close() { + if w.impl == nil { + return + } + w.impl.close() +} + +func (w *Window) Minimize() { + if w.impl == nil { + return + } + w.impl.minimize() +} + +func (w *Window) Zoom() { + if w.impl == nil { + return + } + w.impl.zoom() +} diff --git a/exp/pkg/application/window_darwin.go b/exp/pkg/application/window_darwin.go index bf33fcf2b..a039cf15f 100644 --- a/exp/pkg/application/window_darwin.go +++ b/exp/pkg/application/window_darwin.go @@ -44,9 +44,11 @@ void* windowNew(unsigned int id, int width, int height) { WKWebView* webView = [[WKWebView alloc] initWithFrame:frame configuration:config]; [view addSubview:webView]; + + // Ensure webview resizes with the window + [webView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + delegate.webView = webView; - - delegate.hideOnClose = false; return window; } @@ -507,6 +509,38 @@ void windowDestroy(void* nsWindow) { }); } + +// windowClose closes the current window +static void windowClose(void *window) { + dispatch_async(dispatch_get_main_queue(), ^{ + // close window + [(NSWindow*)window close]; + }); +} + +// windowZoom +static void windowZoom(void *window) { + dispatch_async(dispatch_get_main_queue(), ^{ + // zoom window + [(NSWindow*)window zoom:nil]; + }); +} + +// miniaturize +static void windowMiniaturize(void *window) { + dispatch_async(dispatch_get_main_queue(), ^{ + // miniaturize window + [(NSWindow*)window miniaturize:nil]; + }); +} + +// zoom maximizes the window to the screen dimensions +static void windowMaximize(void *window) { + dispatch_async(dispatch_get_main_queue(), ^{ + // maximize window + [(NSWindow*)window zoom:nil]; + }); +} */ import "C" import ( @@ -526,6 +560,22 @@ type macosWindow struct { // devtools } +func (w *macosWindow) zoom() { + C.windowZoom(w.nsWindow) +} + +func (w *macosWindow) minimize() { + C.windowMiniaturize(w.nsWindow) +} + +func (w *macosWindow) windowZoom() { + C.windowZoom(w.nsWindow) +} + +func (w *macosWindow) close() { + C.windowClose(w.nsWindow) +} + func (w *macosWindow) zoomIn() { C.windowZoomIn(w.nsWindow) } @@ -695,8 +745,12 @@ func (w *macosWindow) run() { w.setTitle(w.options.Title) w.setAlwaysOnTop(w.options.AlwaysOnTop) w.setResizable(!w.options.DisableResize) - w.setMinSize(w.options.MinWidth, w.options.MinHeight) - w.setMaxSize(w.options.MaxWidth, w.options.MaxHeight) + if w.options.MinWidth != 0 || w.options.MinHeight != 0 { + w.setMinSize(w.options.MinWidth, w.options.MinHeight) + } + if w.options.MaxWidth != 0 || w.options.MaxHeight != 0 { + w.setMaxSize(w.options.MaxWidth, w.options.MaxHeight) + } if w.options.EnableDevTools { w.enableDevTools() } diff --git a/exp/pkg/options/mac.go b/exp/pkg/options/mac.go index 40730bc7e..01a6c9d27 100644 --- a/exp/pkg/options/mac.go +++ b/exp/pkg/options/mac.go @@ -40,15 +40,13 @@ type TitleBar struct { } // TitleBarDefault results in the default Mac Titlebar -func TitleBarDefault() *TitleBar { - return &TitleBar{ - AppearsTransparent: false, - Hide: false, - HideTitle: false, - FullSizeContent: false, - UseToolbar: false, - HideToolbarSeparator: false, - } +var TitleBarDefault = &TitleBar{ + AppearsTransparent: false, + Hide: false, + HideTitle: false, + FullSizeContent: false, + UseToolbar: false, + HideToolbarSeparator: false, } // Credit: Comments from Electron site @@ -56,30 +54,24 @@ func TitleBarDefault() *TitleBar { // TitleBarHidden results in a hidden title bar and a full size content window, // yet the title bar still has the standard window controls (“traffic lights”) // in the top left. -func TitleBarHidden() *TitleBar { - return &TitleBar{ - AppearsTransparent: true, - Hide: false, - HideTitle: true, - FullSizeContent: true, - UseToolbar: false, - HideToolbarSeparator: false, - } +var TitleBarHidden = &TitleBar{ + AppearsTransparent: true, + Hide: false, + HideTitle: true, + FullSizeContent: true, + UseToolbar: false, + HideToolbarSeparator: false, } // TitleBarHiddenInset results in a hidden title bar with an alternative look where // the traffic light buttons are slightly more inset from the window edge. -func TitleBarHiddenInset() *TitleBar { - - return &TitleBar{ - AppearsTransparent: true, - Hide: false, - HideTitle: true, - FullSizeContent: true, - UseToolbar: true, - HideToolbarSeparator: true, - } - +var TitleBarHiddenInset = &TitleBar{ + AppearsTransparent: true, + Hide: false, + HideTitle: true, + FullSizeContent: true, + UseToolbar: true, + HideToolbarSeparator: true, } // MacAppearanceType is a type of Appearance for Cocoa windows