mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 04:05:12 -07:00
Compare commits
36 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e01710412 | ||
|
|
1b0193161c | ||
|
|
1b377fb575 | ||
|
|
1203ae64b8 | ||
|
|
26ed8002b9 | ||
|
|
cf23bffc67 | ||
|
|
d70f6fffe7 | ||
|
|
54c99fc386 | ||
|
|
86c1ea5e6a | ||
|
|
b394c1914c | ||
|
|
91c2ddf155 | ||
|
|
712ad96d2a | ||
|
|
86b4a4f2f5 | ||
|
|
4b9786abc9 | ||
|
|
fd96ebc050 | ||
|
|
939e0f5975 | ||
|
|
6a7a288a0f | ||
|
|
0564d0aa98 | ||
|
|
3a136a73ca | ||
|
|
50c219307f | ||
|
|
de3038b302 | ||
|
|
6eb4b0a419 | ||
|
|
41d2158375 | ||
|
|
5d7f57e80b | ||
|
|
4ce3e1d1bf | ||
|
|
e5f2746810 | ||
|
|
92ebf506dd | ||
|
|
9ab06152c5 | ||
|
|
bf36b6a59d | ||
|
|
4b9f6c4fb1 | ||
|
|
b1a42c8dea | ||
|
|
cbd98b5a1a | ||
|
|
c8e0aea69c | ||
|
|
7c0b236eb0 | ||
|
|
16debbd109 | ||
|
|
39bfa5d910 |
@@ -40,4 +40,5 @@ Wails is what it is because of the time and effort given by these great people.
|
|||||||
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
||||||
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
||||||
* [misitebao](https://github.com/misitebao)
|
* [misitebao](https://github.com/misitebao)
|
||||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||||
|
* [Amaury Tobias Quiroz](https://github.com/amaury-tobias)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
echo "**** Checking if Wails passes unit tests ****"
|
echo "**** Checking if Wails passes unit tests ****"
|
||||||
if ! go test ./...
|
if ! go test ./lib/... ./runtime/... ./cmd/...
|
||||||
then
|
then
|
||||||
echo ""
|
echo ""
|
||||||
echo "ERROR: Unit tests failed!"
|
echo "ERROR: Unit tests failed!"
|
||||||
|
|||||||
@@ -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.52"
|
var version = "v2.0.0-alpha.63"
|
||||||
|
|||||||
@@ -5,10 +5,6 @@
|
|||||||
#ifndef COMMON_H
|
#ifndef COMMON_H
|
||||||
#define COMMON_H
|
#define COMMON_H
|
||||||
|
|
||||||
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
|
||||||
#include <objc/objc-runtime.h>
|
|
||||||
#include <CoreGraphics/CoreGraphics.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|||||||
@@ -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
|
||||||
78
v2/internal/ffenestri/ffenestri_windows.c
Normal file
78
v2/internal/ffenestri/ffenestri_windows.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
|
||||||
|
typedef struct {
|
||||||
|
} Application;
|
||||||
|
|
||||||
|
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||||
|
}
|
||||||
|
void SetMinWindowSize(struct Application* app, int minWidth, int minHeight) {
|
||||||
|
}
|
||||||
|
void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) {
|
||||||
|
}
|
||||||
|
void Run(struct Application* app, int argc, char **argv) {
|
||||||
|
}
|
||||||
|
void DestroyApplication(struct Application* app) {
|
||||||
|
}
|
||||||
|
void SetDebug(struct Application* app, int flag) {
|
||||||
|
}
|
||||||
|
void SetBindings(struct Application* app, const char *bindings) {
|
||||||
|
}
|
||||||
|
void ExecJS(struct Application* app, const char *script) {
|
||||||
|
}
|
||||||
|
void Hide(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Show(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Center(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Maximise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Unmaximise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void ToggleMaximise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Minimise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void Unminimise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void ToggleMinimise(struct Application* app) {
|
||||||
|
}
|
||||||
|
void SetColour(struct Application* app, int red, int green, int blue, int alpha) {
|
||||||
|
}
|
||||||
|
void SetSize(struct Application* app, int width, int height) {
|
||||||
|
}
|
||||||
|
void SetPosition(struct Application* app, int x, int y) {
|
||||||
|
}
|
||||||
|
void Quit(struct Application* app) {
|
||||||
|
}
|
||||||
|
void SetTitle(struct Application* app, const char *title) {
|
||||||
|
}
|
||||||
|
void Fullscreen(struct Application* app) {
|
||||||
|
}
|
||||||
|
void UnFullscreen(struct Application* app) {
|
||||||
|
}
|
||||||
|
void ToggleFullscreen(struct Application* app) {
|
||||||
|
}
|
||||||
|
void DisableFrame(struct Application* app) {
|
||||||
|
}
|
||||||
|
void OpenDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {
|
||||||
|
}
|
||||||
|
void SaveDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
||||||
|
}
|
||||||
|
void MessageDialog(struct Application* app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
||||||
|
}
|
||||||
|
void DarkModeEnabled(struct Application* app, char *callbackID) {
|
||||||
|
}
|
||||||
|
void SetApplicationMenu(struct Application* app, const char *applicationMenuJSON) {
|
||||||
|
}
|
||||||
|
void AddTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
||||||
|
}
|
||||||
|
void SetTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
||||||
|
}
|
||||||
|
void DeleteTrayMenuByID(struct Application* app, const char *id) {
|
||||||
|
}
|
||||||
|
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
||||||
|
}
|
||||||
|
void AddContextMenu(struct Application* app, char *contextMenuJSON) {
|
||||||
|
}
|
||||||
|
void UpdateContextMenu(struct Application* app, char *contextMenuJSON) {
|
||||||
|
}
|
||||||
14
v2/internal/ffenestri/ffenestri_windows.go
Normal file
14
v2/internal/ffenestri/ffenestri_windows.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package ffenestri
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#include "ffenestri.h"
|
||||||
|
#include "ffenestri_windows.h"
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
func (a *Application) processPlatformSettings() error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
5
v2/internal/ffenestri/ffenestri_windows.h
Normal file
5
v2/internal/ffenestri/ffenestri_windows.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
#ifndef _FFENESTRI_WINDOWS_
|
||||||
|
#define _FFENESTRI_WINDOWS_
|
||||||
|
|
||||||
|
#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;
|
||||||
@@ -189,6 +189,9 @@ id processAcceleratorKey(const char *key) {
|
|||||||
if( STREQ(key, "return") ) {
|
if( STREQ(key, "return") ) {
|
||||||
return strunicode(0x000d);
|
return strunicode(0x000d);
|
||||||
}
|
}
|
||||||
|
if( STREQ(key, "enter") ) {
|
||||||
|
return strunicode(0x000d);
|
||||||
|
}
|
||||||
if( STREQ(key, "escape") ) {
|
if( STREQ(key, "escape") ) {
|
||||||
return strunicode(0x001b);
|
return strunicode(0x001b);
|
||||||
}
|
}
|
||||||
@@ -345,61 +348,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 +410,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 +439,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 +476,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 +543,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,46 +569,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);
|
||||||
id fontsOnSystem = msg(msg(c("NSFontManager"), s("sharedFontManager")), s("availableFonts"));
|
|
||||||
bool valid = msg(fontsOnSystem, s("containsObject:"), fontNameAsNSString);
|
// Check user supplied font
|
||||||
if( valid ) {
|
if( STR_HAS_CHARS(fontName) ) {
|
||||||
font = msg(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
id fontNameAsNSString = str(fontName);
|
||||||
} else {
|
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||||
bool supportsMonospacedDigitSystemFont = (bool) msg(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
if( userFont != NULL ) {
|
||||||
if( supportsMonospacedDigitSystemFont ) {
|
font = userFont;
|
||||||
font = msg(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, NSFontWeightRegular);
|
|
||||||
} else {
|
|
||||||
font = msg(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
// Add offset to dictionary
|
|
||||||
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) {
|
||||||
@@ -615,20 +609,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,51 +632,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;
|
||||||
}
|
}
|
||||||
@@ -703,38 +693,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");
|
||||||
@@ -767,7 +725,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
bool templateImage = false;
|
bool templateImage = false;
|
||||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||||
|
|
||||||
int fontSize = 13;
|
int fontSize = 0;
|
||||||
getJSONInt(item, "FontSize", &fontSize);
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
@@ -800,9 +758,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);
|
||||||
@@ -821,7 +806,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 ) {
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
|||||||
result->fontName = getJSONString(processedJSON, "FontName");
|
result->fontName = getJSONString(processedJSON, "FontName");
|
||||||
result->RGBA = getJSONString(processedJSON, "RGBA");
|
result->RGBA = getJSONString(processedJSON, "RGBA");
|
||||||
getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage);
|
getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage);
|
||||||
result->fontSize = 13;
|
result->fontSize = 0;
|
||||||
getJSONInt(processedJSON, "FontSize", &result->fontSize);
|
getJSONInt(processedJSON, "FontSize", &result->fontSize);
|
||||||
|
result->tooltip = NULL;
|
||||||
|
result->tooltip = getJSONString(processedJSON, "Tooltip");
|
||||||
|
result->disabled = false;
|
||||||
|
getJSONBool(processedJSON, "Disabled", &result->disabled);
|
||||||
|
|
||||||
// Create the menu
|
// Create the menu
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||||
@@ -59,16 +63,23 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA) {
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled) {
|
||||||
|
|
||||||
// Exit early if NULL
|
// Exit early if NULL
|
||||||
if( trayMenu->label == NULL ) {
|
if( trayMenu->label == NULL ) {
|
||||||
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);
|
||||||
msg(statusBarButton, s("setAttributedTitle:"), attributedString);
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_bool(statusBarButton, s("setEnabled:"), !disabled);
|
||||||
|
|
||||||
|
msg_id(statusBarButton, s("setAttributedTitle:"), attributedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||||
@@ -78,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,33 +114,32 @@ 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);
|
||||||
|
|
||||||
// Update the label if needed
|
// Update the label if needed
|
||||||
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA);
|
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled);
|
||||||
|
|
||||||
// Update the menu
|
// Update the menu
|
||||||
id menu = GetMenu(trayMenu->menu);
|
id menu = GetMenu(trayMenu->menu);
|
||||||
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
|
||||||
@@ -178,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
|
||||||
@@ -217,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,16 +13,19 @@ typedef struct {
|
|||||||
const char *label;
|
const char *label;
|
||||||
const char *icon;
|
const char *icon;
|
||||||
const char *ID;
|
const char *ID;
|
||||||
|
const char *tooltip;
|
||||||
|
|
||||||
bool templateImage;
|
bool templateImage;
|
||||||
const char *fontName;
|
const char *fontName;
|
||||||
int fontSize;
|
int fontSize;
|
||||||
const char *RGBA;
|
const char *RGBA;
|
||||||
|
|
||||||
|
bool disabled;
|
||||||
|
|
||||||
Menu* menu;
|
Menu* menu;
|
||||||
|
|
||||||
id statusbaritem;
|
id statusbaritem;
|
||||||
int trayIconPosition;
|
unsigned int trayIconPosition;
|
||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
|||||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA);
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled);
|
||||||
|
|
||||||
void LoadTrayIcons();
|
void LoadTrayIcons();
|
||||||
void UnloadTrayIcons();
|
void UnloadTrayIcons();
|
||||||
|
|||||||
@@ -121,10 +121,14 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
|
|
||||||
const char *fontName = getJSONString(parsedUpdate, "FontName");
|
const char *fontName = getJSONString(parsedUpdate, "FontName");
|
||||||
const char *RGBA = getJSONString(parsedUpdate, "RGBA");
|
const char *RGBA = getJSONString(parsedUpdate, "RGBA");
|
||||||
int fontSize = 13;
|
int fontSize = 0;
|
||||||
getJSONInt(parsedUpdate, "FontSize", &fontSize);
|
getJSONInt(parsedUpdate, "FontSize", &fontSize);
|
||||||
|
const char *tooltip = getJSONString(parsedUpdate, "Tooltip");
|
||||||
|
bool disabled = false;
|
||||||
|
getJSONBool(parsedUpdate, "Disabled", &disabled);
|
||||||
|
|
||||||
|
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled);
|
||||||
|
|
||||||
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,9 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
if attr.Key == "as" && attr.Val == "script" {
|
if attr.Key == "as" && attr.Val == "script" {
|
||||||
asset.Type = AssetTypes.JS
|
asset.Type = AssetTypes.JS
|
||||||
}
|
}
|
||||||
|
if attr.Key == "rel" && attr.Val == "modulepreload" {
|
||||||
|
asset.Type = AssetTypes.JS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we don't include duplicates
|
// Ensure we don't include duplicates
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ type TrayMenu struct {
|
|||||||
Label string
|
Label string
|
||||||
FontSize int
|
FontSize int
|
||||||
FontName string
|
FontName string
|
||||||
|
Disabled bool
|
||||||
|
Tooltip string `json:",omitempty"`
|
||||||
Image string
|
Image string
|
||||||
MacTemplateImage bool
|
MacTemplateImage bool
|
||||||
RGBA string
|
RGBA string
|
||||||
@@ -50,6 +52,8 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
|||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
FontName: trayMenu.FontName,
|
FontName: trayMenu.FontName,
|
||||||
FontSize: trayMenu.FontSize,
|
FontSize: trayMenu.FontSize,
|
||||||
|
Disabled: trayMenu.Disabled,
|
||||||
|
Tooltip: trayMenu.Tooltip,
|
||||||
Image: trayMenu.Image,
|
Image: trayMenu.Image,
|
||||||
MacTemplateImage: trayMenu.MacTemplateImage,
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
menu: trayMenu.Menu,
|
menu: trayMenu.Menu,
|
||||||
@@ -145,13 +149,27 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LabelUpdate struct {
|
type LabelUpdate struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string
|
||||||
|
FontName string
|
||||||
|
FontSize int
|
||||||
|
RGBA string
|
||||||
|
Disabled bool
|
||||||
|
Tooltip string
|
||||||
|
Image string
|
||||||
|
MacTemplateImage bool
|
||||||
}
|
}
|
||||||
|
|
||||||
update := &LabelUpdate{
|
update := &LabelUpdate{
|
||||||
ID: trayID,
|
ID: trayID,
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
|
FontName: trayMenu.FontName,
|
||||||
|
FontSize: trayMenu.FontSize,
|
||||||
|
Disabled: trayMenu.Disabled,
|
||||||
|
Tooltip: trayMenu.Tooltip,
|
||||||
|
Image: trayMenu.Image,
|
||||||
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
|
RGBA: trayMenu.RGBA,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(update)
|
data, err := json.Marshal(update)
|
||||||
|
|||||||
@@ -17,13 +17,14 @@ func platformInfo() (*OS, error) {
|
|||||||
// Ignore errors as it isn't a showstopper
|
// Ignore errors as it isn't a showstopper
|
||||||
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||||
|
|
||||||
defer key.Close()
|
|
||||||
|
|
||||||
fmt.Printf("%+v\n", key)
|
|
||||||
|
|
||||||
// Ignore errors as it isn't a showstopper
|
|
||||||
productName, _, _ := key.GetStringValue("ProductName")
|
productName, _, _ := key.GetStringValue("ProductName")
|
||||||
fmt.Println(productName)
|
currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber")
|
||||||
|
displayVersion, _, _ := key.GetStringValue("DisplayVersion")
|
||||||
|
releaseId, _, _ := key.GetStringValue("ReleaseId")
|
||||||
|
|
||||||
return nil, nil
|
result.Name = productName
|
||||||
|
result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild)
|
||||||
|
result.ID = displayVersion
|
||||||
|
|
||||||
|
return &result, key.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,5 @@ func (i *Info) discover() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.OS = osinfo
|
i.OS = osinfo
|
||||||
|
|
||||||
// dll := syscall.MustLoadDLL("kernel32.dll")
|
|
||||||
// p := dll.MustFindProc("GetVersion")
|
|
||||||
// v, _, _ := p.Call()
|
|
||||||
// fmt.Printf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"})
|
|
||||||
if !validPlatforms.Contains(options.Platform) {
|
|
||||||
return "", fmt.Errorf("platform %s 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
|
||||||
|
|||||||
@@ -49,4 +49,4 @@ Example:
|
|||||||
|
|
||||||
## Mac
|
## Mac
|
||||||
|
|
||||||
The `mac` directory holds files specific to Mac builds, such as `info.plist`. These may be edited and used as part of the build.
|
The `mac` directory holds files specific to Mac builds, such as `Info.plist`. These may be edited and used as part of the build.
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func packageApplication(options *Options) error {
|
|||||||
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate info.plist
|
// Generate Info.plist
|
||||||
err = processPList(options, contentsDirectory)
|
err = processPList(options, contentsDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -68,7 +68,7 @@ func packageApplication(options *Options) error {
|
|||||||
func processPList(options *Options, contentsDirectory string) error {
|
func processPList(options *Options, contentsDirectory string) error {
|
||||||
|
|
||||||
// Check if plist already exists in project dir
|
// Check if plist already exists in project dir
|
||||||
plistFile := filepath.Join(options.ProjectData.AssetsDir, "mac", "info.plist")
|
plistFile := filepath.Join(options.ProjectData.AssetsDir, "mac", "Info.plist")
|
||||||
|
|
||||||
// If the file doesn't exist, generate it
|
// If the file doesn't exist, generate it
|
||||||
if !fs.FileExists(plistFile) {
|
if !fs.FileExists(plistFile) {
|
||||||
@@ -79,7 +79,7 @@ func processPList(options *Options, contentsDirectory string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy it to the contents directory
|
// Copy it to the contents directory
|
||||||
targetFile := filepath.Join(contentsDirectory, "info.plist")
|
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
||||||
return fs.CopyFile(plistFile, targetFile)
|
return fs.CopyFile(plistFile, targetFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +88,11 @@ func generateDefaultPlist(options *Options, targetPlistFile string) error {
|
|||||||
exe := defaultString(options.OutputFile, name)
|
exe := defaultString(options.OutputFile, name)
|
||||||
version := "1.0.0"
|
version := "1.0.0"
|
||||||
author := defaultString(options.ProjectData.Author.Name, "Anonymous")
|
author := defaultString(options.ProjectData.Author.Name, "Anonymous")
|
||||||
packageID := strings.Join([]string{"wails", name, version}, ".")
|
packageID := strings.Join([]string{"wails", name}, ".")
|
||||||
plistData := newPlistData(name, exe, packageID, version, author)
|
plistData := newPlistData(name, exe, packageID, version, author)
|
||||||
|
|
||||||
tmpl := template.New("infoPlist")
|
tmpl := template.New("infoPlist")
|
||||||
plistTemplate := fs.RelativePath("./internal/packager/darwin/info.plist")
|
plistTemplate := fs.RelativePath("./internal/packager/darwin/Info.plist")
|
||||||
infoPlist, err := ioutil.ReadFile(plistTemplate)
|
infoPlist, err := ioutil.ReadFile(plistTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Cannot open plist template")
|
return errors.Wrap(err, "Cannot open plist template")
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var namedKeys = slicer.String([]string{"backspace", "tab", "return", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"})
|
var namedKeys = slicer.String([]string{"backspace", "tab", "return", "enter", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"})
|
||||||
|
|
||||||
func parseKey(key string) (string, bool) {
|
func parseKey(key string) (string, bool) {
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,15 @@ type TrayMenu struct {
|
|||||||
FontSize int
|
FontSize int
|
||||||
FontName string
|
FontName string
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
Tooltip string
|
||||||
|
|
||||||
|
// Callback function when menu clicked
|
||||||
|
//Click Callback `json:"-"`
|
||||||
|
|
||||||
|
// Disabled makes the item unselectable
|
||||||
|
Disabled bool
|
||||||
|
|
||||||
// Menu is the initial menu we wish to use for the tray
|
// Menu is the initial menu we wish to use for the tray
|
||||||
Menu *Menu
|
Menu *Menu
|
||||||
|
|
||||||
|
|||||||
2
v2/test/kitchensink/.gitignore
vendored
2
v2/test/kitchensink/.gitignore
vendored
@@ -1 +1 @@
|
|||||||
info.plist
|
Info.plist
|
||||||
Reference in New Issue
Block a user