diff --git a/v2/cmd/wails/internal/commands/build/build.go b/v2/cmd/wails/internal/commands/build/build.go index 27035d83..a74ac497 100644 --- a/v2/cmd/wails/internal/commands/build/build.go +++ b/v2/cmd/wails/internal/commands/build/build.go @@ -117,12 +117,20 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) { } // Tags + experimental := false userTags := []string{} for _, tag := range strings.Split(tags, " ") { thisTag := strings.TrimSpace(tag) if thisTag != "" { userTags = append(userTags, thisTag) } + if thisTag == "exp" { + experimental = true + } + } + + if runtime.GOOS == "darwin" && !experimental { + return fmt.Errorf("MacOS version coming soon!") } // Webview2 installer strategy (download by default) diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index 75657ef2..1bdb4917 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -1252,7 +1252,7 @@ void createDelegate(struct Application *app) { app->delegate = delegate; - msg_id(app->application, s("setDelegate:"), delegate); + msg_id(app->application, s("setDelegate:"), delegate); } bool windowShouldClose(id self, SEL cmd, id sender) { diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.h b/v2/internal/frontend/desktop/darwin/AppDelegate.h new file mode 100644 index 00000000..3799aae0 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.h @@ -0,0 +1,20 @@ +// +// AppDelegate.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef AppDelegate_h +#define AppDelegate_h + +#import + +@interface AppDelegate : NSResponder + +@property bool alwaysOnTop; +@property (retain) NSWindow* mainWindow; + +@end + +#endif /* AppDelegate_h */ diff --git a/v2/internal/frontend/desktop/darwin/AppDelegate.m b/v2/internal/frontend/desktop/darwin/AppDelegate.m new file mode 100644 index 00000000..24cfaa01 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/AppDelegate.m @@ -0,0 +1,53 @@ +// +// AppDelegate.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import + +#import "AppDelegate.h" + +@implementation AppDelegate +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return NO; +} +- (void)applicationWillFinishLaunching:(NSNotification *)aNotification { + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [self.mainWindow makeKeyAndOrderFront:self]; + if (self.alwaysOnTop) { + [self.mainWindow setLevel:NSStatusWindowLevel]; + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + [NSApp activateIgnoringOtherApps:YES]; +} +// +//- (void) CreateMenu { +// [NSApplication sharedApplication]; +// menubar = [[NSMenu new] autorelease]; +// id appMenuItem = [[NSMenuItem new] autorelease]; +// [menubar addItem:appMenuItem]; +// [NSApp setMainMenu:menubar]; +// id appMenu = [[NSMenu new] autorelease]; +// id appName = [[NSProcessInfo processInfo] processName]; +// id quitTitle = [@"Quit " stringByAppendingString:appName]; +// id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:quitTitle +// action:@selector(terminate:) keyEquivalent:@"q"] +// autorelease]; +// [appMenu addItem:quitMenuItem]; +// [appMenuItem setSubmenu:appMenu]; +//} +// +//- (void) dealloc { +// [super dealloc]; +// window = nil; +// menubar = nil; +//} + +@synthesize touchBar; + +@end diff --git a/v2/internal/frontend/desktop/darwin/Application.h b/v2/internal/frontend/desktop/darwin/Application.h new file mode 100644 index 00000000..705ef98f --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/Application.h @@ -0,0 +1,31 @@ +// +// Application.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef Application_h +#define Application_h + +#import +#import +#import "WailsContext.h" + +#define ON_MAIN_THREAD(str) dispatch_async(dispatch_get_main_queue(), ^{ str; }); + +WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent); +void Run(void*); + +void SetTitle(WailsContext *ctx, const char *title); +void Center(WailsContext *ctx); +void SetSize(WailsContext *ctx, int width, int height); +void SetMinWindowSize(WailsContext *ctx, int width, int height); +void SetMaxWindowSize(WailsContext *ctx, int width, int height); +void SetPosition(WailsContext *ctx, int x, int y); +void Fullscreen(WailsContext *ctx); +void UnFullscreen(WailsContext *ctx); +void Minimise(WailsContext *ctx); +void UnMinimise(WailsContext *ctx); + +#endif /* Application_h */ diff --git a/v2/internal/frontend/desktop/darwin/Application.m b/v2/internal/frontend/desktop/darwin/Application.m new file mode 100644 index 00000000..d2dd7d9b --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/Application.m @@ -0,0 +1,101 @@ +//// +//// Window.m +//// test +//// +//// Created by Lea Anthony on 10/10/21. +//// +// +#import +#import +#import "WailsContext.h" +#import "Application.h" +#import "AppDelegate.h" +// +// +WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent) { + + WailsContext *result = [WailsContext new]; + + [result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :appearance :windowIsTranslucent]; + [result SetTitle:title]; + [result Center]; + + result.alwaysOnTop = alwaysOnTop; + result.hideOnClose = hideWindowOnClose; + + return result; +} + +void SetTitle(WailsContext *ctx, const char *title) { + ON_MAIN_THREAD( + [ctx SetTitle:title]; + ); +} + +void SetSize(WailsContext *ctx, int width, int height) { + ON_MAIN_THREAD( + [ctx SetSize:width :height]; + ); +} + +void SetMinWindowSize(WailsContext *ctx, int width, int height) { + ON_MAIN_THREAD( + [ctx SetMinWindowSize:width :height]; + ); +} + +void SetMaxWindowSize(WailsContext *ctx, int width, int height) { + ON_MAIN_THREAD( + [ctx SetMaxWindowSize:width :height]; + ); +} + +void SetPosition(WailsContext *ctx, int x, int y) { + ON_MAIN_THREAD( + [ctx SetSize:x :y]; + ); +} + +void Center(WailsContext *ctx) { + ON_MAIN_THREAD( + [ctx Center]; + ); +} + +void Fullscreen(WailsContext *ctx) { + ON_MAIN_THREAD( + [ctx Fullscreen]; + ); +} + +void UnFullscreen(WailsContext *ctx) { + ON_MAIN_THREAD( + [ctx UnFullscreen]; + ); +} + +void Minimise(WailsContext *ctx) { + ON_MAIN_THREAD( + [ctx Minimise]; + ); +} + +void UnMinimise(WailsContext *ctx) { + ON_MAIN_THREAD( + [ctx UnMinimise]; + ); +} + +void Run(void *inctx) { + WailsContext *ctx = (WailsContext*) inctx; + [NSApplication sharedApplication]; + AppDelegate* delegate = [AppDelegate new]; + [NSApp setDelegate:(id)delegate]; + ctx.appdelegate = delegate; + delegate.mainWindow = ctx.mainWindow; + delegate.alwaysOnTop = ctx.alwaysOnTop; + [NSApp run]; + [ctx release]; + NSLog(@"Here"); +} +// diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.h b/v2/internal/frontend/desktop/darwin/WailsContext.h new file mode 100644 index 00000000..f2f55596 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsContext.h @@ -0,0 +1,45 @@ +// +// WailsContext.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef WailsContext_h +#define WailsContext_h + +#import + +@interface WailsWindow : NSWindow +- (BOOL)canBecomeKeyWindow; +@end + +@interface WailsContext : NSObject + +@property (retain) WailsWindow* mainWindow; +@property (nonatomic, assign) id appdelegate; + +@property bool hideOnClose; +@property bool shuttingDown; + +@property NSSize maxSize; +@property NSSize minSize; + +@property bool alwaysOnTop; + +- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(const char *)appearance :(bool)windowIsTranslucent; +- (void) SetSize:(int)width :(int)height; +- (void) SetPosition:(int)x :(int) y; +- (void) SetMinWindowSize:(int)minWidth :(int)minHeight; +- (void) SetMaxWindowSize:(int)maxWidth :(int)maxHeight; +- (void) SetTitle:(const char*)title; +- (void) Center; +- (void) Fullscreen; +- (void) UnFullscreen; +- (void) Minimise; +- (void) UnMinimise; + +@end + + +#endif /* WailsContext_h */ diff --git a/v2/internal/frontend/desktop/darwin/WailsContext.m b/v2/internal/frontend/desktop/darwin/WailsContext.m new file mode 100644 index 00000000..91e51c02 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WailsContext.m @@ -0,0 +1,213 @@ +// +// WailsContext.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import "WailsContext.h" +#import "WindowDelegate.h" + +@implementation WailsWindow + +- (BOOL)canBecomeKeyWindow +{ + return YES; +} + +@end + +@implementation WailsContext + +- (void) SetSize:(int)width :(int)height { + + if (self.shuttingDown) return; + + NSRect frame = [self.mainWindow frame]; + frame.origin.y += frame.size.height - height; + frame.size.width = width; + frame.size.height = height; + [self.mainWindow setFrame:frame display:TRUE animate:FALSE]; +} + +- (void) SetPosition:(int)x :(int)y { + + if (self.shuttingDown) return; + + NSScreen* screen = [self getCurrentScreen]; + NSRect windowFrame = [self.mainWindow frame]; + NSRect screenFrame = [screen frame]; + windowFrame.origin.x += screenFrame.origin.x + (float)x; + windowFrame.origin.y += (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; + + [self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE]; +} + +- (void) SetMinWindowSize:(int)minWidth :(int)minHeight { + + if (self.shuttingDown) return; + + NSSize size = { minWidth, minHeight }; + + self.minSize = size; + + [self.mainWindow setMinSize:size]; + + [self adjustWindowSize]; +} + + +- (void) SetMaxWindowSize:(int)maxWidth :(int)maxHeight { + + if (self.shuttingDown) return; + + NSSize size = { FLT_MAX, FLT_MAX }; + + size.width = maxWidth > 0 ? maxWidth : FLT_MAX; + size.height = maxHeight > 0 ? maxHeight : FLT_MAX; + + self.maxSize = size; + + [self.mainWindow setMinSize:size]; + + [self adjustWindowSize]; +} + + +- (void) adjustWindowSize { + + if (self.shuttingDown) return; + + NSRect currentFrame = [self.mainWindow frame]; + + if ( currentFrame.size.width > self.maxSize.width ) currentFrame.size.width = self.maxSize.width; + if ( currentFrame.size.width < self.minSize.width ) currentFrame.size.width = self.minSize.width; + if ( currentFrame.size.height > self.maxSize.height ) currentFrame.size.height = self.maxSize.height; + if ( currentFrame.size.height < self.minSize.height ) currentFrame.size.height = self.minSize.height; + + [self.mainWindow setFrame:currentFrame display:TRUE animate:FALSE]; + +} + +- (void) dealloc { + [super dealloc]; + [self.appdelegate release]; + [self.mainWindow release]; +} +- (NSScreen*) getCurrentScreen { + NSScreen* screen = [self.mainWindow screen]; + if( screen == NULL ) { + screen = [NSScreen mainScreen]; + } + return screen; +} + +- (void) SetTitle:(const char *)title { + NSString *_title = [NSString stringWithUTF8String:title]; + [self.mainWindow setTitle:_title]; +} + +- (void) Center { + [self.mainWindow center]; +} + +- (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(const char *)appearance :(bool)windowIsTranslucent { + + NSWindowStyleMask styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable; + + if (frameless) { + styleMask = NSWindowStyleMaskBorderless; + } else { + if (resizable) { + styleMask |= NSWindowStyleMaskResizable; + } + } + if (fullscreen) { + styleMask |= NSWindowStyleMaskFullScreen; + } + + if( fullSizeContent || frameless || titlebarAppearsTransparent ) { + styleMask |= NSWindowStyleMaskFullSizeContentView; + } + + self.mainWindow = [[[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height) + styleMask:styleMask backing:NSBackingStoreBuffered defer:NO] + autorelease]; + + if (frameless) { + return; + } + + if (useToolbar) { + id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; + [toolbar autorelease]; + [toolbar setShowsBaselineSeparator:!hideToolbarSeparator]; + [self.mainWindow setToolbar:toolbar]; + } + + [self.mainWindow setTitleVisibility:hideTitle]; + [self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent]; + [self.mainWindow canBecomeKeyWindow]; + + if (windowIsTranslucent) { + id contentView = [self.mainWindow contentView]; + NSVisualEffectView *effectView = [NSVisualEffectView alloc]; + NSRect bounds = [contentView bounds]; + [effectView initWithFrame:bounds]; + [effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; + [effectView setState:NSVisualEffectStateActive]; + [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil]; + } + + if (appearance != nil) { + NSString *name = [NSString stringWithUTF8String:appearance]; + NSAppearance *nsAppearance = [NSAppearance appearanceNamed:name]; + [self.mainWindow setAppearance:nsAppearance]; + } + + // Set up min/max + NSSize maxSize = { FLT_MAX, FLT_MAX }; + self.maxSize = maxSize; + NSSize minSize = { 0, 0 }; + self.minSize = minSize; + [self adjustWindowSize]; + + WindowDelegate *windowDelegate = [WindowDelegate new]; + windowDelegate.hideOnClose = hideWindowOnClose; + [self.mainWindow setDelegate:windowDelegate]; + + // Webview stuff here! + +} + +- (bool) isFullScreen { + long mask = [self.mainWindow styleMask]; + return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; +} + +// Fullscreen sets the main window to be fullscreen +- (void) Fullscreen { + if( ! [self isFullScreen] ) { + [self.mainWindow toggleFullScreen:nil]; + } +} + +// UnFullscreen resets the main window after a fullscreen +- (void) UnFullscreen { + if( [self isFullScreen] ) { + [self.mainWindow toggleFullScreen:nil]; + } +} + +- (void) Minimise { + [self.mainWindow miniaturize:nil]; +} + +- (void) UnMinimise { + [self.mainWindow deminiaturize:nil]; +} + +@end + diff --git a/v2/internal/frontend/desktop/darwin/WindowDelegate.h b/v2/internal/frontend/desktop/darwin/WindowDelegate.h new file mode 100644 index 00000000..e4ba038d --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WindowDelegate.h @@ -0,0 +1,18 @@ +// +// WindowDelegate.h +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#ifndef WindowDelegate_h +#define WindowDelegate_h + +@interface WindowDelegate : NSObject + +@property bool hideOnClose; + +@end + + +#endif /* WindowDelegate_h */ diff --git a/v2/internal/frontend/desktop/darwin/WindowDelegate.m b/v2/internal/frontend/desktop/darwin/WindowDelegate.m new file mode 100644 index 00000000..3ccd0a32 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/WindowDelegate.m @@ -0,0 +1,22 @@ +// +// WindowDelegate.m +// test +// +// Created by Lea Anthony on 10/10/21. +// + +#import +#import +#import "WindowDelegate.h" + +@implementation WindowDelegate + +- (BOOL)windowShouldClose:(NSWindow *)sender { + [sender orderOut:nil]; + if( self.hideOnClose == false ) { + NSLog(@"send message: WC"); + } + return !self.hideOnClose; +} + +@end diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index 7d45804e..63d1f5c7 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -30,7 +30,7 @@ type Frontend struct { assets *assetserver.DesktopAssetServer // main window handle - //mainWindow *Window + mainWindow *Window minWidth, minHeight, maxWidth, maxHeight int bindings *binding.Bindings dispatcher frontend.Dispatcher @@ -81,8 +81,12 @@ func (f *Frontend) Run(ctx context.Context) error { f.ctx = context.WithValue(ctx, "frontend", f) - //mainWindow := NewWindow(nil, f.frontendOptions) - //f.mainWindow = mainWindow + mainWindow := NewWindow(f.frontendOptions) + f.mainWindow = mainWindow + //go func() { + // time.Sleep(3 * time.Second) + // mainWindow.Center() + //}() // //var _debug = ctx.Value("debug") //if _debug != nil { @@ -111,7 +115,7 @@ func (f *Frontend) Run(ctx context.Context) error { // } //}() // - //mainWindow.Run() + mainWindow.Run() return nil } diff --git a/v2/internal/frontend/desktop/darwin/window.go b/v2/internal/frontend/desktop/darwin/window.go new file mode 100644 index 00000000..5252c636 --- /dev/null +++ b/v2/internal/frontend/desktop/darwin/window.go @@ -0,0 +1,84 @@ +package darwin + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation -framework Cocoa +#import +#import "Application.h" +#import "WailsContext.h" + +*/ +import "C" +import ( + "runtime" + "unsafe" + + "github.com/wailsapp/wails/v2/pkg/options" +) + +func init() { + runtime.LockOSThread() +} + +type Window struct { + context unsafe.Pointer +} + +func bool2Cint(value bool) C.int { + if value { + return C.int(1) + } + return C.int(0) +} + +func NewWindow(frontendOptions *options.App) *Window { + + frameless := bool2Cint(frontendOptions.Frameless) + resizable := bool2Cint(!frontendOptions.DisableResize) + fullscreen := bool2Cint(frontendOptions.Fullscreen) + alwaysOnTop := bool2Cint(frontendOptions.AlwaysOnTop) + webviewIsTransparent := bool2Cint(frontendOptions.AlwaysOnTop) + hideWindowOnClose := bool2Cint(frontendOptions.HideWindowOnClose) + + var fullSizeContent, hideTitleBar, hideTitle, useToolbar C.int + var titlebarAppearsTransparent, hideToolbarSeparator, windowIsTranslucent C.int + var appearance, title *C.char + + width := C.int(frontendOptions.Width) + height := C.int(frontendOptions.Height) + + title = C.CString(frontendOptions.Title) + + if frontendOptions.Mac != nil { + mac := frontendOptions.Mac + if mac.TitleBar != nil { + fullSizeContent = bool2Cint(mac.TitleBar.FullSizeContent) + hideTitleBar = bool2Cint(mac.TitleBar.HideTitleBar) + hideTitle = bool2Cint(mac.TitleBar.HideTitle) + useToolbar = bool2Cint(mac.TitleBar.UseToolbar) + titlebarAppearsTransparent = bool2Cint(mac.TitleBar.TitlebarAppearsTransparent) + hideToolbarSeparator = bool2Cint(mac.TitleBar.HideToolbarSeparator) + } + windowIsTranslucent = bool2Cint(mac.WindowIsTranslucent) + appearance = C.CString(string(mac.Appearance)) + } + var context *C.WailsContext = C.Create(title, width, height, frameless, resizable, fullscreen, fullSizeContent, hideTitleBar, titlebarAppearsTransparent, hideTitle, useToolbar, hideToolbarSeparator, webviewIsTransparent, alwaysOnTop, hideWindowOnClose, appearance, windowIsTranslucent) + + C.free(unsafe.Pointer(title)) + if appearance != nil { + C.free(unsafe.Pointer(appearance)) + } + + return &Window{ + context: unsafe.Pointer(context), + } +} + +//func (w *Window) Center() { +// C.Center(w.wailsApplication) +//} + +func (w *Window) Run() { + C.Run(w.context) + println("I exited!") +} diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 50788643..61c800d5 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -3,9 +3,11 @@ package options import ( "context" "embed" - "github.com/wailsapp/wails/v2/pkg/options/windows" "log" + "github.com/wailsapp/wails/v2/pkg/options/mac" + "github.com/wailsapp/wails/v2/pkg/options/windows" + "github.com/wailsapp/wails/v2/pkg/menu" "github.com/imdario/mergo" @@ -40,7 +42,7 @@ type App struct { //ContextMenus []*menu.ContextMenu //TrayMenus []*menu.TrayMenu Windows *windows.Options - //Mac *mac.Options + Mac *mac.Options } type RGBA struct {