mirror of
https://github.com/taigrr/wails.git
synced 2026-04-14 10:50:53 -07:00
Compare commits
90 Commits
feature/v2
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0564d0aa98 | ||
|
|
3a136a73ca | ||
|
|
50c219307f | ||
|
|
de3038b302 | ||
|
|
6eb4b0a419 | ||
|
|
41d2158375 | ||
|
|
5d7f57e80b | ||
|
|
4ce3e1d1bf | ||
|
|
e5f2746810 | ||
|
|
92ebf506dd | ||
|
|
9ab06152c5 | ||
|
|
bf36b6a59d | ||
|
|
4b9f6c4fb1 | ||
|
|
b1a42c8dea | ||
|
|
cbd98b5a1a | ||
|
|
c8e0aea69c | ||
|
|
7c0b236eb0 | ||
|
|
16debbd109 | ||
|
|
39bfa5d910 | ||
|
|
6424579a9e | ||
|
|
a962ae6f63 | ||
|
|
c7dee158ba | ||
|
|
237d25089d | ||
|
|
265328d648 | ||
|
|
6f40e85a6e | ||
|
|
96d8509da3 | ||
|
|
598445ab0f | ||
|
|
24788e2fd3 | ||
|
|
bbf4dde43f | ||
|
|
0afd27ab45 | ||
|
|
d498423ec2 | ||
|
|
66ce84973c | ||
|
|
55e6a0f312 | ||
|
|
81e83fdf18 | ||
|
|
f9b79d24f8 | ||
|
|
0599a47bfe | ||
|
|
817c55d318 | ||
|
|
14146c8c0c | ||
|
|
18adac20d4 | ||
|
|
eb4bff89da | ||
|
|
c66dc777f3 | ||
|
|
9003462457 | ||
|
|
e124f0a220 | ||
|
|
c6d3f57712 | ||
|
|
b4c669ff86 | ||
|
|
2d1b2c0947 | ||
|
|
4a0c5aa785 | ||
|
|
f48d7f8f60 | ||
|
|
651f24f641 | ||
|
|
8fd77148ca | ||
|
|
0dc0762fdf | ||
|
|
1a92550709 | ||
|
|
bffc15bc14 | ||
|
|
198d206c46 | ||
|
|
bb8e848ef6 | ||
|
|
bac3e9e5c1 | ||
|
|
bc5eddeb66 | ||
|
|
8e7258d812 | ||
|
|
7118762cec | ||
|
|
6af92cf0a4 | ||
|
|
1bb91634f7 | ||
|
|
f71ce7913f | ||
|
|
53db687a26 | ||
|
|
13939d3d6b | ||
|
|
552c6b8711 | ||
|
|
feee2b3db2 | ||
|
|
9889c2bdbb | ||
|
|
2432fccf71 | ||
|
|
70510fd180 | ||
|
|
17c6201469 | ||
|
|
0f209c8900 | ||
|
|
cbf043585c | ||
|
|
5ae621ceaa | ||
|
|
1231b59443 | ||
|
|
b18d4fbf41 | ||
|
|
9ec5605e63 | ||
|
|
98a4de8878 | ||
|
|
5fe709f558 | ||
|
|
5231a6893b | ||
|
|
74f3ce990f | ||
|
|
998a913853 | ||
|
|
964844835c | ||
|
|
4e152bb849 | ||
|
|
51133d098c | ||
|
|
d4191e7d1b | ||
|
|
9c273bc745 | ||
|
|
c6f6ad6beb | ||
|
|
4362a14459 | ||
|
|
0080e9e311 | ||
|
|
83d9297cac |
@@ -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
|
||||||
@@ -41,24 +43,39 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
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 := ""
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -72,9 +89,10 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
}
|
}
|
||||||
|
|
||||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
// Ensure package is used with apple identity
|
||||||
logger.Println(task)
|
if appleIdentity != "" && pack == false {
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
||||||
|
}
|
||||||
|
|
||||||
// Setup mode
|
// Setup mode
|
||||||
mode := build.Debug
|
mode := build.Debug
|
||||||
@@ -82,18 +100,68 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
|
KeepAssets: keepAssets,
|
||||||
|
AppleIdentity: appleIdentity,
|
||||||
|
Verbosity: verbosity,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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, "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, "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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
@@ -20,6 +21,21 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func LogGreen(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.Green(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogRed(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.Red(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogDarkYellow(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.DarkYellow(text))
|
||||||
|
}
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
// AddSubcommand adds the `dev` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||||
|
|
||||||
@@ -37,6 +53,13 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
extensions := "go"
|
extensions := "go"
|
||||||
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||||
|
|
||||||
|
// extensions to trigger rebuilds
|
||||||
|
showWarnings := false
|
||||||
|
command.BoolFlag("w", "Show warnings", &showWarnings)
|
||||||
|
|
||||||
|
loglevel := ""
|
||||||
|
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
@@ -52,7 +75,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
var debugBinaryProcess *process.Process = nil
|
||||||
var buildFrontend bool = false
|
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
@@ -63,7 +85,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Do initial build
|
// Do initial build
|
||||||
logger.Println("Building application for development...")
|
logger.Println("Building application for development...")
|
||||||
debugBinaryProcess, err = restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
newProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||||
|
if newProcess != nil {
|
||||||
|
debugBinaryProcess = newProcess
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -79,7 +104,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("%s", err.Error())
|
logger.Fatal("%s", err.Error())
|
||||||
}
|
}
|
||||||
logger.Println("Watching directory: %s", event.Name)
|
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -88,33 +113,29 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
// Check for file writes
|
// Check for file writes
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
|
||||||
logger.Println("modified file: %s", event.Name)
|
|
||||||
var rebuild bool = false
|
var rebuild bool = false
|
||||||
|
|
||||||
// Iterate all file patterns
|
// Iterate all file patterns
|
||||||
for _, pattern := range extensionsThatTriggerARebuild {
|
for _, pattern := range extensionsThatTriggerARebuild {
|
||||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
if strings.HasSuffix(event.Name, pattern) {
|
||||||
if err != nil {
|
rebuild = true
|
||||||
logger.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
if rebuild {
|
|
||||||
// Only build frontend when the file isn't a Go file
|
|
||||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rebuild {
|
if !rebuild {
|
||||||
logger.Println("Filename change: %s did not match extension list (%s)", event.Name, extensions)
|
if showWarnings {
|
||||||
|
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
||||||
|
|
||||||
// Do a rebuild
|
// Do a rebuild
|
||||||
|
|
||||||
// Try and build the app
|
// Try and build the app
|
||||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error during build: %s", err.Error())
|
fmt.Printf("Error during build: %s", err.Error())
|
||||||
return
|
return
|
||||||
@@ -128,23 +149,28 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get project dir
|
// Get project dir
|
||||||
dir, err := os.Getwd()
|
projectDir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all subdirectories
|
// Get all subdirectories
|
||||||
dirs, err := fs.GetSubdirectories(dir)
|
dirs, err := fs.GetSubdirectories(projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogGreen("Watching (sub)/directory: %s", projectDir)
|
||||||
|
|
||||||
// Setup a watcher for non-node_modules directories
|
// Setup a watcher for non-node_modules directories
|
||||||
dirs.Each(func(dir string) {
|
dirs.Each(func(dir string) {
|
||||||
if strings.Contains(dir, "node_modules") {
|
if strings.Contains(dir, "node_modules") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Println("Watching directory: %s", dir)
|
// Ignore build directory
|
||||||
|
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = watcher.Add(dir)
|
err = watcher.Add(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
logger.Fatal(err.Error())
|
||||||
@@ -156,7 +182,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
for quit == false {
|
for quit == false {
|
||||||
select {
|
select {
|
||||||
case <-quitChannel:
|
case <-quitChannel:
|
||||||
println("Caught quit")
|
LogGreen("\nCaught quit")
|
||||||
// Notify debouncer to quit
|
// Notify debouncer to quit
|
||||||
debounceQuit <- true
|
debounceQuit <- true
|
||||||
quit = true
|
quit = true
|
||||||
@@ -171,7 +197,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Development mode exited")
|
LogGreen("Development mode exited")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -198,15 +224,15 @@ exit:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string) (*process.Process, error) {
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||||
println()
|
println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
LogRed("Build error - continuing to run current version")
|
||||||
return nil, errors.Wrap(err, "Build Failed:")
|
LogDarkYellow(err.Error())
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
logger.Println("Build new binary: %s", appBinary)
|
|
||||||
|
|
||||||
// Kill existing binary if need be
|
// Kill existing binary if need be
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
@@ -222,7 +248,7 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
// TODO: Generate `backend.js`
|
// TODO: Generate `backend.js`
|
||||||
|
|
||||||
// Start up new binary
|
// Start up new binary
|
||||||
newProcess := process.NewProcess(logger, appBinary)
|
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
||||||
err = newProcess.Start()
|
err = newProcess.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove binary
|
// Remove binary
|
||||||
@@ -239,7 +265,7 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
||||||
|
|
||||||
// Create random output file
|
// Create random output file
|
||||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
||||||
|
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ import (
|
|||||||
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("update", "Update the Wails CLI")
|
command := app.NewSubCommand("update", "Update the Wails CLI")
|
||||||
command.LongDescription(`This command allows you to update your version of Wails.`)
|
command.LongDescription(`This command allows you to update your version of the Wails CLI.`)
|
||||||
|
|
||||||
// Setup flags
|
// Setup flags
|
||||||
var prereleaseRequired bool
|
var prereleaseRequired bool
|
||||||
command.BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
|
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
||||||
|
|
||||||
var specificVersion string
|
var specificVersion string
|
||||||
command.StringFlag("version", "Install a specific version (Overrides other flags)", &specificVersion)
|
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logger.Print("Installing Wails " + desiredVersion + "...")
|
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
||||||
|
|
||||||
// Run command in non module directory
|
// Run command in non module directory
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
@@ -158,7 +158,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logger.Println("Wails updated to " + desiredVersion)
|
logger.Println("Wails CLI updated to " + desiredVersion)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
@@ -19,11 +22,17 @@ func fatal(message string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func banner(_ *clir.Cli) string {
|
||||||
|
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
||||||
|
|
||||||
|
app.SetBannerFunction(banner)
|
||||||
|
|
||||||
build.AddBuildSubcommand(app, os.Stdout)
|
build.AddBuildSubcommand(app, os.Stdout)
|
||||||
err = initialise.AddSubcommand(app, os.Stdout)
|
err = initialise.AddSubcommand(app, os.Stdout)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.26"
|
var version = "v2.0.0-alpha.57"
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ require (
|
|||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
github.com/tdewolff/minify v2.3.6+incompatible
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||||
github.com/tdewolff/test v1.0.6 // indirect
|
github.com/tdewolff/test v1.0.6 // indirect
|
||||||
|
github.com/wzshiming/ctc v1.2.3
|
||||||
github.com/xyproto/xpm v1.2.1
|
github.com/xyproto/xpm v1.2.1
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
||||||
nhooyr.io/websocket v1.8.6
|
nhooyr.io/websocket v1.8.6
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
|||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
|
||||||
|
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
|
||||||
|
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
|
||||||
|
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20=
|
||||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -92,11 +96,10 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
|
|||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init initialises the application for a debug environment
|
// Init initialises the application for a debug environment
|
||||||
@@ -19,10 +20,10 @@ func (a *App) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set log levels
|
// Set log levels
|
||||||
greeting := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if len(*greeting) > 0 {
|
if len(*loglevel) > 0 {
|
||||||
switch strings.ToLower(*greeting) {
|
switch strings.ToLower(*loglevel) {
|
||||||
case "trace":
|
case "trace":
|
||||||
a.logger.SetLogLevel(logger.TRACE)
|
a.logger.SetLogLevel(logger.TRACE)
|
||||||
case "info":
|
case "info":
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type App struct {
|
|||||||
//binding *subsystem.Binding
|
//binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
menu *subsystem.Menu
|
menu *subsystem.Menu
|
||||||
|
url *subsystem.URL
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
menuManager *menumanager.Manager
|
menuManager *menumanager.Manager
|
||||||
@@ -117,14 +118,6 @@ func (a *App) Run() error {
|
|||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
err = a.servicebus.Start()
|
err = a.servicebus.Start()
|
||||||
@@ -132,7 +125,7 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -168,6 +161,19 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if a.options.Mac.URLHandlers != nil {
|
||||||
|
// Start the url handler subsystem
|
||||||
|
url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.url = url
|
||||||
|
err = a.url.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Start the eventing subsystem
|
// Start the eventing subsystem
|
||||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -207,6 +213,14 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup signal handler
|
||||||
|
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.signal = signalsubsystem
|
||||||
|
a.signal.Start()
|
||||||
|
|
||||||
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
||||||
a.logger.Trace("Ffenestri.Run() exited")
|
a.logger.Trace("Ffenestri.Run() exited")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -231,5 +245,10 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown callback
|
||||||
|
if a.shutdownCallback != nil {
|
||||||
|
a.shutdownCallback()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
|
|
||||||
// Set up logger
|
// Set up logger
|
||||||
myLogger := logger.New(appoptions.Logger)
|
myLogger := logger.New(appoptions.Logger)
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
// Create the menu manager
|
// Create the menu manager
|
||||||
menuManager := menumanager.NewManager()
|
menuManager := menumanager.NewManager()
|
||||||
@@ -119,14 +118,6 @@ func (a *App) Run() error {
|
|||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
err = a.servicebus.Start()
|
err = a.servicebus.Start()
|
||||||
@@ -134,7 +125,7 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -212,6 +203,14 @@ func (a *App) Run() error {
|
|||||||
// Generate backend.js
|
// Generate backend.js
|
||||||
a.bindings.GenerateBackendJS()
|
a.bindings.GenerateBackendJS()
|
||||||
|
|
||||||
|
// Setup signal handler
|
||||||
|
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.signal = signalsubsystem
|
||||||
|
a.signal.Start()
|
||||||
|
|
||||||
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
||||||
a.logger.Trace("Bridge.Run() exited")
|
a.logger.Trace("Bridge.Run() exited")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -236,6 +235,10 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown callback
|
||||||
|
if a.shutdownCallback != nil {
|
||||||
|
a.shutdownCallback()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const backend = {`)
|
|||||||
sortedPackageNames.Add(packageName)
|
sortedPackageNames.Add(packageName)
|
||||||
}
|
}
|
||||||
sortedPackageNames.Sort()
|
sortedPackageNames.Sort()
|
||||||
for _, packageName := range sortedPackageNames.AsSlice() {
|
sortedPackageNames.Each(func(packageName string) {
|
||||||
packages := store[packageName]
|
packages := store[packageName]
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
@@ -67,7 +67,8 @@ const backend = {`)
|
|||||||
sortedStructNames.Add(structName)
|
sortedStructNames.Add(structName)
|
||||||
}
|
}
|
||||||
sortedStructNames.Sort()
|
sortedStructNames.Sort()
|
||||||
for _, structName := range sortedStructNames.AsSlice() {
|
|
||||||
|
sortedStructNames.Each(func(structName string) {
|
||||||
structs := packages[structName]
|
structs := packages[structName]
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
@@ -78,7 +79,7 @@ const backend = {`)
|
|||||||
}
|
}
|
||||||
sortedMethodNames.Sort()
|
sortedMethodNames.Sort()
|
||||||
|
|
||||||
for _, methodName := range sortedMethodNames.AsSlice() {
|
sortedMethodNames.Each(func(methodName string) {
|
||||||
methodDetails := structs[methodName]
|
methodDetails := structs[methodName]
|
||||||
output.WriteString(" /**\n")
|
output.WriteString(" /**\n")
|
||||||
output.WriteString(" * " + methodName + "\n")
|
output.WriteString(" * " + methodName + "\n")
|
||||||
@@ -109,13 +110,16 @@ const backend = {`)
|
|||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
output.WriteString(fmt.Sprintf(" },"))
|
output.WriteString(fmt.Sprintf(" },"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
|
||||||
|
})
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }"))
|
output.WriteString(fmt.Sprintf(" }"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
})
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }\n"))
|
output.WriteString(fmt.Sprintf(" }\n"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
})
|
||||||
|
|
||||||
output.WriteString(`};
|
output.WriteString(`};
|
||||||
export default backend;`)
|
export default backend;`)
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ type BridgeClient struct {
|
|||||||
messageCache chan string
|
messageCache chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) DeleteTrayMenuByID(id string) {
|
||||||
|
b.session.sendMessage("TD" + id)
|
||||||
|
}
|
||||||
|
|
||||||
func NewBridgeClient() *BridgeClient {
|
func NewBridgeClient() *BridgeClient {
|
||||||
return &BridgeClient{
|
return &BridgeClient{
|
||||||
messageCache: make(chan string, 100),
|
messageCache: make(chan string, 100),
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ type DialogClient struct {
|
|||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) DeleteTrayMenuByID(id string) {
|
||||||
|
}
|
||||||
|
|
||||||
func NewDialogClient(log *logger.Logger) *DialogClient {
|
func NewDialogClient(log *logger.Logger) *DialogClient {
|
||||||
return &DialogClient{
|
return &DialogClient{
|
||||||
log: log,
|
log: log,
|
||||||
|
|||||||
89
v2/internal/colour/colour.go
Normal file
89
v2/internal/colour/colour.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package colour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wzshiming/ctc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Col(col ctc.Color, text string) string {
|
||||||
|
return fmt.Sprintf("%s%s%s", col, text, ctc.Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Yellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Red(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Blue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Green(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Cyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Magenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func White(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Black(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkYellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkRed(text string) string {
|
||||||
|
return Col(ctc.ForegroundRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkGreen(text string) string {
|
||||||
|
return Col(ctc.ForegroundGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkCyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkMagenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkWhite(text string) string {
|
||||||
|
return Col(ctc.ForegroundWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlack(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rainbowCols = []func(string) string{Red, Yellow, Green, Cyan, Blue, Magenta}
|
||||||
|
|
||||||
|
func Rainbow(text string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
fn := rainbowCols[i%len(rainbowCols)]
|
||||||
|
builder.WriteString(fn(text[i : i+1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ extern void DarkModeEnabled(struct Application*, char *callbackID);
|
|||||||
extern void SetApplicationMenu(struct Application*, const char *);
|
extern void SetApplicationMenu(struct Application*, const char *);
|
||||||
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
||||||
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
||||||
|
extern void DeleteTrayMenuByID(struct Application*, const char *id);
|
||||||
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
||||||
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
||||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
||||||
|
|||||||
@@ -208,3 +208,7 @@ func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
|||||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
||||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) DeleteTrayMenuByID(id string) {
|
||||||
|
C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id))
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,9 @@ func (a *Application) processPlatformSettings() error {
|
|||||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set activation policy
|
||||||
|
C.SetActivationPolicy(a.app, C.int(mac.ActivationPolicy))
|
||||||
|
|
||||||
// Check if the webview should be transparent
|
// Check if the webview should be transparent
|
||||||
if mac.WebviewIsTransparent {
|
if mac.WebviewIsTransparent {
|
||||||
C.WebviewIsTransparent(a.app)
|
C.WebviewIsTransparent(a.app)
|
||||||
@@ -87,5 +90,10 @@ func (a *Application) processPlatformSettings() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process URL Handlers
|
||||||
|
if a.config.Mac.URLHandlers != nil {
|
||||||
|
C.HasURLHandlers(a.app)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,22 +13,41 @@
|
|||||||
|
|
||||||
// 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 kAEGetURL 'GURL'
|
||||||
|
#define keyDirectObject '----'
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
@@ -66,6 +85,10 @@
|
|||||||
#define NSControlStateValueOff 0
|
#define NSControlStateValueOff 0
|
||||||
#define NSControlStateValueOn 1
|
#define NSControlStateValueOn 1
|
||||||
|
|
||||||
|
#define NSApplicationActivationPolicyRegular 0
|
||||||
|
#define NSApplicationActivationPolicyAccessory 1
|
||||||
|
#define NSApplicationActivationPolicyProhibited 2
|
||||||
|
|
||||||
// Unbelievably, if the user swaps their button preference
|
// Unbelievably, if the user swaps their button preference
|
||||||
// then right buttons are reported as left buttons
|
// then right buttons are reported as left buttons
|
||||||
#define NSEventMaskLeftMouseDown 1 << 1
|
#define NSEventMaskLeftMouseDown 1 << 1
|
||||||
@@ -110,6 +133,10 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
|||||||
//void SetContextMenus(struct Application* app, const char *);
|
//void SetContextMenus(struct Application* app, const char *);
|
||||||
void AddTrayMenu(struct Application* app, const char *);
|
void AddTrayMenu(struct Application* app, const char *);
|
||||||
|
|
||||||
|
void SetActivationPolicy(struct Application* app, int policy);
|
||||||
|
|
||||||
void* lookupStringConstant(id constantName);
|
void* lookupStringConstant(id constantName);
|
||||||
|
|
||||||
|
void HasURLHandlers(struct Application* app);
|
||||||
|
|
||||||
#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;
|
||||||
@@ -180,151 +180,151 @@ id processAcceleratorKey(const char *key) {
|
|||||||
return str("");
|
return str("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if( STREQ(key, "Backspace") ) {
|
if( STREQ(key, "backspace") ) {
|
||||||
return strunicode(0x0008);
|
return strunicode(0x0008);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Tab") ) {
|
if( STREQ(key, "tab") ) {
|
||||||
return strunicode(0x0009);
|
return strunicode(0x0009);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Return") ) {
|
if( STREQ(key, "return") ) {
|
||||||
return strunicode(0x000d);
|
return strunicode(0x000d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Escape") ) {
|
if( STREQ(key, "escape") ) {
|
||||||
return strunicode(0x001b);
|
return strunicode(0x001b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Left") ) {
|
if( STREQ(key, "left") ) {
|
||||||
return strunicode(0x001c);
|
return strunicode(0x001c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Right") ) {
|
if( STREQ(key, "right") ) {
|
||||||
return strunicode(0x001d);
|
return strunicode(0x001d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Up") ) {
|
if( STREQ(key, "up") ) {
|
||||||
return strunicode(0x001e);
|
return strunicode(0x001e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Down") ) {
|
if( STREQ(key, "down") ) {
|
||||||
return strunicode(0x001f);
|
return strunicode(0x001f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Space") ) {
|
if( STREQ(key, "space") ) {
|
||||||
return strunicode(0x0020);
|
return strunicode(0x0020);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Delete") ) {
|
if( STREQ(key, "delete") ) {
|
||||||
return strunicode(0x007f);
|
return strunicode(0x007f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Home") ) {
|
if( STREQ(key, "home") ) {
|
||||||
return strunicode(0x2196);
|
return strunicode(0x2196);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "End") ) {
|
if( STREQ(key, "end") ) {
|
||||||
return strunicode(0x2198);
|
return strunicode(0x2198);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Up") ) {
|
if( STREQ(key, "page up") ) {
|
||||||
return strunicode(0x21de);
|
return strunicode(0x21de);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Down") ) {
|
if( STREQ(key, "page down") ) {
|
||||||
return strunicode(0x21df);
|
return strunicode(0x21df);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F1") ) {
|
if( STREQ(key, "f1") ) {
|
||||||
return strunicode(0xf704);
|
return strunicode(0xf704);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F2") ) {
|
if( STREQ(key, "f2") ) {
|
||||||
return strunicode(0xf705);
|
return strunicode(0xf705);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F3") ) {
|
if( STREQ(key, "f3") ) {
|
||||||
return strunicode(0xf706);
|
return strunicode(0xf706);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F4") ) {
|
if( STREQ(key, "f4") ) {
|
||||||
return strunicode(0xf707);
|
return strunicode(0xf707);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F5") ) {
|
if( STREQ(key, "f5") ) {
|
||||||
return strunicode(0xf708);
|
return strunicode(0xf708);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F6") ) {
|
if( STREQ(key, "f6") ) {
|
||||||
return strunicode(0xf709);
|
return strunicode(0xf709);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F7") ) {
|
if( STREQ(key, "f7") ) {
|
||||||
return strunicode(0xf70a);
|
return strunicode(0xf70a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F8") ) {
|
if( STREQ(key, "f8") ) {
|
||||||
return strunicode(0xf70b);
|
return strunicode(0xf70b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F9") ) {
|
if( STREQ(key, "f9") ) {
|
||||||
return strunicode(0xf70c);
|
return strunicode(0xf70c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F10") ) {
|
if( STREQ(key, "f10") ) {
|
||||||
return strunicode(0xf70d);
|
return strunicode(0xf70d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F11") ) {
|
if( STREQ(key, "f11") ) {
|
||||||
return strunicode(0xf70e);
|
return strunicode(0xf70e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F12") ) {
|
if( STREQ(key, "f12") ) {
|
||||||
return strunicode(0xf70f);
|
return strunicode(0xf70f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F13") ) {
|
if( STREQ(key, "f13") ) {
|
||||||
return strunicode(0xf710);
|
return strunicode(0xf710);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F14") ) {
|
if( STREQ(key, "f14") ) {
|
||||||
return strunicode(0xf711);
|
return strunicode(0xf711);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F15") ) {
|
if( STREQ(key, "f15") ) {
|
||||||
return strunicode(0xf712);
|
return strunicode(0xf712);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F16") ) {
|
if( STREQ(key, "f16") ) {
|
||||||
return strunicode(0xf713);
|
return strunicode(0xf713);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F17") ) {
|
if( STREQ(key, "f17") ) {
|
||||||
return strunicode(0xf714);
|
return strunicode(0xf714);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F18") ) {
|
if( STREQ(key, "f18") ) {
|
||||||
return strunicode(0xf715);
|
return strunicode(0xf715);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F19") ) {
|
if( STREQ(key, "f19") ) {
|
||||||
return strunicode(0xf716);
|
return strunicode(0xf716);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F20") ) {
|
if( STREQ(key, "f20") ) {
|
||||||
return strunicode(0xf717);
|
return strunicode(0xf717);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F21") ) {
|
if( STREQ(key, "f21") ) {
|
||||||
return strunicode(0xf718);
|
return strunicode(0xf718);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F22") ) {
|
if( STREQ(key, "f22") ) {
|
||||||
return strunicode(0xf719);
|
return strunicode(0xf719);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F23") ) {
|
if( STREQ(key, "f23") ) {
|
||||||
return strunicode(0xf71a);
|
return strunicode(0xf71a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F24") ) {
|
if( STREQ(key, "f24") ) {
|
||||||
return strunicode(0xf71b);
|
return strunicode(0xf71b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F25") ) {
|
if( STREQ(key, "f25") ) {
|
||||||
return strunicode(0xf71c);
|
return strunicode(0xf71c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F26") ) {
|
if( STREQ(key, "f26") ) {
|
||||||
return strunicode(0xf71d);
|
return strunicode(0xf71d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F27") ) {
|
if( STREQ(key, "f27") ) {
|
||||||
return strunicode(0xf71e);
|
return strunicode(0xf71e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F28") ) {
|
if( STREQ(key, "f28") ) {
|
||||||
return strunicode(0xf71f);
|
return strunicode(0xf71f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F29") ) {
|
if( STREQ(key, "f29") ) {
|
||||||
return strunicode(0xf720);
|
return strunicode(0xf720);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F30") ) {
|
if( STREQ(key, "f30") ) {
|
||||||
return strunicode(0xf721);
|
return strunicode(0xf721);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F31") ) {
|
if( STREQ(key, "f31") ) {
|
||||||
return strunicode(0xf722);
|
return strunicode(0xf722);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F32") ) {
|
if( STREQ(key, "f32") ) {
|
||||||
return strunicode(0xf723);
|
return strunicode(0xf723);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F33") ) {
|
if( STREQ(key, "f33") ) {
|
||||||
return strunicode(0xf724);
|
return strunicode(0xf724);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F34") ) {
|
if( STREQ(key, "f34") ) {
|
||||||
return strunicode(0xf725);
|
return strunicode(0xf725);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F35") ) {
|
if( STREQ(key, "f35") ) {
|
||||||
return strunicode(0xf726);
|
return strunicode(0xf726);
|
||||||
}
|
}
|
||||||
// if( STREQ(key, "Insert") ) {
|
// if( STREQ(key, "Insert") ) {
|
||||||
@@ -336,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
|||||||
// if( STREQ(key, "ScrollLock") ) {
|
// if( STREQ(key, "ScrollLock") ) {
|
||||||
// return strunicode(0xf72f);
|
// return strunicode(0xf72f);
|
||||||
// }
|
// }
|
||||||
if( STREQ(key, "NumLock") ) {
|
if( STREQ(key, "numLock") ) {
|
||||||
return strunicode(0xf739);
|
return strunicode(0xf739);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
||||||
@@ -508,20 +508,21 @@ unsigned long parseModifiers(const char **modifiers) {
|
|||||||
const char *thisModifier = modifiers[0];
|
const char *thisModifier = modifiers[0];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while( thisModifier != NULL ) {
|
while( thisModifier != NULL ) {
|
||||||
|
|
||||||
// Determine flags
|
// Determine flags
|
||||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
if( STREQ(thisModifier, "optionoralt") ) {
|
||||||
result |= NSEventModifierFlagOption;
|
result |= NSEventModifierFlagOption;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Shift") ) {
|
if( STREQ(thisModifier, "shift") ) {
|
||||||
result |= NSEventModifierFlagShift;
|
result |= NSEventModifierFlagShift;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Super") ) {
|
if( STREQ(thisModifier, "super") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Control") ) {
|
if( STREQ(thisModifier, "ctrl") ) {
|
||||||
result |= NSEventModifierFlagControl;
|
result |= NSEventModifierFlagControl;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
@@ -539,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;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -565,74 +566,42 @@ 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 processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage) {
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||||
id item = ALLOC("NSMenuItem");
|
|
||||||
|
|
||||||
// Create a MenuItemCallbackData
|
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
|
||||||
|
|
||||||
id key = processAcceleratorKey(acceleratorkey);
|
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
|
||||||
s("menuItemCallback:"), key);
|
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
|
||||||
msg(item, s("setToolTip:"), str(tooltip));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process image
|
|
||||||
if( image != NULL && strlen(image) > 0) {
|
|
||||||
id data = ALLOC("NSData");
|
|
||||||
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
|
||||||
id nsimage = ALLOC("NSImage");
|
|
||||||
msg(nsimage, s("initWithData:"), imageData);
|
|
||||||
if( templateImage ) {
|
|
||||||
msg(nsimage, s("template"), YES);
|
|
||||||
}
|
|
||||||
msg(item, s("setImage:"), nsimage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Menu Item attributes
|
// Process Menu Item attributes
|
||||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
|
|
||||||
// Process font
|
// Process font
|
||||||
id font;
|
|
||||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||||
|
|
||||||
// Check if valid
|
// Check if valid
|
||||||
id fontNameAsNSString = str(fontName);
|
id fontNameAsNSString = str(fontName);
|
||||||
id fontsOnSystem = msg(msg(c("NSFontManager"), s("sharedFontManager")), s("availableFonts"));
|
id font = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||||
bool valid = msg(fontsOnSystem, s("containsObject:"), fontNameAsNSString);
|
if( font == NULL ) {
|
||||||
if( valid ) {
|
bool supportsMonospacedDigitSystemFont = (bool) ((id(*)(id, SEL, SEL))objc_msgSend)(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
||||||
font = msg(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
|
||||||
} else {
|
|
||||||
bool supportsMonospacedDigitSystemFont = (bool) msg(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
|
||||||
if( supportsMonospacedDigitSystemFont ) {
|
if( supportsMonospacedDigitSystemFont ) {
|
||||||
font = msg(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, NSFontWeightRegular);
|
font = ((id(*)(id, SEL, CGFloat, CGFloat))objc_msgSend)(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, (CGFloat)NSFontWeightRegular);
|
||||||
} else {
|
} else {
|
||||||
font = msg(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(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_float(c("NSNumber"), s("numberWithFloat:"), (float)0.0);
|
||||||
id offset = msg(c("NSNumber"), s("numberWithFloat:"), 0.0);
|
id offsetAttrName = lookupStringConstant(str("NSBaselineOffsetAttributeName"));
|
||||||
msg(dictionary, s("setObject:forKey:"), offset, lookupStringConstant(str("NSBaselineOffsetAttributeName")));
|
msg_id_id(dictionary, s("setObject:forKey:"), offset, offsetAttrName);
|
||||||
|
|
||||||
// RGBA
|
// RGBA
|
||||||
if( RGBA != NULL && strlen(RGBA) > 0) {
|
if( RGBA != NULL && strlen(RGBA) > 0) {
|
||||||
unsigned short r, g, b, a;
|
unsigned short r, g, b, a;
|
||||||
@@ -641,32 +610,77 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
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);
|
||||||
|
msg_reg(colour, s("autorelease"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(dictionary, s("release"));
|
msg_reg(attributedString, s("autorelease"));
|
||||||
|
msg_reg(dictionary, s("release"));
|
||||||
|
return attributedString;
|
||||||
|
}
|
||||||
|
|
||||||
msg(item, s("setAttributedTitle:"), attributedString);
|
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate) {
|
||||||
msg(attributedString, s("autorelease"));
|
id item = ALLOC("NSMenuItem");
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
// Create a MenuItemCallbackData
|
||||||
msg(item, s("autorelease"));
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
||||||
|
|
||||||
|
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||||
|
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||||
|
|
||||||
|
if( !alternate ) {
|
||||||
|
id key = processAcceleratorKey(acceleratorkey);
|
||||||
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||||
|
s("menuItemCallback:"), key);
|
||||||
|
} else {
|
||||||
|
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg_id(item, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process image
|
||||||
|
if( image != NULL && strlen(image) > 0) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
||||||
|
id nsimage = ALLOC("NSImage");
|
||||||
|
msg_id(nsimage, s("initWithData:"), imageData);
|
||||||
|
if( templateImage ) {
|
||||||
|
msg_bool(nsimage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
msg_id(item, s("setImage:"), nsimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
||||||
|
msg_id(item, s("setAttributedTitle:"), attributedString);
|
||||||
|
|
||||||
|
//msg_id(item, s("setTitle:"), str(title));
|
||||||
|
|
||||||
|
msg_bool(item, s("setEnabled:"), !disabled);
|
||||||
|
msg_reg(item, s("autorelease"));
|
||||||
|
|
||||||
// Process modifiers
|
// Process modifiers
|
||||||
if( modifiers != NULL ) {
|
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);
|
||||||
}
|
}
|
||||||
msg(parentMenu, s("addItem:"), item);
|
|
||||||
|
// alternate
|
||||||
|
if( alternate ) {
|
||||||
|
msg_bool(item, s("setAlternate:"), true);
|
||||||
|
msg_int(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
||||||
|
}
|
||||||
|
msg_id(parentMenu, s("addItem:"), item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -700,8 +714,8 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
|
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
|
||||||
id thisMenu = createMenu(str(name));
|
id thisMenu = createMenu(str(name));
|
||||||
|
|
||||||
msg(thisMenuItem, s("setSubmenu:"), thisMenu);
|
msg_id(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||||
msg(parentMenu, s("addItem:"), thisMenuItem);
|
msg_id(parentMenu, s("addItem:"), thisMenuItem);
|
||||||
|
|
||||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||||
// If we have no items, just return
|
// If we have no items, just return
|
||||||
@@ -726,6 +740,11 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
label = "(empty)";
|
label = "(empty)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Is this an alternate menu item?
|
||||||
|
bool alternate = false;
|
||||||
|
getJSONBool(item, "MacAlternate", &alternate);
|
||||||
|
|
||||||
const char *menuid = getJSONString(item, "ID");
|
const char *menuid = getJSONString(item, "ID");
|
||||||
if ( menuid == NULL) {
|
if ( menuid == NULL) {
|
||||||
menuid = "";
|
menuid = "";
|
||||||
@@ -746,7 +765,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 = 12;
|
int fontSize = 0;
|
||||||
getJSONInt(item, "FontSize", &fontSize);
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
@@ -781,7 +800,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
if( type != NULL ) {
|
if( type != NULL ) {
|
||||||
|
|
||||||
if( STREQ(type->string_, "Text")) {
|
if( STREQ(type->string_, "Text")) {
|
||||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate);
|
||||||
}
|
}
|
||||||
else if ( STREQ(type->string_, "Separator")) {
|
else if ( STREQ(type->string_, "Separator")) {
|
||||||
addSeparator(parentMenu);
|
addSeparator(parentMenu);
|
||||||
|
|||||||
@@ -105,10 +105,12 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
|||||||
|
|
||||||
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage);
|
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate);
|
||||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||||
|
|
||||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) ;
|
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||||
id GetMenu(Menu *menu);
|
id GetMenu(Menu *menu);
|
||||||
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
||||||
|
|
||||||
#endif //ASSETS_C_MENU_DARWIN_H
|
#endif //ASSETS_C_MENU_DARWIN_H
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "traymenu_darwin.h"
|
#include "traymenu_darwin.h"
|
||||||
#include "trayicons.h"
|
#include "trayicons.h"
|
||||||
|
|
||||||
|
extern Class trayMenuDelegateClass;
|
||||||
|
|
||||||
// A cache for all our tray menu icons
|
// A cache for all our tray menu icons
|
||||||
// Global because it's a singleton
|
// Global because it's a singleton
|
||||||
struct hashmap_s trayIconCache;
|
struct hashmap_s trayIconCache;
|
||||||
@@ -29,12 +31,23 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
|||||||
|
|
||||||
result->ID = mustJSONString(processedJSON, "ID");
|
result->ID = mustJSONString(processedJSON, "ID");
|
||||||
result->label = mustJSONString(processedJSON, "Label");
|
result->label = mustJSONString(processedJSON, "Label");
|
||||||
result->icon = mustJSONString(processedJSON, "Icon");
|
result->icon = mustJSONString(processedJSON, "Image");
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
result->fontName = getJSONString(processedJSON, "FontName");
|
||||||
|
result->RGBA = getJSONString(processedJSON, "RGBA");
|
||||||
|
getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage);
|
||||||
|
result->fontSize = 0;
|
||||||
|
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");
|
||||||
result->menu = NewMenu(processedMenu);
|
result->menu = NewMenu(processedMenu);
|
||||||
|
|
||||||
|
result->delegate = NULL;
|
||||||
|
|
||||||
// Init tray status bar item
|
// Init tray status bar item
|
||||||
result->statusbaritem = NULL;
|
result->statusbaritem = NULL;
|
||||||
|
|
||||||
@@ -50,15 +63,23 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
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"));
|
||||||
msg(statusBarButton, s("setTitle:"), str(label));
|
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||||
|
|
||||||
|
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) {
|
||||||
@@ -68,44 +89,64 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
||||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
|
||||||
msg(statusBarButton, s("setImage:"), trayImage);
|
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
||||||
|
if (trayImage == NULL) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(data, s("initWithBase64EncodedString:options:"), str(trayMenu->icon), 0);
|
||||||
|
trayImage = ALLOC("NSImage");
|
||||||
|
msg_id(trayImage, s("initWithData:"), imageData);
|
||||||
|
|
||||||
|
if( trayMenu->templateImage ) {
|
||||||
|
msg_bool(trayImage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_int(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||||
|
msg_id(statusBarButton, s("setImage:"), trayImage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ShowTrayMenu(TrayMenu* trayMenu) {
|
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);
|
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);
|
||||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create delegate
|
||||||
|
id trayMenuDelegate = msg_reg((id)trayMenuDelegateClass, s("new"));
|
||||||
|
msg_id(menu, s("setDelegate:"), trayMenuDelegate);
|
||||||
|
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create menu delegate
|
||||||
|
trayMenu->delegate = trayMenuDelegate;
|
||||||
|
|
||||||
|
msg_id(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
||||||
@@ -147,12 +188,16 @@ 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 ) {
|
||||||
|
msg_reg(trayMenu->delegate, s("release"));
|
||||||
|
}
|
||||||
|
|
||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
MEMFREE(trayMenu);
|
MEMFREE(trayMenu);
|
||||||
}
|
}
|
||||||
@@ -182,9 +227,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,14 +13,24 @@ 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;
|
||||||
|
const char *fontName;
|
||||||
|
int fontSize;
|
||||||
|
const char *RGBA;
|
||||||
|
|
||||||
|
bool disabled;
|
||||||
|
|
||||||
Menu* menu;
|
Menu* menu;
|
||||||
|
|
||||||
id statusbaritem;
|
id statusbaritem;
|
||||||
int trayIconPosition;
|
unsigned int trayIconPosition;
|
||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
|
id delegate;
|
||||||
|
|
||||||
} TrayMenu;
|
} TrayMenu;
|
||||||
|
|
||||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||||
@@ -28,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*);
|
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();
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ TrayMenuStore* NewTrayMenuStore() {
|
|||||||
ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!");
|
ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&result->lock, NULL) != 0) {
|
||||||
|
printf("\n mutex init has failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,15 +30,19 @@ int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DumpTrayMenuStore(TrayMenuStore* store) {
|
void DumpTrayMenuStore(TrayMenuStore* store) {
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
||||||
|
|
||||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
//TODO: check if there is already an entry for this menu
|
//TODO: check if there is already an entry for this menu
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||||
@@ -43,12 +52,13 @@ int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
||||||
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||||
DeleteTrayMenu(e->data);
|
DeleteTrayMenu(e->data);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -65,22 +75,39 @@ void DeleteTrayMenuStore(TrayMenuStore *store) {
|
|||||||
|
|
||||||
// Destroy tray menu map
|
// Destroy tray menu map
|
||||||
hashmap_destroy(&store->trayMenuMap);
|
hashmap_destroy(&store->trayMenuMap);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||||
// Get the current menu
|
// Get the current menu
|
||||||
return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
pthread_mutex_lock(&store->lock);
|
||||||
|
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||||
// Get the current menu
|
// Get the current menu
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
if (result == NULL ) {
|
if (result == NULL ) {
|
||||||
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* ID) {
|
||||||
|
|
||||||
|
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
|
hashmap_remove(&store->trayMenuMap, ID, strlen(ID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
DeleteTrayMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
||||||
// Parse the JSON
|
// Parse the JSON
|
||||||
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
||||||
@@ -91,7 +118,17 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
|
|
||||||
// Check we have this menu
|
// Check we have this menu
|
||||||
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||||
UpdateTrayLabel(menu, Label);
|
|
||||||
|
const char *fontName = getJSONString(parsedUpdate, "FontName");
|
||||||
|
const char *RGBA = getJSONString(parsedUpdate, "RGBA");
|
||||||
|
int fontSize = 0;
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +142,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// If we don't have a menu, we create one
|
// If we don't have a menu, we create one
|
||||||
if ( currentMenu == NULL ) {
|
if ( currentMenu == NULL ) {
|
||||||
// Store the new menu
|
// Store the new menu
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Show it
|
// Show it
|
||||||
ShowTrayMenu(newMenu);
|
ShowTrayMenu(newMenu);
|
||||||
@@ -116,7 +155,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// Save the status bar reference
|
// Save the status bar reference
|
||||||
newMenu->statusbaritem = currentMenu->statusbaritem;
|
newMenu->statusbaritem = currentMenu->statusbaritem;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Delete the current menu
|
// Delete the current menu
|
||||||
DeleteMenu(currentMenu->menu);
|
DeleteMenu(currentMenu->menu);
|
||||||
@@ -125,9 +166,10 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
MEMFREE(currentMenu);
|
MEMFREE(currentMenu);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Show the updated menu
|
// Show the updated menu
|
||||||
ShowTrayMenu(newMenu);
|
ShowTrayMenu(newMenu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
#ifndef TRAYMENUSTORE_DARWIN_H
|
#ifndef TRAYMENUSTORE_DARWIN_H
|
||||||
#define TRAYMENUSTORE_DARWIN_H
|
#define TRAYMENUSTORE_DARWIN_H
|
||||||
|
|
||||||
|
#include "traymenu_darwin.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
int dummy;
|
int dummy;
|
||||||
@@ -13,6 +17,8 @@ typedef struct {
|
|||||||
// It maps tray IDs to TrayMenu*
|
// It maps tray IDs to TrayMenu*
|
||||||
struct hashmap_s trayMenuMap;
|
struct hashmap_s trayMenuMap;
|
||||||
|
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
|
||||||
} TrayMenuStore;
|
} TrayMenuStore;
|
||||||
|
|
||||||
TrayMenuStore* NewTrayMenuStore();
|
TrayMenuStore* NewTrayMenuStore();
|
||||||
@@ -22,6 +28,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON);
|
|||||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||||
|
|
||||||
|
TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID);
|
||||||
|
|
||||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
||||||
|
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
||||||
|
|
||||||
#endif //TRAYMENUSTORE_DARWIN_H
|
#endif //TRAYMENUSTORE_DARWIN_H
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func Mkdir(dirname string) error {
|
|||||||
// Returns error on failure
|
// Returns error on failure
|
||||||
func MkDirs(fullPath string, mode ...os.FileMode) error {
|
func MkDirs(fullPath string, mode ...os.FileMode) error {
|
||||||
var perms os.FileMode
|
var perms os.FileMode
|
||||||
perms = 0700
|
perms = 0755
|
||||||
if len(mode) == 1 {
|
if len(mode) == 1 {
|
||||||
perms = mode[0]
|
perms = mode[0]
|
||||||
}
|
}
|
||||||
@@ -243,7 +243,7 @@ func CopyDir(src string, dst string) (err error) {
|
|||||||
return fmt.Errorf("destination already exists")
|
return fmt.Errorf("destination already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.MkdirAll(dst, si.Mode())
|
err = MkDirs(dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type ProcessedMenuItem struct {
|
|||||||
// Image - base64 image data
|
// Image - base64 image data
|
||||||
Image string `json:",omitempty"`
|
Image string `json:",omitempty"`
|
||||||
MacTemplateImage bool `json:", omitempty"`
|
MacTemplateImage bool `json:", omitempty"`
|
||||||
|
MacAlternate bool `json:", omitempty"`
|
||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
Tooltip string `json:",omitempty"`
|
Tooltip string `json:",omitempty"`
|
||||||
@@ -60,6 +61,7 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
|
|||||||
FontName: menuItem.FontName,
|
FontName: menuItem.FontName,
|
||||||
Image: menuItem.Image,
|
Image: menuItem.Image,
|
||||||
MacTemplateImage: menuItem.MacTemplateImage,
|
MacTemplateImage: menuItem.MacTemplateImage,
|
||||||
|
MacAlternate: menuItem.MacAlternate,
|
||||||
Tooltip: menuItem.Tooltip,
|
Tooltip: menuItem.Tooltip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,29 +3,39 @@ package menumanager
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var trayMenuID int
|
var trayMenuID int
|
||||||
var trayMenuIDMutex sync.Mutex
|
var trayMenuIDMutex sync.Mutex
|
||||||
|
|
||||||
func generateTrayID() string {
|
func generateTrayID() string {
|
||||||
|
var idStr string
|
||||||
trayMenuIDMutex.Lock()
|
trayMenuIDMutex.Lock()
|
||||||
result := fmt.Sprintf("%d", trayMenuID)
|
idStr = strconv.Itoa(trayMenuID)
|
||||||
trayMenuID++
|
trayMenuID++
|
||||||
trayMenuIDMutex.Unlock()
|
trayMenuIDMutex.Unlock()
|
||||||
return result
|
return idStr
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrayMenu struct {
|
type TrayMenu struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string
|
||||||
Icon string
|
FontSize int
|
||||||
menuItemMap *MenuItemMap
|
FontName string
|
||||||
menu *menu.Menu
|
Disabled bool
|
||||||
ProcessedMenu *WailsMenu
|
Tooltip string `json:",omitempty"`
|
||||||
|
Image string
|
||||||
|
MacTemplateImage bool
|
||||||
|
RGBA string
|
||||||
|
menuItemMap *MenuItemMap
|
||||||
|
menu *menu.Menu
|
||||||
|
ProcessedMenu *WailsMenu
|
||||||
|
trayMenu *menu.TrayMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrayMenu) AsJSON() (string, error) {
|
func (t *TrayMenu) AsJSON() (string, error) {
|
||||||
@@ -39,10 +49,17 @@ func (t *TrayMenu) AsJSON() (string, error) {
|
|||||||
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
||||||
|
|
||||||
result := &TrayMenu{
|
result := &TrayMenu{
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
Icon: trayMenu.Icon,
|
FontName: trayMenu.FontName,
|
||||||
menu: trayMenu.Menu,
|
FontSize: trayMenu.FontSize,
|
||||||
menuItemMap: NewMenuItemMap(),
|
Disabled: trayMenu.Disabled,
|
||||||
|
Tooltip: trayMenu.Tooltip,
|
||||||
|
Image: trayMenu.Image,
|
||||||
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
|
menu: trayMenu.Menu,
|
||||||
|
RGBA: trayMenu.RGBA,
|
||||||
|
menuItemMap: NewMenuItemMap(),
|
||||||
|
trayMenu: trayMenu,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.menuItemMap.AddMenu(trayMenu.Menu)
|
result.menuItemMap.AddMenu(trayMenu.Menu)
|
||||||
@@ -51,6 +68,28 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuOpen(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnOpen == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuClose(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnClose == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnClose()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
newTrayMenu := NewTrayMenu(trayMenu)
|
newTrayMenu := NewTrayMenu(trayMenu)
|
||||||
|
|
||||||
@@ -65,6 +104,14 @@ func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
return newTrayMenu.AsJSON()
|
return newTrayMenu.AsJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetTrayID(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
|
trayID, exists := m.trayMenuPointers[trayMenu]
|
||||||
|
if !exists {
|
||||||
|
return "", fmt.Errorf("Unable to find menu ID for tray menu!")
|
||||||
|
}
|
||||||
|
return trayID, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetTrayMenu updates or creates a menu
|
// SetTrayMenu updates or creates a menu
|
||||||
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
|
trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
|
||||||
@@ -102,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)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ type Client interface {
|
|||||||
SetTrayMenu(trayMenuJSON string)
|
SetTrayMenu(trayMenuJSON string)
|
||||||
UpdateTrayMenuLabel(JSON string)
|
UpdateTrayMenuLabel(JSON string)
|
||||||
UpdateContextMenu(contextMenuJSON string)
|
UpdateContextMenu(contextMenuJSON string)
|
||||||
|
DeleteTrayMenuByID(id string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DispatchClient is what the frontends use to interface with the
|
// DispatchClient is what the frontends use to interface with the
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ func menuMessageParser(message string) (*parsedMessage, error) {
|
|||||||
callbackid := message[2:]
|
callbackid := message[2:]
|
||||||
topic = "menu:clicked"
|
topic = "menu:clicked"
|
||||||
data = callbackid
|
data = callbackid
|
||||||
|
case 'o':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayopen"
|
||||||
|
data = callbackid
|
||||||
|
case 'c':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayclose"
|
||||||
|
data = callbackid
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid menu message: %s", message)
|
return nil, fmt.Errorf("invalid menu message: %s", message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,14 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
|||||||
'M': menuMessageParser,
|
'M': menuMessageParser,
|
||||||
'T': trayMessageParser,
|
'T': trayMessageParser,
|
||||||
'X': contextMenusMessageParser,
|
'X': contextMenusMessageParser,
|
||||||
|
'U': urlMessageParser,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse will attempt to parse the given message
|
// Parse will attempt to parse the given message
|
||||||
func Parse(message string) (*parsedMessage, error) {
|
func Parse(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
if len(message) == 0 {
|
if len(message) == 0 {
|
||||||
return nil, fmt.Errorf("MessageParser received blank message");
|
return nil, fmt.Errorf("MessageParser received blank message")
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMethod := messageParsers[message[0]]
|
parseMethod := messageParsers[message[0]]
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func systemMessageParser(message string) (*parsedMessage, error) {
|
|||||||
// This is our startup hook - the frontend is now ready
|
// This is our startup hook - the frontend is now ready
|
||||||
case 'S':
|
case 'S':
|
||||||
topic := "hooks:startup"
|
topic := "hooks:startup"
|
||||||
responseMessage = &parsedMessage{Topic: topic, Data: nil}
|
startupURL := message[1:]
|
||||||
|
responseMessage = &parsedMessage{Topic: topic, Data: startupURL}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||||
}
|
}
|
||||||
|
|||||||
20
v2/internal/messagedispatcher/message/url.go
Normal file
20
v2/internal/messagedispatcher/message/url.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// urlMessageParser does what it says on the tin!
|
||||||
|
func urlMessageParser(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
|
// Sanity check: URL messages must be at least 2 bytes
|
||||||
|
if len(message) < 2 {
|
||||||
|
return nil, fmt.Errorf("log message was an invalid length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch on the log type
|
||||||
|
switch message[1] {
|
||||||
|
case 'C':
|
||||||
|
return &parsedMessage{Topic: "url:handler", Data: message[2:]}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("url message type '%c' invalid", message[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -527,6 +527,17 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel)
|
client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel)
|
||||||
}
|
}
|
||||||
|
case "deletetraymenu":
|
||||||
|
traymenuid, ok := result.Data().(string)
|
||||||
|
if !ok {
|
||||||
|
d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v",
|
||||||
|
result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, client := range d.clients {
|
||||||
|
client.frontend.DeleteTrayMenuByID(traymenuid)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
d.logger.Error("Unknown menufrontend command: %s", command)
|
||||||
|
|||||||
@@ -39,7 +39,12 @@ func (p *Process) Start() error {
|
|||||||
|
|
||||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
||||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
||||||
cmd.Wait()
|
err := cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != "signal: killed" {
|
||||||
|
logger.Fatal("Fatal error from app: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||||
*running = false
|
*running = false
|
||||||
exitChannel <- true
|
exitChannel <- true
|
||||||
|
|||||||
@@ -620,7 +620,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Menubar **/
|
/** Menubar **/
|
||||||
const menuVisible = writable(true);
|
const menuVisible = writable(false);
|
||||||
|
|
||||||
/** Trays **/
|
/** Trays **/
|
||||||
|
|
||||||
@@ -649,6 +649,18 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteTrayMenu(id) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === id);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
return log("ERROR: Attempted to delete tray index ")
|
||||||
|
}
|
||||||
|
current.splice(index, 1);
|
||||||
|
return current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let selectedMenu = writable(null);
|
let selectedMenu = writable(null);
|
||||||
|
|
||||||
function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
|
function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
|
||||||
@@ -1220,11 +1232,11 @@
|
|||||||
|
|
||||||
function get_each_context$1(ctx, list, i) {
|
function get_each_context$1(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[8] = list[i];
|
child_ctx[9] = list[i];
|
||||||
return child_ctx;
|
return child_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (29:0) {#if $menuVisible }
|
// (38:0) {#if $menuVisible }
|
||||||
function create_if_block$3(ctx) {
|
function create_if_block$3(ctx) {
|
||||||
let div;
|
let div;
|
||||||
let span1;
|
let span1;
|
||||||
@@ -1336,11 +1348,11 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// (32:4) {#each $trays as tray}
|
// (41:4) {#each $trays as tray}
|
||||||
function create_each_block$1(ctx) {
|
function create_each_block$1(ctx) {
|
||||||
let traymenu;
|
let traymenu;
|
||||||
let current;
|
let current;
|
||||||
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[8] } });
|
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[9] } });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
@@ -1352,7 +1364,7 @@
|
|||||||
},
|
},
|
||||||
p(ctx, dirty) {
|
p(ctx, dirty) {
|
||||||
const traymenu_changes = {};
|
const traymenu_changes = {};
|
||||||
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[8];
|
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[9];
|
||||||
traymenu.$set(traymenu_changes);
|
traymenu.$set(traymenu_changes);
|
||||||
},
|
},
|
||||||
i(local) {
|
i(local) {
|
||||||
@@ -1373,6 +1385,8 @@
|
|||||||
function create_fragment$3(ctx) {
|
function create_fragment$3(ctx) {
|
||||||
let if_block_anchor;
|
let if_block_anchor;
|
||||||
let current;
|
let current;
|
||||||
|
let mounted;
|
||||||
|
let dispose;
|
||||||
let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx);
|
let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1384,6 +1398,11 @@
|
|||||||
if (if_block) if_block.m(target, anchor);
|
if (if_block) if_block.m(target, anchor);
|
||||||
insert(target, if_block_anchor, anchor);
|
insert(target, if_block_anchor, anchor);
|
||||||
current = true;
|
current = true;
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
dispose = listen(window, "keydown", /*handleKeydown*/ ctx[3]);
|
||||||
|
mounted = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
p(ctx, [dirty]) {
|
p(ctx, [dirty]) {
|
||||||
if (/*$menuVisible*/ ctx[1]) {
|
if (/*$menuVisible*/ ctx[1]) {
|
||||||
@@ -1421,6 +1440,8 @@
|
|||||||
d(detaching) {
|
d(detaching) {
|
||||||
if (if_block) if_block.d(detaching);
|
if (if_block) if_block.d(detaching);
|
||||||
if (detaching) detach(if_block_anchor);
|
if (detaching) detach(if_block_anchor);
|
||||||
|
mounted = false;
|
||||||
|
dispose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1440,7 +1461,7 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
const interval = setInterval(
|
const interval = setInterval(
|
||||||
() => {
|
() => {
|
||||||
$$invalidate(3, time = new Date());
|
$$invalidate(4, time = new Date());
|
||||||
},
|
},
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
@@ -1450,33 +1471,52 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
// Backtick toggle
|
||||||
|
if (e.keyCode == 192) {
|
||||||
|
menuVisible.update(current => {
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$$self.$$.update = () => {
|
$$self.$$.update = () => {
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(4, day = time.toLocaleString("default", { weekday: "short" }));
|
$$invalidate(5, day = time.toLocaleString("default", { weekday: "short" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(5, dom = time.getDate());
|
$$invalidate(6, dom = time.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(6, mon = time.toLocaleString("default", { month: "short" }));
|
$$invalidate(7, mon = time.toLocaleString("default", { month: "short" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(7, currentTime = time.toLocaleString("en-US", {
|
$$invalidate(8, currentTime = time.toLocaleString("en-US", {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
hour12: true
|
hour12: true
|
||||||
}).toLowerCase());
|
}).toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 240) {
|
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 480) {
|
||||||
$$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`);
|
$$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [dateTimeString, $menuVisible, $trays, time, day, dom, mon, currentTime];
|
return [
|
||||||
|
dateTimeString,
|
||||||
|
$menuVisible,
|
||||||
|
$trays,
|
||||||
|
handleKeydown,
|
||||||
|
time,
|
||||||
|
day,
|
||||||
|
dom,
|
||||||
|
mon,
|
||||||
|
currentTime
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Menubar extends SvelteComponent {
|
class Menubar extends SvelteComponent {
|
||||||
@@ -1638,6 +1678,11 @@
|
|||||||
let trayLabelData = JSON.parse(updateTrayLabelJSON);
|
let trayLabelData = JSON.parse(updateTrayLabelJSON);
|
||||||
updateTrayLabel(trayLabelData);
|
updateTrayLabel(trayLabelData);
|
||||||
break
|
break
|
||||||
|
case 'D':
|
||||||
|
// Delete Tray Menu
|
||||||
|
const id = trayMessage.slice(1);
|
||||||
|
deleteTrayMenu(id);
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
log('Unknown tray message: ' + message.data);
|
log('Unknown tray message: ' + message.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wails/runtime",
|
||||||
"version": "1.3.9",
|
"version": "1.3.12",
|
||||||
"description": "Wails V2 Javascript runtime library",
|
"description": "Wails V2 Javascript runtime library",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"types": "runtime.d.ts",
|
"types": "runtime.d.ts",
|
||||||
|
|||||||
@@ -24,6 +24,15 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
// Backtick toggle
|
||||||
|
if( e.keyCode == 192 ) {
|
||||||
|
menuVisible.update( (current) => {
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $menuVisible }
|
{#if $menuVisible }
|
||||||
@@ -37,6 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window on:keydown={handleKeydown}/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.tray-menus {
|
.tray-menus {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function hideOverlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Menubar **/
|
/** Menubar **/
|
||||||
export const menuVisible = writable(true);
|
export const menuVisible = writable(false);
|
||||||
|
|
||||||
export function showMenuBar() {
|
export function showMenuBar() {
|
||||||
menuVisible.set(true);
|
menuVisible.set(true);
|
||||||
@@ -49,4 +49,16 @@ export function updateTrayLabel(tray) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deleteTrayMenu(id) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === id);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
return log("ERROR: Attempted to delete tray index ", id, "but it doesn't exist")
|
||||||
|
}
|
||||||
|
current.splice(index, 1);
|
||||||
|
return current;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export let selectedMenu = writable(null);
|
export let selectedMenu = writable(null);
|
||||||
@@ -10,7 +10,7 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
import {setTray, hideOverlay, showOverlay, updateTrayLabel} from "./store";
|
import {setTray, hideOverlay, showOverlay, updateTrayLabel, deleteTrayMenu} from "./store";
|
||||||
import {log} from "./log";
|
import {log} from "./log";
|
||||||
|
|
||||||
let websocket = null;
|
let websocket = null;
|
||||||
@@ -154,6 +154,11 @@ function handleMessage(message) {
|
|||||||
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
||||||
updateTrayLabel(trayLabelData)
|
updateTrayLabel(trayLabelData)
|
||||||
break
|
break
|
||||||
|
case 'D':
|
||||||
|
// Delete Tray Menu
|
||||||
|
const id = trayMessage.slice(1);
|
||||||
|
deleteTrayMenu(id)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
log('Unknown tray message: ' + message.data);
|
log('Unknown tray message: ' + message.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Menu interface {
|
|||||||
UpdateApplicationMenu()
|
UpdateApplicationMenu()
|
||||||
UpdateContextMenu(contextMenu *menu.ContextMenu)
|
UpdateContextMenu(contextMenu *menu.ContextMenu)
|
||||||
SetTrayMenu(trayMenu *menu.TrayMenu)
|
SetTrayMenu(trayMenu *menu.TrayMenu)
|
||||||
|
DeleteTrayMenu(trayMenu *menu.TrayMenu)
|
||||||
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
|
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,3 +40,7 @@ func (m *menuRuntime) SetTrayMenu(trayMenu *menu.TrayMenu) {
|
|||||||
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
|
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
|
||||||
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
|
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *menuRuntime) DeleteTrayMenu(trayMenu *menu.TrayMenu) {
|
||||||
|
m.bus.Publish("menu:deletetraymenu", trayMenu)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,20 +6,19 @@ import (
|
|||||||
|
|
||||||
// Runtime is a means for the user to interact with the application at runtime
|
// Runtime is a means for the user to interact with the application at runtime
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Browser Browser
|
Browser Browser
|
||||||
Events Events
|
Events Events
|
||||||
Window Window
|
Window Window
|
||||||
Dialog Dialog
|
Dialog Dialog
|
||||||
System System
|
System System
|
||||||
Menu Menu
|
Menu Menu
|
||||||
Store *StoreProvider
|
Store *StoreProvider
|
||||||
Log Log
|
Log Log
|
||||||
bus *servicebus.ServiceBus
|
bus *servicebus.ServiceBus
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new runtime
|
// New creates a new runtime
|
||||||
func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
Browser: newBrowser(),
|
Browser: newBrowser(),
|
||||||
Events: newEvents(serviceBus),
|
Events: newEvents(serviceBus),
|
||||||
@@ -36,11 +35,6 @@ func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
|||||||
|
|
||||||
// Quit the application
|
// Quit the application
|
||||||
func (r *Runtime) Quit() {
|
func (r *Runtime) Quit() {
|
||||||
// Call back to user's shutdown method if defined
|
|
||||||
if r.shutdownCallback != nil {
|
|
||||||
r.shutdownCallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start shutdown of Wails
|
// Start shutdown of Wails
|
||||||
r.bus.Publish("quit", "runtime.Quit()")
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,25 +26,20 @@ type Manager struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
// The shutdown callback to notify the user's app that a shutdown
|
|
||||||
// has started
|
|
||||||
shutdownCallback func()
|
|
||||||
|
|
||||||
// Parent waitgroup
|
// Parent waitgroup
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new signal manager
|
// NewManager creates a new signal manager
|
||||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger, shutdownCallback func()) (*Manager, error) {
|
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||||
|
|
||||||
result := &Manager{
|
result := &Manager{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
logger: logger.CustomLogger("Event Manager"),
|
logger: logger.CustomLogger("Event Manager"),
|
||||||
signalchannel: make(chan os.Signal, 2),
|
signalchannel: make(chan os.Signal, 2),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
shutdownCallback: shutdownCallback,
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -67,11 +62,6 @@ func (m *Manager) Start() {
|
|||||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||||
m.bus.Publish("quit", "ctrl-c pressed")
|
m.bus.Publish("quit", "ctrl-c pressed")
|
||||||
|
|
||||||
// Shutdown app first
|
|
||||||
if m.shutdownCallback != nil {
|
|
||||||
m.shutdownCallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start shutdown of Wails
|
// Start shutdown of Wails
|
||||||
m.cancel()
|
m.cancel()
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ func (m *Menu) Start() error {
|
|||||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||||
menuMessageType := splitTopic[1]
|
menuMessageType := splitTopic[1]
|
||||||
switch menuMessageType {
|
switch menuMessageType {
|
||||||
|
case "ontrayopen":
|
||||||
|
trayID := menuMessage.Data().(string)
|
||||||
|
m.menuManager.OnTrayMenuOpen(trayID)
|
||||||
|
case "ontrayclose":
|
||||||
|
trayID := menuMessage.Data().(string)
|
||||||
|
m.menuManager.OnTrayMenuClose(trayID)
|
||||||
case "clicked":
|
case "clicked":
|
||||||
if len(splitTopic) != 2 {
|
if len(splitTopic) != 2 {
|
||||||
m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
|
m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
|
||||||
@@ -137,6 +143,17 @@ func (m *Menu) Start() error {
|
|||||||
// Notify frontend of menu change
|
// Notify frontend of menu change
|
||||||
m.bus.Publish("menufrontend:settraymenu", updatedMenu)
|
m.bus.Publish("menufrontend:settraymenu", updatedMenu)
|
||||||
|
|
||||||
|
case "deletetraymenu":
|
||||||
|
trayMenu := menuMessage.Data().(*menu.TrayMenu)
|
||||||
|
trayID, err := m.menuManager.GetTrayID(trayMenu)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Trace("%s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify frontend of menu change
|
||||||
|
m.bus.Publish("menufrontend:deletetraymenu", trayID)
|
||||||
|
|
||||||
case "updatetraymenulabel":
|
case "updatetraymenulabel":
|
||||||
trayMenu := menuMessage.Data().(*menu.TrayMenu)
|
trayMenu := menuMessage.Data().(*menu.TrayMenu)
|
||||||
updatedLabel, err := m.menuManager.UpdateTrayMenuLabel(trayMenu)
|
updatedLabel, err := m.menuManager.UpdateTrayMenuLabel(trayMenu)
|
||||||
|
|||||||
@@ -34,10 +34,13 @@ type Runtime struct {
|
|||||||
|
|
||||||
// Startup Hook
|
// Startup Hook
|
||||||
startupOnce sync.Once
|
startupOnce sync.Once
|
||||||
|
|
||||||
|
// Service bus
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime creates a new runtime subsystem
|
// NewRuntime creates a new runtime subsystem
|
||||||
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime)) (*Runtime, error) {
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Subscribe to log messages
|
||||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||||
@@ -52,13 +55,13 @@ func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
runtimeChannel: runtimeChannel,
|
runtimeChannel: runtimeChannel,
|
||||||
hooksChannel: hooksChannel,
|
hooksChannel: hooksChannel,
|
||||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||||
runtime: runtime.New(bus, shutdownCallback),
|
runtime: runtime.New(bus),
|
||||||
startupCallback: startupCallback,
|
startupCallback: startupCallback,
|
||||||
shutdownCallback: shutdownCallback,
|
ctx: ctx,
|
||||||
ctx: ctx,
|
bus: bus,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -80,7 +83,15 @@ func (r *Runtime) Start() error {
|
|||||||
case "startup":
|
case "startup":
|
||||||
if r.startupCallback != nil {
|
if r.startupCallback != nil {
|
||||||
r.startupOnce.Do(func() {
|
r.startupOnce.Do(func() {
|
||||||
go r.startupCallback(r.runtime)
|
go func() {
|
||||||
|
r.startupCallback(r.runtime)
|
||||||
|
|
||||||
|
// If we got a url, publish it now startup completed
|
||||||
|
url, ok := hooksMessage.Data().(string)
|
||||||
|
if ok && len(url) > 0 {
|
||||||
|
r.bus.Publish("url:handler", url)
|
||||||
|
}
|
||||||
|
}()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
r.logger.Warning("no startup callback registered!")
|
r.logger.Warning("no startup callback registered!")
|
||||||
|
|||||||
98
v2/internal/subsystem/url.go
Normal file
98
v2/internal/subsystem/url.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package subsystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// URL is the URL Handler subsystem. It handles messages with topics starting
|
||||||
|
// with "url:"
|
||||||
|
type URL struct {
|
||||||
|
urlChannel <-chan *servicebus.Message
|
||||||
|
|
||||||
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
|
// Logger!
|
||||||
|
logger *logger.Logger
|
||||||
|
|
||||||
|
// Context for shutdown
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// internal waitgroup
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
handlers map[string]func(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewURL creates a new log subsystem
|
||||||
|
func NewURL(bus *servicebus.ServiceBus, logger *logger.Logger, handlers map[string]func(string)) (*URL, error) {
|
||||||
|
|
||||||
|
// Subscribe to log messages
|
||||||
|
urlChannel, err := bus.Subscribe("url")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
result := &URL{
|
||||||
|
urlChannel: urlChannel,
|
||||||
|
logger: logger,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
handlers: handlers,
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the subsystem
|
||||||
|
func (u *URL) Start() error {
|
||||||
|
|
||||||
|
u.wg.Add(1)
|
||||||
|
|
||||||
|
// Spin off a go routine
|
||||||
|
go func() {
|
||||||
|
defer u.logger.Trace("URL Shutdown")
|
||||||
|
|
||||||
|
for u.shouldQuit == false {
|
||||||
|
select {
|
||||||
|
case <-u.ctx.Done():
|
||||||
|
u.wg.Done()
|
||||||
|
return
|
||||||
|
case urlMessage := <-u.urlChannel:
|
||||||
|
// Guard against nil messages
|
||||||
|
if urlMessage == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
messageType := strings.TrimPrefix(urlMessage.Topic(), "url:")
|
||||||
|
switch messageType {
|
||||||
|
case "handler":
|
||||||
|
url := urlMessage.Data().(string)
|
||||||
|
splitURL := strings.Split(url, ":")
|
||||||
|
protocol := splitURL[0]
|
||||||
|
callback, ok := u.handlers[protocol]
|
||||||
|
if ok {
|
||||||
|
go callback(url)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
u.logger.Error("unknown url message: %+v", urlMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *URL) Close() {
|
||||||
|
u.cancel()
|
||||||
|
u.wg.Wait()
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,20 +12,19 @@ type Basic struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newBasic creates a new Basic application struct
|
// newBasic creates a new Basic application struct
|
||||||
func newBasic() *Basic {
|
func NewBasic() *Basic {
|
||||||
return &Basic{}
|
return &Basic{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WailsInit is called at application startup
|
// startup is called at application startup
|
||||||
func (b *Basic) WailsInit(runtime *wails.Runtime) error {
|
func (b *Basic) startup(runtime *wails.Runtime) {
|
||||||
// Perform your setup here
|
// Perform your setup here
|
||||||
b.runtime = runtime
|
b.runtime = runtime
|
||||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WailsShutdown is called at application termination
|
// shutdown is called at application termination
|
||||||
func (b *Basic) WailsShutdown() {
|
func (b *Basic) shutdown() {
|
||||||
// Perform your teardown here
|
// Perform your teardown here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<link rel="stylesheet" href="/main.css">
|
<link rel="stylesheet" href="/main.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body data-wails-drag>
|
||||||
<div id="logo"></div>
|
<div id="logo"></div>
|
||||||
<div id="input">
|
<div id="input">
|
||||||
<input id="name" type="text"></input>
|
<input id="name" type="text"></input>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,21 +1,38 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
app, err := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
app := NewBasic()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Bind(newBasic())
|
err := wails.Run(&options.App{
|
||||||
|
Title: "{{.ProjectName}}",
|
||||||
err = app.Run()
|
Width: 800,
|
||||||
|
Height: 600,
|
||||||
|
DisableResize: true,
|
||||||
|
Mac: &mac.Options{
|
||||||
|
WebviewIsTransparent: true,
|
||||||
|
WindowBackgroundIsTranslucent: true,
|
||||||
|
TitleBar: mac.TitleBarHiddenInset(),
|
||||||
|
Menu: menu.DefaultMacMenu(),
|
||||||
|
},
|
||||||
|
LogLevel: logger.DEBUG,
|
||||||
|
Startup: app.startup,
|
||||||
|
Shutdown: app.shutdown,
|
||||||
|
Bind: []interface{}{
|
||||||
|
app,
|
||||||
|
},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLILogger is used by the cli
|
// CLILogger is used by the cli
|
||||||
@@ -51,9 +53,9 @@ func (c *CLILogger) Println(message string, args ...interface{}) {
|
|||||||
// Fatal prints the given message then aborts
|
// Fatal prints the given message then aborts
|
||||||
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
||||||
temp := fmt.Sprintf(message, args...)
|
temp := fmt.Sprintf(message, args...)
|
||||||
_, err := fmt.Fprintln(c.Writer, "FATAL: "+temp)
|
_, err := fmt.Fprintln(c.Writer, colour.Red("FATAL: "+temp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("FATAL: ", err)
|
println(colour.Red("FATAL: " + err.Error()))
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,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,9 +146,28 @@ 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 {
|
||||||
|
|
||||||
|
// Run go mod tidy first
|
||||||
|
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Default go build command
|
// Default go build command
|
||||||
commands := slicer.String([]string{"build"})
|
commands := slicer.String([]string{"build"})
|
||||||
|
|
||||||
@@ -160,6 +183,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
// potentially try and see if the assets have changed but will
|
// potentially try and see if the assets have changed but will
|
||||||
// this take as much time as a `-a` build?
|
// this take as much time as a `-a` build?
|
||||||
commands.Add("-a")
|
commands.Add("-a")
|
||||||
|
commands.Add("-x")
|
||||||
|
|
||||||
var tags slicer.StringSlicer
|
var tags slicer.StringSlicer
|
||||||
tags.Add(options.OutputType)
|
tags.Add(options.OutputType)
|
||||||
@@ -188,9 +212,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 != "" {
|
||||||
@@ -199,10 +225,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)
|
||||||
@@ -211,14 +234,14 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
options.CompiledBinary = compiledBinary
|
options.CompiledBinary = compiledBinary
|
||||||
|
|
||||||
// Create the command
|
// Create the command
|
||||||
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd = exec.Command(options.Compiler, commands.AsSlice()...)
|
||||||
|
if options.Verbosity == VERBOSE {
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
}
|
||||||
// Set the directory
|
// Set the directory
|
||||||
cmd.Dir = b.projectData.Path
|
cmd.Dir = b.projectData.Path
|
||||||
|
|
||||||
// Set GO111MODULE environment variable
|
|
||||||
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
|
||||||
|
|
||||||
// Add CGO flags
|
// Add CGO flags
|
||||||
// We use the project/build dir as a temporary place for our generated c headers
|
// We use the project/build dir as a temporary place for our generated c headers
|
||||||
buildBaseDir, err := fs.RelativeToCwd("build")
|
buildBaseDir, err := fs.RelativeToCwd("build")
|
||||||
@@ -226,31 +249,47 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("CGO_CFLAGS=-I%s", buildBaseDir))
|
cmd.Env = os.Environ() // inherit env
|
||||||
|
|
||||||
// Setup buffers
|
// Use upsertEnv so we don't overwrite user's CGO_CFLAGS
|
||||||
var stdo, stde bytes.Buffer
|
cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
|
||||||
cmd.Stdout = &stdo
|
if v != "" {
|
||||||
cmd.Stderr = &stde
|
v += " "
|
||||||
|
}
|
||||||
|
v += "-I" + buildBaseDir
|
||||||
|
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"
|
||||||
|
})
|
||||||
|
|
||||||
// Run command
|
// Run command
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
|
|
||||||
// Format error if we have one
|
// Format error if we have one
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
@@ -292,7 +331,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)
|
||||||
}
|
}
|
||||||
@@ -341,31 +380,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("\tCommand: " + 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("\tCommand: '" + 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") {
|
||||||
@@ -375,7 +423,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
|
||||||
@@ -384,3 +437,22 @@ func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) {
|
|||||||
// Read in html
|
// Read in html
|
||||||
return html.NewAssetBundle(b.projectData.HTML)
|
return html.NewAssetBundle(b.projectData.HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func upsertEnv(env []string, key string, update func(v string) string) []string {
|
||||||
|
newEnv := make([]string, len(env), len(env)+1)
|
||||||
|
found := false
|
||||||
|
for i := range env {
|
||||||
|
if strings.HasPrefix(env[i], key+"=") {
|
||||||
|
eqIndex := strings.Index(env[i], "=")
|
||||||
|
val := env[i][eqIndex+1:]
|
||||||
|
newEnv[i] = fmt.Sprintf("%s=%v", key, update(val))
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newEnv[i] = env[i]
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update("")))
|
||||||
|
}
|
||||||
|
return newEnv
|
||||||
|
}
|
||||||
|
|||||||
31
v2/pkg/commands/build/base_test.go
Normal file
31
v2/pkg/commands/build/base_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUpdateEnv(t *testing.T) {
|
||||||
|
|
||||||
|
env := []string{"one=1", "two=a=b", "three="}
|
||||||
|
newEnv := upsertEnv(env, "two", func(v string) string {
|
||||||
|
return v + "+added"
|
||||||
|
})
|
||||||
|
newEnv = upsertEnv(newEnv, "newVar", func(v string) string {
|
||||||
|
return "added"
|
||||||
|
})
|
||||||
|
newEnv = upsertEnv(newEnv, "three", func(v string) string {
|
||||||
|
return "3"
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(newEnv) != 4 {
|
||||||
|
t.Errorf("expected: 4, got: %d", len(newEnv))
|
||||||
|
}
|
||||||
|
if newEnv[1] != "two=a=b+added" {
|
||||||
|
t.Errorf("expected: \"two=a=b+added\", got: %q", newEnv[1])
|
||||||
|
}
|
||||||
|
if newEnv[2] != "three=3" {
|
||||||
|
t.Errorf("expected: \"three=3\", got: %q", newEnv[2])
|
||||||
|
}
|
||||||
|
if newEnv[3] != "newVar=added" {
|
||||||
|
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,19 +28,23 @@ 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
|
||||||
|
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)
|
||||||
|
AppleIdentity string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModeAsString returns the current mode as a string
|
// GetModeAsString returns the current mode as a string
|
||||||
@@ -57,12 +64,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 {
|
||||||
@@ -70,7 +71,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
|
||||||
@@ -106,7 +107,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
|
||||||
@@ -114,30 +114,62 @@ 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
|
||||||
|
err = builder.CompileProject(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Build arm64
|
||||||
|
options.Arch = "arm64"
|
||||||
|
options.OutputFile = arm64Filename
|
||||||
|
err = builder.CompileProject(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Run lipo
|
||||||
|
_, 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
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -52,6 +54,14 @@ func packageApplication(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sign app if needed
|
||||||
|
if options.AppleIdentity != "" {
|
||||||
|
err = signApplication(options)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,3 +186,21 @@ func processApplicationIcon(resourceDir string, iconsDir string) (err error) {
|
|||||||
}()
|
}()
|
||||||
return icns.Encode(dest, srcImg)
|
return icns.Encode(dest, srcImg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func signApplication(options *Options) error {
|
||||||
|
bundlename := filepath.Join(options.BuildDirectory, options.ProjectData.Name+".app")
|
||||||
|
identity := fmt.Sprintf(`"%s"`, options.AppleIdentity)
|
||||||
|
cmd := exec.Command("codesign", "--sign", identity, "--deep", "--force", "--verbose", "--timestamp", "--options", "runtime", bundlename)
|
||||||
|
var stdo, stde bytes.Buffer
|
||||||
|
cmd.Stdout = &stdo
|
||||||
|
cmd.Stderr = &stde
|
||||||
|
|
||||||
|
// Run command
|
||||||
|
err := cmd.Run()
|
||||||
|
|
||||||
|
// Format error if we have one
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
66
v2/pkg/logger/filelogger.go
Normal file
66
v2/pkg/logger/filelogger.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileLogger is a utility to log messages to a number of destinations
|
||||||
|
type FileLogger struct {
|
||||||
|
filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileLogger creates a new Logger.
|
||||||
|
func NewFileLogger(filename string) Logger {
|
||||||
|
return &FileLogger{
|
||||||
|
filename: filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print works like Sprintf.
|
||||||
|
func (l *FileLogger) Print(message string) {
|
||||||
|
f, err := os.OpenFile(l.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := f.WriteString(message); err != nil {
|
||||||
|
f.Close()
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *FileLogger) Println(message string) {
|
||||||
|
l.Print(message + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Trace(message string) {
|
||||||
|
l.Println("TRACE | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Debug(message string) {
|
||||||
|
l.Println("DEBUG | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Info(message string) {
|
||||||
|
l.Println("INFO | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Warning(message string) {
|
||||||
|
l.Println("WARN | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Error(message string) {
|
||||||
|
l.Println("ERROR | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Fatal(message string) {
|
||||||
|
l.Println("FATAL | " + message)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
51
v2/pkg/mac/mac.go
Normal file
51
v2/pkg/mac/mac.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// build
|
||||||
|
package mac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 startup")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartsAtLogin() (bool, error) {
|
||||||
|
exe, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
binName := filepath.Base(exe)
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,21 +1,44 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// Modifier is actually a string
|
// Modifier is actually a string
|
||||||
type Modifier string
|
type Modifier string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CmdOrCtrlKey represents Command on Mac and Control on other platforms
|
// CmdOrCtrlKey represents Command on Mac and Control on other platforms
|
||||||
CmdOrCtrlKey Modifier = "CmdOrCtrl"
|
CmdOrCtrlKey Modifier = "cmdorctrl"
|
||||||
// OptionOrAltKey represents Option on Mac and Alt on other platforms
|
// OptionOrAltKey represents Option on Mac and Alt on other platforms
|
||||||
OptionOrAltKey Modifier = "OptionOrAlt"
|
OptionOrAltKey Modifier = "optionoralt"
|
||||||
// ShiftKey represents the shift key on all systems
|
// ShiftKey represents the shift key on all systems
|
||||||
ShiftKey Modifier = "Shift"
|
ShiftKey Modifier = "shift"
|
||||||
// SuperKey represents Command on Mac and the Windows key on the other platforms
|
// SuperKey represents Command on Mac and the Windows key on the other platforms
|
||||||
SuperKey Modifier = "Super"
|
SuperKey Modifier = "super"
|
||||||
// ControlKey represents the control key on all systems
|
// ControlKey represents the control key on all systems
|
||||||
ControlKey Modifier = "Control"
|
ControlKey Modifier = "ctrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var modifierMap = map[string]Modifier{
|
||||||
|
"cmdorctrl": CmdOrCtrlKey,
|
||||||
|
"optionoralt": OptionOrAltKey,
|
||||||
|
"shift": ShiftKey,
|
||||||
|
"super": SuperKey,
|
||||||
|
"ctrl": ControlKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseModifier(text string) (*Modifier, error) {
|
||||||
|
lowertext := strings.ToLower(text)
|
||||||
|
result, valid := modifierMap[lowertext]
|
||||||
|
if !valid {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid modifier", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Accelerator holds the keyboard shortcut for a menu item
|
// Accelerator holds the keyboard shortcut for a menu item
|
||||||
type Accelerator struct {
|
type Accelerator struct {
|
||||||
Key string
|
Key string
|
||||||
@@ -25,14 +48,14 @@ type Accelerator struct {
|
|||||||
// Key creates a standard key Accelerator
|
// Key creates a standard key Accelerator
|
||||||
func Key(key string) *Accelerator {
|
func Key(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator
|
// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator
|
||||||
func CmdOrCtrl(key string) *Accelerator {
|
func CmdOrCtrl(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{CmdOrCtrlKey},
|
Modifiers: []Modifier{CmdOrCtrlKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +63,7 @@ func CmdOrCtrl(key string) *Accelerator {
|
|||||||
// OptionOrAlt creates a 'OptionOrAlt' Accelerator
|
// OptionOrAlt creates a 'OptionOrAlt' Accelerator
|
||||||
func OptionOrAlt(key string) *Accelerator {
|
func OptionOrAlt(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{OptionOrAltKey},
|
Modifiers: []Modifier{OptionOrAltKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +71,7 @@ func OptionOrAlt(key string) *Accelerator {
|
|||||||
// Shift creates a 'Shift' Accelerator
|
// Shift creates a 'Shift' Accelerator
|
||||||
func Shift(key string) *Accelerator {
|
func Shift(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{ShiftKey},
|
Modifiers: []Modifier{ShiftKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +79,7 @@ func Shift(key string) *Accelerator {
|
|||||||
// Control creates a 'Control' Accelerator
|
// Control creates a 'Control' Accelerator
|
||||||
func Control(key string) *Accelerator {
|
func Control(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{ControlKey},
|
Modifiers: []Modifier{ControlKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +87,7 @@ func Control(key string) *Accelerator {
|
|||||||
// Super creates a 'Super' Accelerator
|
// Super creates a 'Super' Accelerator
|
||||||
func Super(key string) *Accelerator {
|
func Super(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{SuperKey},
|
Modifiers: []Modifier{SuperKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
90
v2/pkg/menu/keys/parser.go
Normal file
90
v2/pkg/menu/keys/parser.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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"})
|
||||||
|
|
||||||
|
func parseKey(key string) (string, bool) {
|
||||||
|
|
||||||
|
// Lowercase!
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
|
||||||
|
// Check special case
|
||||||
|
if key == "plus" {
|
||||||
|
return "+", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle named keys
|
||||||
|
if namedKeys.Contains(key) {
|
||||||
|
return key, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we only have a single character
|
||||||
|
if len(key) != 1 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
runeKey := rune(key[0])
|
||||||
|
|
||||||
|
// This may be too inclusive
|
||||||
|
if strconv.IsPrint(runeKey) {
|
||||||
|
return key, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(shortcut string) (*Accelerator, error) {
|
||||||
|
|
||||||
|
var result Accelerator
|
||||||
|
|
||||||
|
// Split the shortcut by +
|
||||||
|
components := strings.Split(shortcut, "+")
|
||||||
|
|
||||||
|
// If we only have one it should be a key
|
||||||
|
// We require components
|
||||||
|
if len(components) == 0 {
|
||||||
|
return nil, fmt.Errorf("no components given to validateComponents")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of modifiers we have processed
|
||||||
|
var modifiersProcessed slicer.StringSlicer
|
||||||
|
|
||||||
|
// Check components
|
||||||
|
for index, component := range components {
|
||||||
|
|
||||||
|
// If last component
|
||||||
|
if index == len(components)-1 {
|
||||||
|
processedkey, validKey := parseKey(component)
|
||||||
|
if !validKey {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid key", component)
|
||||||
|
}
|
||||||
|
result.Key = processedkey
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not last component - needs to be modifier
|
||||||
|
lowercaseComponent := strings.ToLower(component)
|
||||||
|
thisModifier, valid := modifierMap[lowercaseComponent]
|
||||||
|
if !valid {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid modifier", component)
|
||||||
|
}
|
||||||
|
// Needs to be unique
|
||||||
|
if modifiersProcessed.Contains(lowercaseComponent) {
|
||||||
|
return nil, fmt.Errorf("Modifier '%s' is defined twice for shortcut: %s", component, shortcut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save this data
|
||||||
|
result.Modifiers = append(result.Modifiers, thisModifier)
|
||||||
|
modifiersProcessed.Add(lowercaseComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
38
v2/pkg/menu/keys/parser_test.go
Normal file
38
v2/pkg/menu/keys/parser_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
|
||||||
|
i := is.New(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
Input string
|
||||||
|
Expected *Accelerator
|
||||||
|
}
|
||||||
|
|
||||||
|
gooddata := []args{
|
||||||
|
{"CmdOrCtrl+A", CmdOrCtrl("A")},
|
||||||
|
{"SHIFT+.", Shift(".")},
|
||||||
|
{"CTRL+plus", Control("+")},
|
||||||
|
{"CTRL+SHIFT+escApe", Combo("escape", ControlKey, ShiftKey)},
|
||||||
|
{";", Key(";")},
|
||||||
|
{"Super+Tab", Super("tab")},
|
||||||
|
{"OptionOrAlt+Page Down", OptionOrAlt("Page Down")},
|
||||||
|
}
|
||||||
|
for _, tt := range gooddata {
|
||||||
|
result, err := Parse(tt.Input)
|
||||||
|
i.NoErr(err)
|
||||||
|
i.Equal(result, tt.Expected)
|
||||||
|
}
|
||||||
|
baddata := []string{"CmdOrCrl+A", "SHIT+.", "CTL+plus", "CTRL+SHIF+esApe", "escap", "Sper+Tab", "OptionOrAlt"}
|
||||||
|
for _, d := range baddata {
|
||||||
|
result, err := Parse(d)
|
||||||
|
i.True(err != nil)
|
||||||
|
i.Equal(result, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ type MenuItem struct {
|
|||||||
// Callback function when menu clicked
|
// Callback function when menu clicked
|
||||||
Click Callback `json:"-"`
|
Click Callback `json:"-"`
|
||||||
|
|
||||||
// Colour
|
// Text Colour
|
||||||
RGBA string
|
RGBA string
|
||||||
|
|
||||||
// Font
|
// Font
|
||||||
@@ -39,9 +39,12 @@ type MenuItem struct {
|
|||||||
// Image - base64 image data
|
// Image - base64 image data
|
||||||
Image string
|
Image string
|
||||||
|
|
||||||
// MacTemplateImage indicates that on a mac, this image is a template image
|
// MacTemplateImage indicates that on a Mac, this image is a template image
|
||||||
MacTemplateImage bool
|
MacTemplateImage bool
|
||||||
|
|
||||||
|
// MacAlternate indicates that this item is an alternative to the previous menu item
|
||||||
|
MacAlternate bool
|
||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
Tooltip string
|
Tooltip string
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,38 @@ type TrayMenu struct {
|
|||||||
// Label is the text we wish to display in the tray
|
// Label is the text we wish to display in the tray
|
||||||
Label string
|
Label string
|
||||||
|
|
||||||
// Icon is the name of the tray icon we wish to display.
|
// Image is the name of the tray icon we wish to display.
|
||||||
// These are read up during build from <projectdir>/trayicons and
|
// These are read up during build from <projectdir>/trayicons and
|
||||||
// the filenames are used as IDs, minus the extension
|
// the filenames are used as IDs, minus the extension
|
||||||
// EG: <projectdir>/trayicons/main.png can be referenced here with "main"
|
// EG: <projectdir>/trayicons/main.png can be referenced here with "main"
|
||||||
Icon string
|
// If the image is not a filename, it will be treated as base64 image data
|
||||||
|
Image string
|
||||||
|
|
||||||
|
// MacTemplateImage indicates that on a Mac, this image is a template image
|
||||||
|
MacTemplateImage bool
|
||||||
|
|
||||||
|
// Text Colour
|
||||||
|
RGBA string
|
||||||
|
|
||||||
|
// Font
|
||||||
|
FontSize int
|
||||||
|
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
|
||||||
|
|
||||||
|
// OnOpen is called when the Menu is opened
|
||||||
|
OnOpen func()
|
||||||
|
|
||||||
|
// OnClose is called when the Menu is closed
|
||||||
|
OnClose func()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ package mac
|
|||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/pkg/menu"
|
import "github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
|
||||||
|
type ActivationPolicy int
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSApplicationActivationPolicyRegular ActivationPolicy = 0
|
||||||
|
NSApplicationActivationPolicyAccessory ActivationPolicy = 1
|
||||||
|
NSApplicationActivationPolicyProhibited ActivationPolicy = 2
|
||||||
|
)
|
||||||
|
|
||||||
// Options are options specific to Mac
|
// Options are options specific to Mac
|
||||||
type Options struct {
|
type Options struct {
|
||||||
TitleBar *TitleBar
|
TitleBar *TitleBar
|
||||||
@@ -11,4 +19,6 @@ type Options struct {
|
|||||||
Menu *menu.Menu
|
Menu *menu.Menu
|
||||||
TrayMenus []*menu.TrayMenu
|
TrayMenus []*menu.TrayMenu
|
||||||
ContextMenus []*menu.ContextMenu
|
ContextMenus []*menu.ContextMenu
|
||||||
|
ActivationPolicy ActivationPolicy
|
||||||
|
URLHandlers map[string]func(string)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user