mirror of
https://github.com/taigrr/wails.git
synced 2026-04-16 19:55:05 -07:00
Compare commits
21 Commits
v2-alpha-a
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1203ae64b8 | ||
|
|
26ed8002b9 | ||
|
|
cf23bffc67 | ||
|
|
d70f6fffe7 | ||
|
|
54c99fc386 | ||
|
|
86c1ea5e6a | ||
|
|
b394c1914c | ||
|
|
91c2ddf155 | ||
|
|
712ad96d2a | ||
|
|
86b4a4f2f5 | ||
|
|
4b9786abc9 | ||
|
|
fd96ebc050 | ||
|
|
939e0f5975 | ||
|
|
6a7a288a0f | ||
|
|
0564d0aa98 | ||
|
|
3a136a73ca | ||
|
|
50c219307f | ||
|
|
de3038b302 | ||
|
|
6eb4b0a419 | ||
|
|
41d2158375 | ||
|
|
5d7f57e80b |
@@ -3,8 +3,10 @@ package build
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
@@ -23,8 +25,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
command := app.NewSubCommand("build", "Builds the application")
|
command := app.NewSubCommand("build", "Builds the application")
|
||||||
|
|
||||||
// Setup target type flag
|
// Setup target type flag
|
||||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
//description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||||
command.StringFlag("t", description, &outputType)
|
//command.StringFlag("t", description, &outputType)
|
||||||
|
|
||||||
// Setup production flag
|
// Setup production flag
|
||||||
production := false
|
production := false
|
||||||
@@ -37,26 +39,37 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
compilerCommand := "go"
|
compilerCommand := "go"
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||||
|
|
||||||
|
compress := false
|
||||||
|
command.BoolFlag("compress", "Compress final binary", &compress)
|
||||||
|
|
||||||
// Setup Platform flag
|
// Setup Platform flag
|
||||||
platform := runtime.GOOS
|
platform := runtime.GOOS
|
||||||
command.StringFlag("platform", "Platform to target", &platform)
|
command.StringFlag("platform", "Platform to target", &platform)
|
||||||
|
|
||||||
// Quiet Build
|
// Verbosity
|
||||||
quiet := false
|
verbosity := 1
|
||||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
||||||
|
|
||||||
// ldflags to pass to `go`
|
// ldflags to pass to `go`
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
|
|
||||||
// Log to file
|
// Log to file
|
||||||
logFile := ""
|
//logFile := ""
|
||||||
command.StringFlag("l", "Log to file", &logFile)
|
//command.StringFlag("l", "Log to file", &logFile)
|
||||||
|
|
||||||
// Retain assets
|
// Retain assets
|
||||||
keepAssets := false
|
keepAssets := false
|
||||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
||||||
|
|
||||||
|
// Retain assets
|
||||||
|
outputFilename := ""
|
||||||
|
command.StringFlag("o", "Output filename", &outputFilename)
|
||||||
|
|
||||||
|
// Clean build directory
|
||||||
|
cleanBuildDirectory := false
|
||||||
|
command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory)
|
||||||
|
|
||||||
appleIdentity := ""
|
appleIdentity := ""
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
||||||
@@ -64,6 +77,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
|
quiet := verbosity == 0
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := clilogger.New(w)
|
||||||
logger.Mute(quiet)
|
logger.Mute(quiet)
|
||||||
@@ -82,29 +97,81 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
||||||
}
|
}
|
||||||
|
|
||||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
|
||||||
logger.Println(task)
|
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
|
||||||
|
|
||||||
// Setup mode
|
// Setup mode
|
||||||
mode := build.Debug
|
mode := build.Debug
|
||||||
if production {
|
if production {
|
||||||
mode = build.Production
|
mode = build.Production
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check platform
|
||||||
|
validPlatformArch := slicer.String([]string{
|
||||||
|
"darwin",
|
||||||
|
"darwin/amd64",
|
||||||
|
"darwin/arm64",
|
||||||
|
"darwin/universal",
|
||||||
|
//"linux/amd64",
|
||||||
|
//"linux/arm-7",
|
||||||
|
//"windows/amd64",
|
||||||
|
})
|
||||||
|
if !validPlatformArch.Contains(platform) {
|
||||||
|
return fmt.Errorf("platform %s is not supported", platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
if compress && platform == "darwin/universal" {
|
||||||
|
println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
||||||
|
compress = false
|
||||||
|
}
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
OutputType: outputType,
|
OutputType: outputType,
|
||||||
Mode: mode,
|
OutputFile: outputFilename,
|
||||||
Pack: pack,
|
CleanBuildDirectory: cleanBuildDirectory,
|
||||||
Platform: platform,
|
Mode: mode,
|
||||||
LDFlags: ldflags,
|
Pack: pack,
|
||||||
Compiler: compilerCommand,
|
LDFlags: ldflags,
|
||||||
KeepAssets: keepAssets,
|
Compiler: compilerCommand,
|
||||||
AppleIdentity: appleIdentity,
|
KeepAssets: keepAssets,
|
||||||
|
AppleIdentity: appleIdentity,
|
||||||
|
Verbosity: verbosity,
|
||||||
|
Compress: compress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate platform and arch
|
||||||
|
platformSplit := strings.Split(platform, "/")
|
||||||
|
buildOptions.Platform = platformSplit[0]
|
||||||
|
buildOptions.Arch = runtime.GOARCH
|
||||||
|
if len(platformSplit) == 2 {
|
||||||
|
buildOptions.Arch = platformSplit[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new tabwriter
|
||||||
|
w := new(tabwriter.Writer)
|
||||||
|
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||||
|
|
||||||
|
buildModeText := "debug"
|
||||||
|
if production {
|
||||||
|
buildModeText = "production"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write out the system information
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||||
|
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
||||||
|
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
||||||
|
fmt.Fprintf(w, "Compiler: \t%s\n", buildOptions.Compiler)
|
||||||
|
fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||||
|
fmt.Fprintf(w, "Build Mode: \t%s\n", buildModeText)
|
||||||
|
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||||
|
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
||||||
|
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
||||||
|
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||||
|
if len(buildOptions.OutputFile) > 0 {
|
||||||
|
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
return doBuild(buildOptions)
|
return doBuild(buildOptions)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.55"
|
var version = "v2.0.0-alpha.62"
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
|||||||
FREE_AND_SET(contextMenu->contextMenuData, contextMenuData);
|
FREE_AND_SET(contextMenu->contextMenuData, contextMenuData);
|
||||||
|
|
||||||
// Grab the content view and show the menu
|
// Grab the content view and show the menu
|
||||||
id contentView = msg(mainWindow, s("contentView"));
|
id contentView = msg_reg(mainWindow, s("contentView"));
|
||||||
|
|
||||||
// Get the triggering event
|
// Get the triggering event
|
||||||
id menuEvent = msg(mainWindow, s("currentEvent"));
|
id menuEvent = msg_reg(mainWindow, s("currentEvent"));
|
||||||
|
|
||||||
if( contextMenu->nsmenu == NULL ) {
|
if( contextMenu->nsmenu == NULL ) {
|
||||||
// GetMenu creates the NSMenu
|
// GetMenu creates the NSMenu
|
||||||
@@ -93,7 +93,7 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show popup
|
// Show popup
|
||||||
msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
((id(*)(id, SEL, id, id, id))objc_msgSend)(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,13 @@
|
|||||||
|
|
||||||
// Macros to make it slightly more sane
|
// Macros to make it slightly more sane
|
||||||
#define msg objc_msgSend
|
#define msg objc_msgSend
|
||||||
|
#define msg_reg ((id(*)(id, SEL))objc_msgSend)
|
||||||
|
#define msg_id ((id(*)(id, SEL, id))objc_msgSend)
|
||||||
|
#define msg_id_id ((id(*)(id, SEL, id, id))objc_msgSend)
|
||||||
|
#define msg_bool ((id(*)(id, SEL, BOOL))objc_msgSend)
|
||||||
|
#define msg_int ((id(*)(id, SEL, int))objc_msgSend)
|
||||||
|
#define msg_uint ((id(*)(id, SEL, unsigned int))objc_msgSend)
|
||||||
|
#define msg_float ((id(*)(id, SEL, float))objc_msgSend)
|
||||||
#define kInternetEventClass 'GURL'
|
#define kInternetEventClass 'GURL'
|
||||||
#define kAEGetURL 'GURL'
|
#define kAEGetURL 'GURL'
|
||||||
#define keyDirectObject '----'
|
#define keyDirectObject '----'
|
||||||
@@ -21,18 +27,27 @@
|
|||||||
#define c(str) (id)objc_getClass(str)
|
#define c(str) (id)objc_getClass(str)
|
||||||
#define s(str) sel_registerName(str)
|
#define s(str) sel_registerName(str)
|
||||||
#define u(str) sel_getUid(str)
|
#define u(str) sel_getUid(str)
|
||||||
#define str(input) msg(c("NSString"), s("stringWithUTF8String:"), input)
|
#define str(input) ((id(*)(id, SEL, const char *))objc_msgSend)(c("NSString"), s("stringWithUTF8String:"), input)
|
||||||
#define strunicode(input) msg(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
#define strunicode(input) ((id(*)(id, SEL, id, unsigned short))objc_msgSend)(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
||||||
#define cstr(input) (const char *)msg(input, s("UTF8String"))
|
#define cstr(input) (const char *)msg_reg(input, s("UTF8String"))
|
||||||
#define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input))
|
#define url(input) msg_id(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||||
#define ALLOC(classname) msg(c(classname), s("alloc"))
|
#define ALLOC(classname) msg_reg(c(classname), s("alloc"))
|
||||||
#define ALLOC_INIT(classname) msg(msg(c(classname), s("alloc")), s("init"))
|
#define ALLOC_INIT(classname) msg_reg(msg_reg(c(classname), s("alloc")), s("init"))
|
||||||
|
|
||||||
|
#if defined (__aarch64__)
|
||||||
|
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("frame"))
|
||||||
|
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("bounds"))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (__x86_64__)
|
||||||
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
|
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
|
||||||
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds"))
|
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds"))
|
||||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))msg)(receiver, s("backingScaleFactor"))
|
#endif
|
||||||
|
|
||||||
|
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))
|
||||||
|
|
||||||
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
|
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
|
||||||
#define MAIN_WINDOW_CALL(str) msg(app->mainWindow, s((str)))
|
#define MAIN_WINDOW_CALL(str) msg_reg(app->mainWindow, s((str)))
|
||||||
|
|
||||||
#define NSBackingStoreBuffered 2
|
#define NSBackingStoreBuffered 2
|
||||||
|
|
||||||
@@ -102,6 +117,8 @@
|
|||||||
#define NSAlertSecondButtonReturn 1001
|
#define NSAlertSecondButtonReturn 1001
|
||||||
#define NSAlertThirdButtonReturn 1002
|
#define NSAlertThirdButtonReturn 1002
|
||||||
|
|
||||||
|
#define BrokenImage "iVBORw0KGgoAAAANSUhEUgAAABAAAAASCAMAAABl5a5YAAABj1BMVEWopan///+koqSWk5P9/v3///////////+AgACMiovz8/PB0fG9z+3i4+WysbGBfX1Erh80rACLiYqBxolEsDhHlDEbqQDDx+CNho7W1tj4+/bw+O3P5Mn4/f/W1tbK6sX////b2dn////////////8/Pz6+vro6Ojj4+P////G1PL////EzNydmp2cmZnd3eDF1PHs8v/o8P/Q3vrS3vfE0vCdmpqZkpr19/3N2vXI1vPH1fOgnqDg6frP3PbCytvHx8irqq6HhIZtuGtjnlZetU1Xs0NWskBNsi7w9v/d6P7w9P3S4Pzr8Pvl7PrY5PrU4PjQ3fjD1Ozo6Om30NjGzNi7ubm34K+UxKmbnaWXlJeUjpSPi4tppF1TtjxSsTf2+f7L2PTr7e3H2+3V7+q+0uXg4OPg4eLR1uG7z+Hg4ODGzODV2N7V1trP5dmxzs65vcfFxMWq0cKxxr+/vr+0s7apxbWaxrCv2qao05+dlp2Uuo2Dn4F8vIB6xnyAoHmAym9zqGpctENLryNFsgoblJpnAAAAKnRSTlP+hP7+5ZRmYgL+/f39/f39/f38/Pz8/Pv69+7j083My8GocnBPTTMWEgjxeITOAAABEklEQVQY0y3KZXuCYBiG4ceYuu7u3nyVAaKOMBBQ7O5Yd3f3fvheDnd9u8/jBkGwNxP6sjOWVQvY/ftrzfT6bd3yEhCnYZqiaYoKiwX/gXkFiHySTcUTLJMsZ9v8nQvgssWYOEKedKpcOO6CUXD5IlGEY5hLUbyDAAZ6HRf1bnkoavOsFQibg+Q4nuNYL+ON5PHD5nBaraRVyxnzGf6BJzUi2QQCQgMyk8tleL7dg1owpJ17D5IkvV100EingeOopPyo6vfAuXF+9hbDTknZCIaUoeK4efKwG4iT6xDewd7imGlid7gGwv37b6Oh9jwaTdOf/Tc1qH7UZVmuP6G5qZfBr9cAGNy4KiDd4tXIs7tS+QO9aUKvPAIKuQAAAABJRU5ErkJggg=="
|
||||||
|
|
||||||
struct Application;
|
struct Application;
|
||||||
int releaseNSObject(void *const context, struct hashmap_element_s *const e);
|
int releaseNSObject(void *const context, struct hashmap_element_s *const e);
|
||||||
void TitlebarAppearsTransparent(struct Application* app);
|
void TitlebarAppearsTransparent(struct Application* app);
|
||||||
@@ -124,4 +141,6 @@ void* lookupStringConstant(id constantName);
|
|||||||
|
|
||||||
void HasURLHandlers(struct Application* app);
|
void HasURLHandlers(struct Application* app);
|
||||||
|
|
||||||
|
id createImageFromBase64Data(const char *data, bool isTemplateImage);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -90,7 +90,7 @@ void DeleteMenu(Menu *menu) {
|
|||||||
|
|
||||||
// Free nsmenu if we have it
|
// Free nsmenu if we have it
|
||||||
if ( menu->menu != NULL ) {
|
if ( menu->menu != NULL ) {
|
||||||
msg(menu->menu, s("release"));
|
msg_reg(menu->menu, s("release"));
|
||||||
}
|
}
|
||||||
|
|
||||||
free(menu);
|
free(menu);
|
||||||
@@ -120,17 +120,17 @@ const char* createMenuClickedMessage(const char *menuItemID, const char *data, e
|
|||||||
|
|
||||||
// Callback for text menu items
|
// Callback for text menu items
|
||||||
void menuItemCallback(id self, SEL cmd, id sender) {
|
void menuItemCallback(id self, SEL cmd, id sender) {
|
||||||
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg(msg(sender, s("representedObject")), s("pointerValue"));
|
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg_reg(msg_reg(sender, s("representedObject")), s("pointerValue"));
|
||||||
const char *message;
|
const char *message;
|
||||||
|
|
||||||
// Update checkbox / radio item
|
// Update checkbox / radio item
|
||||||
if( callbackData->menuItemType == Checkbox) {
|
if( callbackData->menuItemType == Checkbox) {
|
||||||
// Toggle state
|
// Toggle state
|
||||||
bool state = msg(callbackData->menuItem, s("state"));
|
bool state = msg_reg(callbackData->menuItem, s("state"));
|
||||||
msg(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
msg_int(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
||||||
} else if( callbackData->menuItemType == Radio ) {
|
} else if( callbackData->menuItemType == Radio ) {
|
||||||
// Check the menu items' current state
|
// Check the menu items' current state
|
||||||
bool selected = msg(callbackData->menuItem, s("state"));
|
bool selected = (bool)msg_reg(callbackData->menuItem, s("state"));
|
||||||
|
|
||||||
// If it's already selected, exit early
|
// If it's already selected, exit early
|
||||||
if (selected) return;
|
if (selected) return;
|
||||||
@@ -142,13 +142,13 @@ void menuItemCallback(id self, SEL cmd, id sender) {
|
|||||||
id thisMember = members[0];
|
id thisMember = members[0];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while(thisMember != NULL) {
|
while(thisMember != NULL) {
|
||||||
msg(thisMember, s("setState:"), NSControlStateValueOff);
|
msg_int(thisMember, s("setState:"), NSControlStateValueOff);
|
||||||
count = count + 1;
|
count = count + 1;
|
||||||
thisMember = members[count];
|
thisMember = members[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the selected menu item
|
// check the selected menu item
|
||||||
msg(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
msg_int(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *menuID = callbackData->menuID;
|
const char *menuID = callbackData->menuID;
|
||||||
@@ -345,61 +345,61 @@ id processAcceleratorKey(const char *key) {
|
|||||||
|
|
||||||
|
|
||||||
void addSeparator(id menu) {
|
void addSeparator(id menu) {
|
||||||
id item = msg(c("NSMenuItem"), s("separatorItem"));
|
id item = msg_reg(c("NSMenuItem"), s("separatorItem"));
|
||||||
msg(menu, s("addItem:"), item);
|
msg_id(menu, s("addItem:"), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
id createMenuItemNoAutorelease( id title, const char *action, const char *key) {
|
id createMenuItemNoAutorelease( id title, const char *action, const char *key) {
|
||||||
id item = ALLOC("NSMenuItem");
|
id item = ALLOC("NSMenuItem");
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id createMenuItem(id title, const char *action, const char *key) {
|
id createMenuItem(id title, const char *action, const char *key) {
|
||||||
id item = ALLOC("NSMenuItem");
|
id item = ALLOC("NSMenuItem");
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||||
msg(item, s("autorelease"));
|
msg_reg(item, s("autorelease"));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled) {
|
id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled) {
|
||||||
id item = createMenuItem(str(title), action, key);
|
id item = createMenuItem(str(title), action, key);
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg_bool(item, s("setEnabled:"), !disabled);
|
||||||
msg(menu, s("addItem:"), item);
|
msg_id(menu, s("addItem:"), item);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id createMenu(id title) {
|
id createMenu(id title) {
|
||||||
id menu = ALLOC("NSMenu");
|
id menu = ALLOC("NSMenu");
|
||||||
msg(menu, s("initWithTitle:"), title);
|
msg_id(menu, s("initWithTitle:"), title);
|
||||||
msg(menu, s("setAutoenablesItems:"), NO);
|
msg_bool(menu, s("setAutoenablesItems:"), NO);
|
||||||
// msg(menu, s("autorelease"));
|
// msg(menu, s("autorelease"));
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createDefaultAppMenu(id parentMenu) {
|
void createDefaultAppMenu(id parentMenu) {
|
||||||
// App Menu
|
// App Menu
|
||||||
id appName = msg(msg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
id appName = msg_reg(msg_reg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
||||||
id appMenuItem = createMenuItemNoAutorelease(appName, NULL, "");
|
id appMenuItem = createMenuItemNoAutorelease(appName, NULL, "");
|
||||||
id appMenu = createMenu(appName);
|
id appMenu = createMenu(appName);
|
||||||
|
|
||||||
msg(appMenuItem, s("setSubmenu:"), appMenu);
|
msg_id(appMenuItem, s("setSubmenu:"), appMenu);
|
||||||
msg(parentMenu, s("addItem:"), appMenuItem);
|
msg_id(parentMenu, s("addItem:"), appMenuItem);
|
||||||
|
|
||||||
id title = msg(str("Hide "), s("stringByAppendingString:"), appName);
|
id title = msg_id(str("Hide "), s("stringByAppendingString:"), appName);
|
||||||
id item = createMenuItem(title, "hide:", "h");
|
id item = createMenuItem(title, "hide:", "h");
|
||||||
msg(appMenu, s("addItem:"), item);
|
msg_id(appMenu, s("addItem:"), item);
|
||||||
|
|
||||||
id hideOthers = addMenuItem(appMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
id hideOthers = addMenuItem(appMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||||
|
|
||||||
addMenuItem(appMenu, "Show All", "unhideAllApplications:", "", FALSE);
|
addMenuItem(appMenu, "Show All", "unhideAllApplications:", "", FALSE);
|
||||||
|
|
||||||
addSeparator(appMenu);
|
addSeparator(appMenu);
|
||||||
|
|
||||||
title = msg(str("Quit "), s("stringByAppendingString:"), appName);
|
title = msg_id(str("Quit "), s("stringByAppendingString:"), appName);
|
||||||
item = createMenuItem(title, "terminate:", "q");
|
item = createMenuItem(title, "terminate:", "q");
|
||||||
msg(appMenu, s("addItem:"), item);
|
msg_id(appMenu, s("addItem:"), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createDefaultEditMenu(id parentMenu) {
|
void createDefaultEditMenu(id parentMenu) {
|
||||||
@@ -407,8 +407,8 @@ void createDefaultEditMenu(id parentMenu) {
|
|||||||
id editMenuItem = createMenuItemNoAutorelease(str("Edit"), NULL, "");
|
id editMenuItem = createMenuItemNoAutorelease(str("Edit"), NULL, "");
|
||||||
id editMenu = createMenu(str("Edit"));
|
id editMenu = createMenu(str("Edit"));
|
||||||
|
|
||||||
msg(editMenuItem, s("setSubmenu:"), editMenu);
|
msg_id(editMenuItem, s("setSubmenu:"), editMenu);
|
||||||
msg(parentMenu, s("addItem:"), editMenuItem);
|
msg_id(parentMenu, s("addItem:"), editMenuItem);
|
||||||
|
|
||||||
addMenuItem(editMenu, "Undo", "undo:", "z", FALSE);
|
addMenuItem(editMenu, "Undo", "undo:", "z", FALSE);
|
||||||
addMenuItem(editMenu, "Redo", "redo:", "y", FALSE);
|
addMenuItem(editMenu, "Redo", "redo:", "y", FALSE);
|
||||||
@@ -436,7 +436,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
}
|
}
|
||||||
if ( STREQ(roleName, "hideothers")) {
|
if ( STREQ(roleName, "hideothers")) {
|
||||||
id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "unhide")) {
|
if ( STREQ(roleName, "unhide")) {
|
||||||
@@ -473,7 +473,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
}
|
}
|
||||||
if( STREQ(roleName, "pasteandmatchstyle")) {
|
if( STREQ(roleName, "pasteandmatchstyle")) {
|
||||||
id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE);
|
id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE);
|
||||||
msg(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
msg_int(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "selectall")) {
|
if ( STREQ(roleName, "selectall")) {
|
||||||
addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE);
|
addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE);
|
||||||
@@ -540,18 +540,18 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
|||||||
// Create a MenuItemCallbackData
|
// Create a MenuItemCallbackData
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Radio);
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Radio);
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||||
|
|
||||||
id key = processAcceleratorKey(acceleratorkey);
|
id key = processAcceleratorKey(acceleratorkey);
|
||||||
|
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg_bool(item, s("setEnabled:"), !disabled);
|
||||||
msg(item, s("autorelease"));
|
msg_reg(item, s("autorelease"));
|
||||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||||
|
|
||||||
msg(parentmenu, s("addItem:"), item);
|
msg_id(parentmenu, s("addItem:"), item);
|
||||||
return item;
|
return item;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -566,42 +566,37 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
|||||||
// Create a MenuItemCallbackData
|
// Create a MenuItemCallbackData
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Checkbox);
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Checkbox);
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg_bool(item, s("setEnabled:"), !disabled);
|
||||||
msg(item, s("autorelease"));
|
msg_reg(item, s("autorelease"));
|
||||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||||
msg(parentmenu, s("addItem:"), item);
|
msg_id(parentmenu, s("addItem:"), item);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||||
|
|
||||||
// Process Menu Item attributes
|
// Create new Dictionary
|
||||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
|
|
||||||
// Process font
|
|
||||||
id font;
|
|
||||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||||
|
|
||||||
// Check if valid
|
// Use default font
|
||||||
id fontNameAsNSString = str(fontName);
|
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||||
font = msg(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
|
||||||
if( font == NULL ) {
|
// Check user supplied font
|
||||||
bool supportsMonospacedDigitSystemFont = (bool) msg(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
if( STR_HAS_CHARS(fontName) ) {
|
||||||
if( supportsMonospacedDigitSystemFont ) {
|
id fontNameAsNSString = str(fontName);
|
||||||
font = msg(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, NSFontWeightRegular);
|
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||||
} else {
|
if( userFont != NULL ) {
|
||||||
font = msg(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
font = userFont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add font to dictionary
|
// Add font to dictionary
|
||||||
msg(dictionary, s("setObject:forKey:"), font, lookupStringConstant(str("NSFontAttributeName")));
|
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||||
|
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||||
id offset = msg(c("NSNumber"), s("numberWithFloat:"), 0.0);
|
|
||||||
msg(dictionary, s("setObject:forKey:"), offset, lookupStringConstant(str("NSBaselineOffsetAttributeName")));
|
|
||||||
|
|
||||||
// RGBA
|
// RGBA
|
||||||
if( RGBA != NULL && strlen(RGBA) > 0) {
|
if( RGBA != NULL && strlen(RGBA) > 0) {
|
||||||
@@ -611,20 +606,20 @@ id createAttributedString(const char* title, const char* fontName, int fontSize,
|
|||||||
r = g = b = a = 255;
|
r = g = b = a = 255;
|
||||||
int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
id colour = msg(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||||
(float)r / 255.0,
|
(CGFloat)r / (CGFloat)255.0,
|
||||||
(float)g / 255.0,
|
(CGFloat)g / (CGFloat)255.0,
|
||||||
(float)b / 255.0,
|
(CGFloat)b / (CGFloat)255.0,
|
||||||
(float)a / 255.0);
|
(CGFloat)a / (CGFloat)255.0);
|
||||||
msg(dictionary, s("setObject:forKey:"), colour, lookupStringConstant(str("NSForegroundColorAttributeName")));
|
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||||
msg(colour, s("release"));
|
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id attributedString = ALLOC("NSMutableAttributedString");
|
id attributedString = ALLOC("NSMutableAttributedString");
|
||||||
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||||
msg(attributedString, s("autorelease"));
|
msg_reg(attributedString, s("autorelease"));
|
||||||
msg(dictionary, s("release"));
|
msg_reg(dictionary, s("autorelease"));
|
||||||
return attributedString;
|
return attributedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,51 +629,47 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
// Create a MenuItemCallbackData
|
// Create a MenuItemCallbackData
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||||
|
|
||||||
if( !alternate ) {
|
if( !alternate ) {
|
||||||
id key = processAcceleratorKey(acceleratorkey);
|
id key = processAcceleratorKey(acceleratorkey);
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||||
s("menuItemCallback:"), key);
|
s("menuItemCallback:"), key);
|
||||||
} else {
|
} else {
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
if( tooltip != NULL ) {
|
||||||
msg(item, s("setToolTip:"), str(tooltip));
|
msg_id(item, s("setToolTip:"), str(tooltip));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process image
|
// Process image
|
||||||
if( image != NULL && strlen(image) > 0) {
|
if( image != NULL && strlen(image) > 0) {
|
||||||
id data = ALLOC("NSData");
|
id nsimage = createImageFromBase64Data(image, templateImage);
|
||||||
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
msg_id(item, s("setImage:"), nsimage);
|
||||||
id nsimage = ALLOC("NSImage");
|
|
||||||
msg(nsimage, s("initWithData:"), imageData);
|
|
||||||
if( templateImage ) {
|
|
||||||
msg(nsimage, s("setTemplate:"), YES);
|
|
||||||
}
|
|
||||||
msg(item, s("setImage:"), nsimage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
||||||
msg(item, s("setAttributedTitle:"), attributedString);
|
msg_id(item, s("setAttributedTitle:"), attributedString);
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
//msg_id(item, s("setTitle:"), str(title));
|
||||||
msg(item, s("autorelease"));
|
|
||||||
|
msg_bool(item, s("setEnabled:"), !disabled);
|
||||||
|
msg_reg(item, s("autorelease"));
|
||||||
|
|
||||||
// Process modifiers
|
// Process modifiers
|
||||||
if( modifiers != NULL && !alternate) {
|
if( modifiers != NULL && !alternate) {
|
||||||
unsigned long modifierFlags = parseModifiers(modifiers);
|
unsigned long modifierFlags = parseModifiers(modifiers);
|
||||||
msg(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
((id(*)(id, SEL, unsigned long))objc_msgSend)(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// alternate
|
// alternate
|
||||||
if( alternate ) {
|
if( alternate ) {
|
||||||
msg(item, s("setAlternate:"), true);
|
msg_bool(item, s("setAlternate:"), true);
|
||||||
msg(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
msg_int(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
||||||
}
|
}
|
||||||
msg(parentMenu, s("addItem:"), item);
|
msg_id(parentMenu, s("addItem:"), item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -699,38 +690,6 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a submenu
|
|
||||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
|
||||||
if( submenu != NULL ) {
|
|
||||||
// Get the label
|
|
||||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
|
||||||
const char *name = "";
|
|
||||||
if ( menuNameNode != NULL) {
|
|
||||||
name = menuNameNode->string_;
|
|
||||||
}
|
|
||||||
|
|
||||||
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
|
|
||||||
id thisMenu = createMenu(str(name));
|
|
||||||
|
|
||||||
msg(thisMenuItem, s("setSubmenu:"), thisMenu);
|
|
||||||
msg(parentMenu, s("addItem:"), thisMenuItem);
|
|
||||||
|
|
||||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
|
||||||
// If we have no items, just return
|
|
||||||
if ( submenuItems == NULL ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop over submenu items
|
|
||||||
JsonNode *item;
|
|
||||||
json_foreach(item, submenuItems) {
|
|
||||||
// Get item label
|
|
||||||
processMenuItem(menu, thisMenu, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a user menu. Get the common data
|
// This is a user menu. Get the common data
|
||||||
// Get the label
|
// Get the label
|
||||||
const char *label = getJSONString(item, "Label");
|
const char *label = getJSONString(item, "Label");
|
||||||
@@ -796,9 +755,36 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
// Get the Type
|
// Get the Type
|
||||||
JsonNode *type = json_find_member(item, "Type");
|
JsonNode *type = json_find_member(item, "Type");
|
||||||
if( type != NULL ) {
|
if( type != NULL ) {
|
||||||
|
if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) {
|
||||||
|
id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate);
|
||||||
|
|
||||||
if( STREQ(type->string_, "Text")) {
|
// Check if this node has a submenu
|
||||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate);
|
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||||
|
if( submenu != NULL ) {
|
||||||
|
// Get the label
|
||||||
|
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||||
|
const char *name = "";
|
||||||
|
if ( menuNameNode != NULL) {
|
||||||
|
name = menuNameNode->string_;
|
||||||
|
}
|
||||||
|
|
||||||
|
id thisMenu = createMenu(str(name));
|
||||||
|
|
||||||
|
msg_id(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||||
|
|
||||||
|
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||||
|
// If we have no items, just return
|
||||||
|
if ( submenuItems == NULL ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over submenu items
|
||||||
|
JsonNode *item;
|
||||||
|
json_foreach(item, submenuItems) {
|
||||||
|
// Get item label
|
||||||
|
processMenuItem(menu, thisMenu, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if ( STREQ(type->string_, "Separator")) {
|
else if ( STREQ(type->string_, "Separator")) {
|
||||||
addSeparator(parentMenu);
|
addSeparator(parentMenu);
|
||||||
@@ -817,7 +803,6 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
|
|
||||||
processRadioMenuItem(menu, parentMenu, label, menuid, disabled, checked, "");
|
processRadioMenuItem(menu, parentMenu, label, menuid, disabled, checked, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( modifiers != NULL ) {
|
if ( modifiers != NULL ) {
|
||||||
|
|||||||
@@ -70,16 +70,16 @@ void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Update button label
|
// Update button label
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||||
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
if( tooltip != NULL ) {
|
||||||
msg(statusBarButton, s("setToolTip:"), str(tooltip));
|
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
|
||||||
}
|
}
|
||||||
|
|
||||||
msg(statusBarButton, s("setEnabled:"), !disabled);
|
msg_bool(statusBarButton, s("setEnabled:"), !disabled);
|
||||||
|
|
||||||
msg(statusBarButton, s("setAttributedTitle:"), attributedString);
|
msg_id(statusBarButton, s("setAttributedTitle:"), attributedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||||
@@ -89,12 +89,12 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||||
|
|
||||||
// Empty icon means remove it
|
// Empty icon means remove it
|
||||||
if( STREMPTY(trayMenu->icon) ) {
|
if( STREMPTY(trayMenu->icon) ) {
|
||||||
// Remove image
|
// Remove image
|
||||||
msg(statusBarButton, s("setImage:"), NULL);
|
msg_id(statusBarButton, s("setImage:"), NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,18 +102,11 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
|||||||
|
|
||||||
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
||||||
if (trayImage == NULL) {
|
if (trayImage == NULL) {
|
||||||
id data = ALLOC("NSData");
|
trayImage = createImageFromBase64Data(trayMenu->icon, trayMenu->templateImage);
|
||||||
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(trayMenu->icon), 0);
|
|
||||||
trayImage = ALLOC("NSImage");
|
|
||||||
msg(trayImage, s("initWithData:"), imageData);
|
|
||||||
|
|
||||||
if( trayMenu->templateImage ) {
|
|
||||||
msg(trayImage, s("setTemplate:"), YES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
msg_int(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||||
msg(statusBarButton, s("setImage:"), trayImage);
|
msg_id(statusBarButton, s("setImage:"), trayImage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,14 +114,13 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
|
|
||||||
// Create a status bar item if we don't have one
|
// Create a status bar item if we don't have one
|
||||||
if( trayMenu->statusbaritem == NULL ) {
|
if( trayMenu->statusbaritem == NULL ) {
|
||||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||||
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
trayMenu->statusbaritem = ((id(*)(id, SEL, CGFloat))objc_msgSend)(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||||
msg(trayMenu->statusbaritem, s("retain"));
|
msg_reg(trayMenu->statusbaritem, s("retain"));
|
||||||
}
|
}
|
||||||
|
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
msg_uint(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||||
|
|
||||||
// Update the icon if needed
|
// Update the icon if needed
|
||||||
UpdateTrayIcon(trayMenu);
|
UpdateTrayIcon(trayMenu);
|
||||||
|
|
||||||
@@ -140,14 +132,14 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
// Create delegate
|
// Create delegate
|
||||||
id trayMenuDelegate = msg((id)trayMenuDelegateClass, s("new"));
|
id trayMenuDelegate = msg_reg((id)trayMenuDelegateClass, s("new"));
|
||||||
msg(menu, s("setDelegate:"), trayMenuDelegate);
|
msg_id(menu, s("setDelegate:"), trayMenuDelegate);
|
||||||
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
// Create menu delegate
|
// Create menu delegate
|
||||||
trayMenu->delegate = trayMenuDelegate;
|
trayMenu->delegate = trayMenuDelegate;
|
||||||
|
|
||||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
msg_id(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
||||||
@@ -189,14 +181,14 @@ void DeleteTrayMenu(TrayMenu* trayMenu) {
|
|||||||
|
|
||||||
// Free the status item
|
// Free the status item
|
||||||
if ( trayMenu->statusbaritem != NULL ) {
|
if ( trayMenu->statusbaritem != NULL ) {
|
||||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||||
msg(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
msg_id(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
||||||
msg(trayMenu->statusbaritem, s("release"));
|
msg_reg(trayMenu->statusbaritem, s("release"));
|
||||||
trayMenu->statusbaritem = NULL;
|
trayMenu->statusbaritem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( trayMenu->delegate != NULL ) {
|
if ( trayMenu->delegate != NULL ) {
|
||||||
msg(trayMenu->delegate, s("release"));
|
msg_reg(trayMenu->delegate, s("release"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
@@ -228,9 +220,9 @@ void LoadTrayIcons() {
|
|||||||
int length = atoi((const char *)lengthAsString);
|
int length = atoi((const char *)lengthAsString);
|
||||||
|
|
||||||
// Create the icon and add to the hashmap
|
// Create the icon and add to the hashmap
|
||||||
id imageData = msg(c("NSData"), s("dataWithBytes:length:"), data, length);
|
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), data, length);
|
||||||
id trayImage = ALLOC("NSImage");
|
id trayImage = ALLOC("NSImage");
|
||||||
msg(trayImage, s("initWithData:"), imageData);
|
msg_id(trayImage, s("initWithData:"), imageData);
|
||||||
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ typedef struct {
|
|||||||
Menu* menu;
|
Menu* menu;
|
||||||
|
|
||||||
id statusbaritem;
|
id statusbaritem;
|
||||||
int trayIconPosition;
|
unsigned int trayIconPosition;
|
||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
@@ -19,6 +21,10 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
VERBOSE int = 2
|
||||||
|
)
|
||||||
|
|
||||||
// BaseBuilder is the common builder struct
|
// BaseBuilder is the common builder struct
|
||||||
type BaseBuilder struct {
|
type BaseBuilder struct {
|
||||||
filesToDelete slicer.StringSlicer
|
filesToDelete slicer.StringSlicer
|
||||||
@@ -142,11 +148,25 @@ func (b *BaseBuilder) CleanUp() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseBuilder) OutputFilename(options *Options) string {
|
||||||
|
outputFile := options.OutputFile
|
||||||
|
if outputFile == "" {
|
||||||
|
outputFile = b.projectData.OutputFilename
|
||||||
|
}
|
||||||
|
return outputFile
|
||||||
|
}
|
||||||
|
|
||||||
// CompileProject compiles the project
|
// CompileProject compiles the project
|
||||||
func (b *BaseBuilder) CompileProject(options *Options) error {
|
func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||||
|
|
||||||
|
verbose := options.Verbosity == VERBOSE
|
||||||
// Run go mod tidy first
|
// Run go mod tidy first
|
||||||
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
||||||
|
if verbose {
|
||||||
|
println("")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -195,9 +215,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
|
|
||||||
// Get application build directory
|
// Get application build directory
|
||||||
appDir := options.BuildDirectory
|
appDir := options.BuildDirectory
|
||||||
err = cleanBuildDirectory(options)
|
if options.CleanBuildDirectory {
|
||||||
if err != nil {
|
err = cleanBuildDirectory(options)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.LDFlags != "" {
|
if options.LDFlags != "" {
|
||||||
@@ -206,10 +228,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up output filename
|
// Set up output filename
|
||||||
outputFile := options.OutputFile
|
outputFile := b.OutputFilename(options)
|
||||||
if outputFile == "" {
|
|
||||||
outputFile = b.projectData.OutputFilename
|
|
||||||
}
|
|
||||||
compiledBinary := filepath.Join(appDir, outputFile)
|
compiledBinary := filepath.Join(appDir, outputFile)
|
||||||
commands.Add("-o")
|
commands.Add("-o")
|
||||||
commands.Add(compiledBinary)
|
commands.Add(compiledBinary)
|
||||||
@@ -219,7 +238,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
|
|
||||||
// Create the command
|
// Create the command
|
||||||
cmd = exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd = exec.Command(options.Compiler, commands.AsSlice()...)
|
||||||
|
if verbose {
|
||||||
|
println(" Build command:", commands.Join(" "))
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
// Set the directory
|
// Set the directory
|
||||||
cmd.Dir = b.projectData.Path
|
cmd.Dir = b.projectData.Path
|
||||||
|
|
||||||
@@ -241,6 +264,22 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
return v
|
return v
|
||||||
})
|
})
|
||||||
|
|
||||||
|
cmd.Env = upsertEnv(cmd.Env, "GOOS", func(v string) string {
|
||||||
|
return options.Platform
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd.Env = upsertEnv(cmd.Env, "GOARCH", func(v string) string {
|
||||||
|
return options.Arch
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd.Env = upsertEnv(cmd.Env, "CGO_ENABLED", func(v string) string {
|
||||||
|
return "1"
|
||||||
|
})
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println(" Environment:", strings.Join(cmd.Env, " "))
|
||||||
|
}
|
||||||
|
|
||||||
// Setup buffers
|
// Setup buffers
|
||||||
var stdo, stde bytes.Buffer
|
var stdo, stde bytes.Buffer
|
||||||
cmd.Stdout = &stdo
|
cmd.Stdout = &stdo
|
||||||
@@ -254,16 +293,37 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !options.Compress {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have upx installed?
|
||||||
|
if !shell.CommandExists("upx") {
|
||||||
|
println("Warning: Cannot compress binary: upx not found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println(" Compressing with:", "upx", "--best", "--no-color", "--no-progress", options.CompiledBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := exec.Command(options.BuildDirectory, "upx", "--best", "--no-color", "--no-progress", options.CompiledBinary).Output()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Error during compression:")
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
println(output)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NpmInstall runs "npm install" in the given directory
|
// NpmInstall runs "npm install" in the given directory
|
||||||
func (b *BaseBuilder) NpmInstall(sourceDir string) error {
|
func (b *BaseBuilder) NpmInstall(sourceDir string, verbose bool) error {
|
||||||
return b.NpmInstallUsingCommand(sourceDir, "npm install")
|
return b.NpmInstallUsingCommand(sourceDir, "npm install", verbose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NpmInstallUsingCommand runs the given install command in the specified npm project directory
|
// NpmInstallUsingCommand runs the given install command in the specified npm project directory
|
||||||
func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string) error {
|
func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand string, verbose bool) error {
|
||||||
|
|
||||||
packageJSON := filepath.Join(sourceDir, "package.json")
|
packageJSON := filepath.Join(sourceDir, "package.json")
|
||||||
|
|
||||||
@@ -305,7 +365,7 @@ func (b *BaseBuilder) NpmInstallUsingCommand(sourceDir string, installCommand st
|
|||||||
// Split up the InstallCommand and execute it
|
// Split up the InstallCommand and execute it
|
||||||
cmd := strings.Split(installCommand, " ")
|
cmd := strings.Split(installCommand, " ")
|
||||||
stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...)
|
stdout, stderr, err := shell.RunCommand(sourceDir, cmd[0], cmd[1:]...)
|
||||||
if err != nil {
|
if verbose || err != nil {
|
||||||
for _, l := range strings.Split(stdout, "\n") {
|
for _, l := range strings.Split(stdout, "\n") {
|
||||||
fmt.Printf(" %s\n", l)
|
fmt.Printf(" %s\n", l)
|
||||||
}
|
}
|
||||||
@@ -354,31 +414,40 @@ func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verb
|
|||||||
func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||||
|
|
||||||
// TODO: Fix this up from the CLI
|
// TODO: Fix this up from the CLI
|
||||||
verbose := false
|
verbose := b.options.Verbosity == VERBOSE
|
||||||
|
|
||||||
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
||||||
|
|
||||||
// Check there is an 'InstallCommand' provided in wails.json
|
// Check there is an 'InstallCommand' provided in wails.json
|
||||||
if b.projectData.InstallCommand == "" {
|
if b.projectData.InstallCommand == "" {
|
||||||
// No - don't install
|
// No - don't install
|
||||||
outputLogger.Println(" - No Install command. Skipping.")
|
outputLogger.Println("No Install command. Skipping.")
|
||||||
} else {
|
} else {
|
||||||
// Do install if needed
|
// Do install if needed
|
||||||
outputLogger.Println(" - Installing dependencies...")
|
outputLogger.Print("Installing frontend dependencies: ")
|
||||||
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
|
if verbose {
|
||||||
|
outputLogger.Println("")
|
||||||
|
outputLogger.Println(" Install command: '" + b.projectData.InstallCommand + "'")
|
||||||
|
}
|
||||||
|
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand, verbose); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
outputLogger.Println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there is a build command
|
// Check if there is a build command
|
||||||
if b.projectData.BuildCommand == "" {
|
if b.projectData.BuildCommand == "" {
|
||||||
outputLogger.Println(" - No Build command. Skipping.")
|
outputLogger.Println("No Build command. Skipping.")
|
||||||
// No - ignore
|
// No - ignore
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
outputLogger.Println(" - Compiling Frontend Project")
|
outputLogger.Print("Compiling frontend: ")
|
||||||
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
||||||
|
if verbose {
|
||||||
|
outputLogger.Println("")
|
||||||
|
outputLogger.Println(" Build command: '" + strings.Join(cmd, " ") + "'")
|
||||||
|
}
|
||||||
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
||||||
if verbose || err != nil {
|
if verbose || err != nil {
|
||||||
for _, l := range strings.Split(stdout, "\n") {
|
for _, l := range strings.Split(stdout, "\n") {
|
||||||
@@ -388,7 +457,12 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
|||||||
fmt.Printf(" %s\n", l)
|
fmt.Printf(" %s\n", l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
outputLogger.Println("Done.")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractAssets gets the assets from the index.html file
|
// ExtractAssets gets the assets from the index.html file
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/project"
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
)
|
)
|
||||||
@@ -25,20 +28,24 @@ var modeMap = []string{"Debug", "Production"}
|
|||||||
|
|
||||||
// Options contains all the build options as well as the project data
|
// Options contains all the build options as well as the project data
|
||||||
type Options struct {
|
type Options struct {
|
||||||
LDFlags string // Optional flags to pass to linker
|
LDFlags string // Optional flags to pass to linker
|
||||||
Logger *clilogger.CLILogger // All output to the logger
|
Logger *clilogger.CLILogger // All output to the logger
|
||||||
OutputType string // EG: desktop, server....
|
OutputType string // EG: desktop, server....
|
||||||
Mode Mode // release or debug
|
Mode Mode // release or debug
|
||||||
ProjectData *project.Project // The project data
|
ProjectData *project.Project // The project data
|
||||||
Pack bool // Create a package for the app after building
|
Pack bool // Create a package for the app after building
|
||||||
Platform string // The platform to build for
|
Platform string // The platform to build for
|
||||||
Compiler string // The compiler command to use
|
Arch string // The architecture to build for
|
||||||
IgnoreFrontend bool // Indicates if the frontend does not need building
|
Compiler string // The compiler command to use
|
||||||
OutputFile string // Override the output filename
|
IgnoreFrontend bool // Indicates if the frontend does not need building
|
||||||
BuildDirectory string // Directory to use for building the application
|
OutputFile string // Override the output filename
|
||||||
CompiledBinary string // Fully qualified path to the compiled binary
|
BuildDirectory string // Directory to use for building the application
|
||||||
KeepAssets bool // /Keep the generated assets/files
|
CleanBuildDirectory bool // Indicates if the build directory should be cleaned before building
|
||||||
AppleIdentity string
|
CompiledBinary string // Fully qualified path to the compiled binary
|
||||||
|
KeepAssets bool // Keep the generated assets/files
|
||||||
|
Verbosity int // Verbosity level (0 - silent, 1 - default, 2 - verbose)
|
||||||
|
Compress bool // Compress the final binary
|
||||||
|
AppleIdentity string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModeAsString returns the current mode as a string
|
// GetModeAsString returns the current mode as a string
|
||||||
@@ -58,12 +65,6 @@ func Build(options *Options) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check platform
|
|
||||||
validPlatforms := slicer.String([]string{"linux", "darwin", "windows"})
|
|
||||||
if !validPlatforms.Contains(options.Platform) {
|
|
||||||
return "", fmt.Errorf("platform %s is not supported", options.Platform)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load project
|
// Load project
|
||||||
projectData, err := project.Load(cwd)
|
projectData, err := project.Load(cwd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,7 +72,7 @@ func Build(options *Options) (string, error) {
|
|||||||
}
|
}
|
||||||
options.ProjectData = projectData
|
options.ProjectData = projectData
|
||||||
|
|
||||||
// Calculate build dir
|
// Set build directory
|
||||||
options.BuildDirectory = filepath.Join(options.ProjectData.Path, "build", options.Platform, options.OutputType)
|
options.BuildDirectory = filepath.Join(options.ProjectData.Path, "build", options.Platform, options.OutputType)
|
||||||
|
|
||||||
// Save the project type
|
// Save the project type
|
||||||
@@ -107,7 +108,6 @@ func Build(options *Options) (string, error) {
|
|||||||
// return "", err
|
// return "", err
|
||||||
// }
|
// }
|
||||||
if !options.IgnoreFrontend {
|
if !options.IgnoreFrontend {
|
||||||
outputLogger.Println(" - Building Project Frontend")
|
|
||||||
err = builder.BuildFrontend(outputLogger)
|
err = builder.BuildFrontend(outputLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -115,30 +115,74 @@ func Build(options *Options) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build the base assets
|
// Build the base assets
|
||||||
outputLogger.Println(" - Compiling Assets")
|
|
||||||
err = builder.BuildAssets(options)
|
err = builder.BuildAssets(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the application
|
// Compile the application
|
||||||
outputLogger.Print(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
outputLogger.Print("Compiling application: ")
|
||||||
err = builder.CompileProject(options)
|
|
||||||
if err != nil {
|
if options.Platform == "darwin" && options.Arch == "universal" {
|
||||||
return "", err
|
outputFile := builder.OutputFilename(options)
|
||||||
|
amd64Filename := outputFile + "-amd64"
|
||||||
|
arm64Filename := outputFile + "-arm64"
|
||||||
|
|
||||||
|
// Build amd64 first
|
||||||
|
options.Arch = "amd64"
|
||||||
|
options.OutputFile = amd64Filename
|
||||||
|
options.CleanBuildDirectory = false
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
println()
|
||||||
|
println(" Building AMD64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||||
|
}
|
||||||
|
err = builder.CompileProject(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Build arm64
|
||||||
|
options.Arch = "arm64"
|
||||||
|
options.OutputFile = arm64Filename
|
||||||
|
options.CleanBuildDirectory = false
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
println(" Building ARM64 Target:", filepath.Join(options.BuildDirectory, options.OutputFile))
|
||||||
|
}
|
||||||
|
err = builder.CompileProject(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Run lipo
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
println(" Running lipo: ", "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||||
|
}
|
||||||
|
_, stderr, err := shell.RunCommand(options.BuildDirectory, "lipo", "-create", "-output", outputFile, amd64Filename, arm64Filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%s - %s", err.Error(), stderr)
|
||||||
|
}
|
||||||
|
// Remove temp binaries
|
||||||
|
fs.DeleteFile(filepath.Join(options.BuildDirectory, amd64Filename))
|
||||||
|
fs.DeleteFile(filepath.Join(options.BuildDirectory, arm64Filename))
|
||||||
|
projectData.OutputFilename = outputFile
|
||||||
|
options.CompiledBinary = filepath.Join(options.BuildDirectory, outputFile)
|
||||||
|
} else {
|
||||||
|
err = builder.CompileProject(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
outputLogger.Println("done.")
|
outputLogger.Println("Done.")
|
||||||
|
|
||||||
// Do we need to pack the app?
|
// Do we need to pack the app?
|
||||||
if options.Pack {
|
if options.Pack {
|
||||||
|
|
||||||
outputLogger.Println(" - Packaging Application")
|
outputLogger.Print("Packaging application: ")
|
||||||
|
|
||||||
// TODO: Allow cross platform build
|
// TODO: Allow cross platform build
|
||||||
err = packageProject(options, runtime.GOOS)
|
err = packageProject(options, runtime.GOOS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
outputLogger.Println("Done.")
|
||||||
}
|
}
|
||||||
|
|
||||||
return projectData.OutputFilename, nil
|
return projectData.OutputFilename, nil
|
||||||
|
|||||||
@@ -12,5 +12,6 @@ type Builder interface {
|
|||||||
BuildFrontend(*clilogger.CLILogger) error
|
BuildFrontend(*clilogger.CLILogger) error
|
||||||
BuildRuntime(*Options) error
|
BuildRuntime(*Options) error
|
||||||
CompileProject(*Options) error
|
CompileProject(*Options) error
|
||||||
|
OutputFilename(*Options) string
|
||||||
CleanUp()
|
CleanUp()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
outputLogger := options.Logger
|
outputLogger := options.Logger
|
||||||
outputLogger.Print(" - Embedding Assets...")
|
outputLogger.Print("Building assets: ")
|
||||||
|
|
||||||
// Get target asset directory
|
// Get target asset directory
|
||||||
assetDir, err := fs.RelativeToCwd("build")
|
assetDir, err := fs.RelativeToCwd("build")
|
||||||
@@ -96,7 +96,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
outputLogger.Println("done.")
|
outputLogger.Println("Done.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -125,11 +125,11 @@ func (d *DesktopBuilder) BuildRuntime(options *Options) error {
|
|||||||
|
|
||||||
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
||||||
|
|
||||||
if err := d.NpmInstall(sourceDir); err != nil {
|
if err := d.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
outputLogger.Print(" - Embedding Runtime...")
|
outputLogger.Print("Embedding Runtime: ")
|
||||||
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
||||||
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
|
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
|
|||||||
func (s *ServerBuilder) BuildRuntime(options *Options) error {
|
func (s *ServerBuilder) BuildRuntime(options *Options) error {
|
||||||
|
|
||||||
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
sourceDir := fs.RelativePath("../../../internal/runtime/js")
|
||||||
if err := s.NpmInstall(sourceDir); err != nil {
|
if err := s.NpmInstall(sourceDir, options.Verbosity == VERBOSE); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
v2/pkg/mac/mac_darwin.go
Normal file
60
v2/pkg/mac/mac_darwin.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Package mac provides MacOS related utility functions for Wails applications
|
||||||
|
package mac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartAtLogin will either add or remove this application to/from the login
|
||||||
|
// items, depending on the given boolean flag. The limitation is that the
|
||||||
|
// currently running app must be in an app bundle.
|
||||||
|
func StartAtLogin(enabled bool) error {
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Error running os.Executable:")
|
||||||
|
}
|
||||||
|
binName := filepath.Base(exe)
|
||||||
|
if !strings.HasSuffix(exe, "/Contents/MacOS/"+binName) {
|
||||||
|
return fmt.Errorf("app needs to be running as package.app file to start at login")
|
||||||
|
}
|
||||||
|
appPath := strings.TrimSuffix(exe, "/Contents/MacOS/"+binName)
|
||||||
|
var command string
|
||||||
|
if enabled {
|
||||||
|
command = fmt.Sprintf("tell application \"System Events\" to make login item at end with properties {name: \"%s\",path:\"%s\", hidden:false}", binName, appPath)
|
||||||
|
} else {
|
||||||
|
command = fmt.Sprintf("tell application \"System Events\" to delete login item \"%s\"", binName)
|
||||||
|
}
|
||||||
|
_, stde, err := shell.RunCommand("/tmp", "osascript", "-e", command)
|
||||||
|
if err != nil {
|
||||||
|
errors.Wrap(err, stde)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartsAtLogin will indicate if this application is in the login
|
||||||
|
// items. The limitation is that the currently running app must be
|
||||||
|
// in an app bundle.
|
||||||
|
func StartsAtLogin() (bool, error) {
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
binName := filepath.Base(exe)
|
||||||
|
if !strings.HasSuffix(exe, "/Contents/MacOS/"+binName) {
|
||||||
|
return false, fmt.Errorf("app needs to be running as package.app file to start at login")
|
||||||
|
}
|
||||||
|
results, stde, err := shell.RunCommand("/tmp", "osascript", "-e", `tell application "System Events" to get the name of every login item`)
|
||||||
|
if err != nil {
|
||||||
|
return false, errors.Wrap(err, stde)
|
||||||
|
}
|
||||||
|
results = strings.TrimSpace(results)
|
||||||
|
startupApps := slicer.String(strings.Split(results, ", "))
|
||||||
|
return startupApps.Contains(binName), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user