mirror of
https://github.com/taigrr/wails.git
synced 2026-04-04 14:12:40 -07:00
Compare commits
1 Commits
v2.0.0-alp
...
feature/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29715b2d57 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -27,7 +27,3 @@ v2/pkg/parser/testproject/frontend/wails
|
||||
v2/test/kitchensink/frontend/public
|
||||
v2/test/kitchensink/build/darwin/desktop/kitchensink
|
||||
v2/test/kitchensink/frontend/package.json.md5
|
||||
/v2/internal/ffenestri/windows/test/cmake-build-debug/
|
||||
!v2/internal/ffenestri/windows/x64/webview2.dll
|
||||
!v2/internal/ffenestri/windows/x64/WebView2Loader.dll
|
||||
.idea/
|
||||
|
||||
@@ -40,5 +40,4 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
||||
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
||||
* [misitebao](https://github.com/misitebao)
|
||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||
* [Amaury Tobias Quiroz](https://github.com/amaury-tobias)
|
||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "**** Checking if Wails passes unit tests ****"
|
||||
if ! go test ./lib/... ./runtime/... ./cmd/...
|
||||
if ! go test ./...
|
||||
then
|
||||
echo ""
|
||||
echo "ERROR: Unit tests failed!"
|
||||
|
||||
13
v2/.vscode/settings.json
vendored
Normal file
13
v2/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"ios": "c",
|
||||
"typeinfo": "c",
|
||||
"sstream": "c",
|
||||
"__functional_03": "c",
|
||||
"functional": "c",
|
||||
"__locale": "c",
|
||||
"locale": "c",
|
||||
"chrono": "c",
|
||||
"system_error": "c"
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
# Build
|
||||
|
||||
The build command processes the Wails project and generates an application binary.
|
||||
|
||||
## Usage
|
||||
|
||||
`wails build <flags>`
|
||||
|
||||
### Flags
|
||||
|
||||
| Flag | Details | Default |
|
||||
| :------------- | :----------- | :------ |
|
||||
| -clean | Clean the bin directory before building | |
|
||||
| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go |
|
||||
| -ldflags "custom ld flags" | Use given ldflags | |
|
||||
| -o path/to/binary | Compile to given path/filename | |
|
||||
| -k | Keep generated assets | |
|
||||
| -package | Create a platform specific package | |
|
||||
| -production | Compile in production mode: -ldflags="-w -s" + "-h windows" on Windows | |
|
||||
| -tags | Build tags to pass to Go compiler (quoted and space separated) | |
|
||||
| -upx | Compress final binary with UPX (if installed) | |
|
||||
| -upxflags "custom flags" | Flags to pass to upx | |
|
||||
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
||||
| -delve | If true, runs delve on the compiled binary | false |
|
||||
|
||||
## The Build Process
|
||||
|
||||
The build process is as follows:
|
||||
|
||||
- The flags are processed, and an Options struct built containing the build context.
|
||||
- The type of target is determined, and a custom build process is followed for target.
|
||||
|
||||
### Desktop Target
|
||||
|
||||
- The frontend dependencies are installed. The command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored.
|
||||
- The frontend is then built. This command is read from the project file `wails.json` under the key `frontend:install` and executed in the `frontend` directory. If this is not defined, it is ignored.
|
||||
- The project directory is checked to see if the `build` directory exists. If not, it is created and default project assets are copied to it.
|
||||
- An asset bundle is then created by reading the `html` key from `wails.json` and loading the referenced file. This is then parsed, looking for local Javascript and CSS references. Those files are in turn loaded into memory, converted to C data and saved into the asset bundle located at `build/assets.h`, which also includes the original HTML.
|
||||
- The application icon is then processed: if there is no `build/appicon.png`, a default icon is copied. On Windows, an `app.ico` file is generated from this png. On Mac, `icons.icns` is generated.
|
||||
- If there are icons in the `build/tray` directory, these are processed, converted to C data and saved as `build/trayicons.h`, ready for the compilation step.
|
||||
- If there are icons in the `build/dialog` directory, these are processed, converted to C data and saved as `build/userdialogicons.h`, ready for the compilation step.
|
||||
- If the `-package` flag is given for a Windows target, the Windows assets in the `build/windows` directory are processed: manifest + icons compiled to a `.syso` file (deleted after compilation).
|
||||
- If we are building a universal binary for Mac, the application is compiled for both `arm64` and `amd64`. The `lipo` tool is then executed to create the universal binary.
|
||||
- If we are not building a universal binary for Mac, the application is built using `go build`, using build tags to indicate type of application and build mode (debug/production).
|
||||
- If the `-upx` flag was provided, `upx` is invoked to compress the binary. Custom flags may be provided using the `-upxflags` flag.
|
||||
- If the `package` flag is given for a non Windows target, the application is bundled for the platform. On Mac, this creates a `.app` with the processed icons, the `Info.plist` in `build/darwin` and the compiled binary.
|
||||
|
||||
|
||||
@@ -3,17 +3,10 @@ package build
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/system"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
@@ -29,6 +22,10 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
command := app.NewSubCommand("build", "Builds the application")
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
// Setup production flag
|
||||
production := false
|
||||
command.BoolFlag("production", "Build in production mode", &production)
|
||||
@@ -40,50 +37,28 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
compress := false
|
||||
command.BoolFlag("upx", "Compress final binary with UPX (if installed)", &compress)
|
||||
|
||||
compressFlags := ""
|
||||
command.StringFlag("upxflags", "Flags to pass to upx", &compressFlags)
|
||||
|
||||
// Setup Platform flag
|
||||
platform := runtime.GOOS
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Verbosity
|
||||
verbosity := 1
|
||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
||||
// Quiet Build
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
|
||||
// ldflags to pass to `go`
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// tags to pass to `go`
|
||||
tags := ""
|
||||
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
|
||||
// Log to file
|
||||
logFile := ""
|
||||
command.StringFlag("l", "Log to file", &logFile)
|
||||
|
||||
// Retain assets
|
||||
keepAssets := false
|
||||
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)
|
||||
|
||||
webview2 := "download"
|
||||
command.StringFlag("webview2", "WebView2 installer strategy: download,embed,browser,error.", &webview2)
|
||||
|
||||
runDelve := false
|
||||
command.BoolFlag("delve", "Runs the built binary in delve for debugging", &runDelve)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
quiet := verbosity == 0
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
@@ -97,133 +72,28 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
if production {
|
||||
mode = build.Production
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
"linux",
|
||||
//"linux/amd64",
|
||||
//"linux/arm-7",
|
||||
"windows",
|
||||
"windows/amd64",
|
||||
})
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
return fmt.Errorf("platform %s is not supported", platform)
|
||||
}
|
||||
|
||||
if compress && platform == "darwin/universal" {
|
||||
println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
||||
compress = false
|
||||
}
|
||||
|
||||
// Lookup compiler path
|
||||
compilerPath, err := exec.LookPath(compilerCommand)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to find compiler: %s", compilerCommand)
|
||||
}
|
||||
|
||||
// Tags
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
}
|
||||
|
||||
// Webview2 installer strategy (download by default)
|
||||
wv2rtstrategy := ""
|
||||
webview2 = strings.ToLower(webview2)
|
||||
if webview2 != "" {
|
||||
validWV2Runtime := slicer.String([]string{"download", "embed", "browser", "error"})
|
||||
if !validWV2Runtime.Contains(webview2) {
|
||||
return fmt.Errorf("invalid option for flag 'webview2': %s", webview2)
|
||||
}
|
||||
// These are the build tags associated with the strategies
|
||||
switch webview2 {
|
||||
case "embed":
|
||||
wv2rtstrategy = "wv2runtime.embed"
|
||||
case "error":
|
||||
wv2rtstrategy = "wv2runtime.error"
|
||||
case "browser":
|
||||
wv2rtstrategy = "wv2runtime.browser"
|
||||
}
|
||||
}
|
||||
|
||||
// If we want to use delve we need to compile in DEBUG mode
|
||||
if runDelve {
|
||||
mode = build.Debug
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
OutputFile: outputFilename,
|
||||
CleanBuildDirectory: cleanBuildDirectory,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: keepAssets,
|
||||
Verbosity: verbosity,
|
||||
Compress: compress,
|
||||
CompressFlags: compressFlags,
|
||||
UserTags: userTags,
|
||||
WebView2Strategy: wv2rtstrategy,
|
||||
RunDelve: runDelve,
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
Platform: platform,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: keepAssets,
|
||||
}
|
||||
|
||||
// Calculate platform and arch
|
||||
platformSplit := strings.Split(platform, "/")
|
||||
buildOptions.Platform = platformSplit[0]
|
||||
if system.IsAppleSilicon() {
|
||||
buildOptions.Arch = "arm64"
|
||||
} else {
|
||||
buildOptions.Arch = runtime.GOARCH
|
||||
}
|
||||
if len(platformSplit) == 2 {
|
||||
buildOptions.Arch = platformSplit[1]
|
||||
}
|
||||
|
||||
println("Build arch =", buildOptions.Arch)
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
buildModeText := "debug"
|
||||
if production {
|
||||
buildModeText = "production"
|
||||
}
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
||||
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
||||
fmt.Fprintf(w, "Compiler: \t%s\n", compilerPath)
|
||||
fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||
fmt.Fprintf(w, "Build Mode: \t%s\n", buildModeText)
|
||||
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
||||
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
||||
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||
fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
|
||||
if len(buildOptions.OutputFile) > 0 {
|
||||
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
w.Flush()
|
||||
|
||||
return doBuild(buildOptions)
|
||||
})
|
||||
}
|
||||
@@ -245,40 +115,5 @@ func doBuild(buildOptions *build.Options) error {
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||
buildOptions.Logger.Println("")
|
||||
|
||||
if buildOptions.RunDelve {
|
||||
// Check delve exists
|
||||
delveExists := shell.CommandExists("dlv")
|
||||
if !delveExists {
|
||||
return fmt.Errorf("cannot launch delve (Is it installed?)")
|
||||
}
|
||||
|
||||
// Get cwd
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Launch delve
|
||||
buildOptions.Logger.Println("Launching Delve on port 2345...")
|
||||
cmdArgs := slicer.String([]string{"--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", outputFilename})
|
||||
if buildOptions.Verbosity == build.VERBOSE {
|
||||
buildOptions.Logger.Println("\tRunning: dlv %s", cmdArgs.Join(" "))
|
||||
}
|
||||
stdout, stderr, err := shell.RunCommand(cwd, "dlv", cmdArgs.AsSlice()...)
|
||||
if buildOptions.Verbosity == build.VERBOSE || err != nil {
|
||||
trimstdout := strings.TrimSpace(stdout)
|
||||
if trimstdout != "" {
|
||||
buildOptions.Logger.Println(trimstdout)
|
||||
}
|
||||
trimstderr := strings.TrimSpace(stderr)
|
||||
if trimstderr != "" {
|
||||
buildOptions.Logger.Println(trimstderr)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
123
v2/cmd/wails/internal/commands/debug/debug.go
Normal file
123
v2/cmd/wails/internal/commands/debug/debug.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `debug` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
outputType := "desktop"
|
||||
|
||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||
|
||||
command := app.NewSubCommand("debug", "Builds the application then runs delve on the binary")
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
|
||||
// ldflags to pass to `go`
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// Log to file
|
||||
logFile := ""
|
||||
command.StringFlag("l", "Log to file", &logFile)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Validate output type
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: mode,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: false,
|
||||
}
|
||||
|
||||
outputFilename, err := doDebugBuild(buildOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check delve exists
|
||||
delveExists := shell.CommandExists("dlv")
|
||||
if !delveExists {
|
||||
return fmt.Errorf("cannot launch delve (Is it installed?)")
|
||||
}
|
||||
|
||||
// Get cwd
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Launch delve
|
||||
println("Launching Delve on port 2345...")
|
||||
command := shell.CreateCommand(cwd, "dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", outputFilename)
|
||||
return command.Run()
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doDebugBuild is our main build command
|
||||
func doDebugBuild(buildOptions *build.Options) (string, error) {
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
outputFilename, err := build.Build(buildOptions)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
buildOptions.Logger.Println("")
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||
buildOptions.Logger.Println("")
|
||||
|
||||
return outputFilename, nil
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
# Dev
|
||||
|
||||
The dev command allows you to develop your application through a standard browser.
|
||||
|
||||
## Usage
|
||||
|
||||
`wails dev <flags>`
|
||||
|
||||
### Flags
|
||||
|
||||
| Flag | Details | Default |
|
||||
| :------------- | :----------- | :------ |
|
||||
| -compiler path/to/compiler | Use a different go compiler, eg go1.15beta1 | go |
|
||||
| -ldflags "custom ld flags" | Use given ldflags | |
|
||||
| -e list,of,extensions | File extensions to trigger rebuilds | go |
|
||||
| -w | Show warnings | false |
|
||||
| -v int | Verbosity level (0 - silent, 1 - default, 2 - verbose) | 1 |
|
||||
| -loglevel | Loglevel to pass to the application - Trace, Debug, Info, Warning, Error | Debug |
|
||||
|
||||
## How it works
|
||||
|
||||
The project is built using a special mode that starts a webserver and starts listening to port 34115. When the frontend project is run independently, so long as the JS is wrapped with the runtime method `ready`, then the frontend will connect to the backend code via websockets. The interface should be present in your browser, and you should be able to interact with the backend as you would in a desktop app.
|
||||
@@ -1,42 +1,30 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"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
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
@@ -54,17 +42,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
extensions := "go"
|
||||
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)
|
||||
|
||||
// Verbosity
|
||||
verbosity := 1
|
||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
||||
|
||||
loglevel := ""
|
||||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
@@ -72,243 +49,264 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
app.PrintBanner()
|
||||
|
||||
// TODO: Check you are in a project directory
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func(watcher *fsnotify.Watcher) {
|
||||
err := watcher.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(watcher)
|
||||
|
||||
var debugBinaryProcess *process.Process = nil
|
||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
debounceQuit := make(chan bool, 1)
|
||||
|
||||
var passthruArgs []string
|
||||
//if len(os.Args) > 2 {
|
||||
// passthruArgs = os.Args[2:]
|
||||
//}
|
||||
|
||||
// Do initial build
|
||||
logger.Println("Building application for development...")
|
||||
newProcess, appBinary, err := restartApp(logger, ldflags, compilerCommand, debugBinaryProcess, loglevel, passthruArgs, verbosity)
|
||||
if newProcess != nil {
|
||||
debugBinaryProcess = newProcess
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var newBinaryProcess *process.Process
|
||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
logger.Fatal("%s", err.Error())
|
||||
}
|
||||
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
if showWarnings {
|
||||
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// Try and build the app
|
||||
newBinaryProcess, _, err = restartApp(logger, ldflags, compilerCommand, debugBinaryProcess, loglevel, passthruArgs, verbosity)
|
||||
if err != nil {
|
||||
fmt.Printf("Error during build: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// If we have a new process, save it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
projectDir, err := os.Getwd()
|
||||
reloader, err := NewReloader(logger, extensionsThatTriggerARebuild, ldflags, compilerCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(projectDir)
|
||||
// Start
|
||||
err = reloader.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
println("ERRRRRRRRRR: %+v", err)
|
||||
}
|
||||
|
||||
LogGreen("Watching (sub)/directory: %s", projectDir)
|
||||
logger.Println("\nDevelopment mode exited")
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
// Ignore build directory
|
||||
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
||||
return
|
||||
}
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Wait until we get a quit signal
|
||||
quit := false
|
||||
for quit == false {
|
||||
select {
|
||||
case <-quitChannel:
|
||||
LogGreen("\nCaught quit")
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- true
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
err := debugBinaryProcess.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dev binary
|
||||
err = os.Remove(appBinary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
LogGreen("Development mode exited")
|
||||
|
||||
return nil
|
||||
return err
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
|
||||
func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
|
||||
var item fsnotify.Event
|
||||
timer := time.NewTimer(interval)
|
||||
exit:
|
||||
type Reloader struct {
|
||||
|
||||
// Main context
|
||||
ctx context.Context
|
||||
|
||||
// Signal context
|
||||
signalContext context.Context
|
||||
|
||||
// notify
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
// Logger
|
||||
logger *clilogger.CLILogger
|
||||
|
||||
// Extensions to listen for
|
||||
extensionsThatTriggerARebuild []string
|
||||
|
||||
// The binary we are running
|
||||
binary *process.Process
|
||||
|
||||
// options
|
||||
ldflags string
|
||||
compiler string
|
||||
}
|
||||
|
||||
func NewReloader(logger *clilogger.CLILogger, extensionsThatTriggerARebuild []string, ldFlags string, compiler string) (*Reloader, error) {
|
||||
var result Reloader
|
||||
|
||||
// Create context
|
||||
result.ctx = context.Background()
|
||||
|
||||
// Signal context (we don't need cancel)
|
||||
signalContext, _ := signal.NotifyContext(result.ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
result.signalContext = signalContext
|
||||
|
||||
// Create watcher
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.watcher = watcher
|
||||
|
||||
// Logger
|
||||
result.logger = logger
|
||||
|
||||
// Extensions
|
||||
result.extensionsThatTriggerARebuild = extensionsThatTriggerARebuild
|
||||
|
||||
// Options
|
||||
result.ldflags = ldFlags
|
||||
result.compiler = compiler
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (r *Reloader) Start() error {
|
||||
|
||||
err := r.rebuildBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
r.logger.Println("Watching (sub)directories: %s", dir)
|
||||
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
err = r.watcher.Add(dir)
|
||||
if err != nil {
|
||||
r.logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Main loop
|
||||
for {
|
||||
select {
|
||||
case item = <-input:
|
||||
timer.Reset(interval)
|
||||
case <-timer.C:
|
||||
if item.Name != "" {
|
||||
cb(item)
|
||||
case <-r.signalContext.Done():
|
||||
if r.binary != nil {
|
||||
println("Binary is not nil - kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
return nil
|
||||
case event := <-r.watcher.Events:
|
||||
err := r.processWatcherEvent(event)
|
||||
if err != nil {
|
||||
println("error from processWatcherEvent. Calling cancel()")
|
||||
println("Calling kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
case <-quitChannel:
|
||||
break exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restartApp(logger *clilogger.CLILogger, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string, passthruArgs []string, verbosity int) (*process.Process, string, error) {
|
||||
func (r *Reloader) processWatcherEvent(event fsnotify.Event) error {
|
||||
|
||||
appBinary, err := buildApp(logger, ldflags, compilerCommand, verbosity)
|
||||
println()
|
||||
if err != nil {
|
||||
LogRed("Build error - continuing to run current version")
|
||||
LogDarkYellow(err.Error())
|
||||
return nil, "", nil
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := r.watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
killError := debugBinaryProcess.Kill()
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
if killError != nil {
|
||||
logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
var rebuild bool
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range r.extensionsThatTriggerARebuild {
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
}
|
||||
}
|
||||
|
||||
debugBinaryProcess = nil
|
||||
if !rebuild {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.logger.Println("\n%s[Build triggered] %s %s", ctc.ForegroundGreen|ctc.ForegroundBright, event.Name, ctc.Reset)
|
||||
|
||||
return r.rebuildBinary()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reloader) rebuildBinary() error {
|
||||
|
||||
// rebuild binary
|
||||
binary, err := r.buildApp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Generate `backend.js`
|
||||
|
||||
// Start up new binary with correct args
|
||||
var args = []string{"-loglevel", loglevel}
|
||||
if len(passthruArgs) > 0 {
|
||||
args = append(args, passthruArgs...)
|
||||
// Kill current binary if running
|
||||
if r.binary != nil {
|
||||
err = r.binary.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
newProcess := process.NewProcess(logger, appBinary, args...)
|
||||
|
||||
newProcess := process.NewProcess(r.ctx, r.logger, binary)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
deleteError := fs.DeleteFile(appBinary)
|
||||
if deleteError != nil {
|
||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
}
|
||||
logger.Fatal("Unable to start application: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return newProcess, appBinary, nil
|
||||
// Ensure process runs correctly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildApp(logger *clilogger.CLILogger, ldflags string, compilerCommand string, verbosity int) (string, error) {
|
||||
//func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
||||
//
|
||||
// appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||
// println()
|
||||
// if err != nil {
|
||||
// logger.Fatal(err.Error())
|
||||
// return nil, errors.Wrap(err, "Build Failed:")
|
||||
// }
|
||||
// logger.Println("Build new binary: %s", appBinary)
|
||||
//
|
||||
// // Kill existing binary if need be
|
||||
// if debugBinaryProcess != nil {
|
||||
// killError := debugBinaryProcess.Kill()
|
||||
//
|
||||
// if killError != nil {
|
||||
// logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
// }
|
||||
//
|
||||
// debugBinaryProcess = nil
|
||||
// }
|
||||
//
|
||||
// // TODO: Generate `backend.js`
|
||||
//
|
||||
// // Start up new binary
|
||||
// newProcess := process.NewProcess(logger, appBinary)
|
||||
// err = newProcess.Start()
|
||||
// if err != nil {
|
||||
// // Remove binary
|
||||
// deleteError := fs.DeleteFile(appBinary)
|
||||
// if deleteError != nil {
|
||||
// logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
// }
|
||||
// logger.Fatal("Unable to start application: %s", err.Error())
|
||||
// }
|
||||
//
|
||||
// // Check if port is open
|
||||
// timeout := time.Second
|
||||
// conn, err := net.DialTimeout("tcp", net.JoinHostPort("host", port), timeout)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// newProcess.Running
|
||||
// return newProcess, nil
|
||||
//}
|
||||
|
||||
// buildapp attempts to compile the application
|
||||
// It returns the path to the new binary or an error
|
||||
func (r *Reloader) buildApp() (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := "wailsdev"
|
||||
if runtime.GOOS == "windows" {
|
||||
outputFile += ".exe"
|
||||
}
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
Logger: r.logger,
|
||||
OutputType: "dev",
|
||||
Mode: build.Debug,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
LDFlags: r.ldflags,
|
||||
Compiler: r.compiler,
|
||||
OutputFile: outputFile,
|
||||
IgnoreFrontend: true,
|
||||
Verbosity: verbosity,
|
||||
}
|
||||
|
||||
return build.Build(buildOptions)
|
||||
|
||||
@@ -3,6 +3,7 @@ package doctor
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -24,13 +25,11 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
logger := clilogger.New(w)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Scanning system - Please wait (this may take a long time)...")
|
||||
logger.Print("Scanning system - please wait...")
|
||||
|
||||
// Get system info
|
||||
info, err := system.GetInfo()
|
||||
if err != nil {
|
||||
logger.Println("Failed.")
|
||||
return err
|
||||
}
|
||||
logger.Println("Done.")
|
||||
@@ -40,22 +39,27 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "System\n")
|
||||
fmt.Fprintf(w, "------\n")
|
||||
fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID)
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM == nil {
|
||||
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
|
||||
w.Flush()
|
||||
println()
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
|
||||
// Output Go Information
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version())
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS)
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH)
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM != nil {
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
}
|
||||
|
||||
// Output Dependencies Status
|
||||
var dependenciesMissing = []string{}
|
||||
var externalPackages = []*packagemanager.Dependancy{}
|
||||
@@ -65,14 +69,12 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n")
|
||||
fmt.Fprintf(w, "----------\t------------\t------\t-------\n")
|
||||
|
||||
hasOptionalDependencies := false
|
||||
// Loop over dependencies
|
||||
for _, dependency := range info.Dependencies {
|
||||
|
||||
name := dependency.Name
|
||||
if dependency.Optional {
|
||||
name = "*" + name
|
||||
hasOptionalDependencies = true
|
||||
name += "*"
|
||||
}
|
||||
packageName := "Unknown"
|
||||
status := "Not Found"
|
||||
@@ -107,48 +109,44 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, packageName, status, dependency.Version)
|
||||
}
|
||||
if hasOptionalDependencies {
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||
w.Flush()
|
||||
logger.Println("")
|
||||
logger.Println("Diagnosis")
|
||||
logger.Println("---------")
|
||||
logger.Println("---------\n")
|
||||
|
||||
// Generate an appropriate diagnosis
|
||||
|
||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||
logger.Println("Your system is ready for Wails development!")
|
||||
} else {
|
||||
logger.Println("Your system has missing dependencies!\n")
|
||||
}
|
||||
|
||||
if dependenciesAvailableRequired != 0 {
|
||||
logger.Println("Required package(s) installation details: \n" + info.Dependencies.InstallAllRequiredCommand())
|
||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||
}
|
||||
|
||||
if dependenciesAvailableOptional != 0 {
|
||||
logger.Println("Optional package(s) installation details: \n" + info.Dependencies.InstallAllOptionalCommand())
|
||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||
}
|
||||
|
||||
if len(externalPackages) > 0 {
|
||||
for _, p := range externalPackages {
|
||||
if p.Optional {
|
||||
print("[Optional] ")
|
||||
}
|
||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
||||
}
|
||||
}
|
||||
//
|
||||
//if len(externalPackages) > 0 {
|
||||
// for _, p := range externalPackages {
|
||||
// if p.Optional {
|
||||
// print("[Optional] ")
|
||||
// }
|
||||
// logger.Println("Install " + p.Name + ": " + p.InstallCommand)
|
||||
// }
|
||||
//}
|
||||
|
||||
if len(dependenciesMissing) != 0 {
|
||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
||||
logger.Println("Fatal:")
|
||||
logger.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
logger.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||
log.Println("Fatal:")
|
||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||
}
|
||||
|
||||
logger.Println("")
|
||||
log.Println("")
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Generate
|
||||
|
||||
The `generate` command provides the ability to generate various Wails related components.
|
||||
|
||||
## Usage
|
||||
|
||||
`wails generate [subcommand] [options]`
|
||||
|
||||
## Template
|
||||
|
||||
`wails generate template -name <name> [-frontend] [-q]`
|
||||
|
||||
Generate a starter template for you to customise.
|
||||
|
||||
| Flag | Details |
|
||||
| :------------- | :----------- |
|
||||
| -frontend | Copies all the files from the current directory into the template's `frontend` directory. Useful for converting frontend projects created by boilerplate generators. |
|
||||
| -q | Suppress output |
|
||||
@@ -2,22 +2,62 @@ package generate
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate/template"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `generate` command for the Wails application
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
||||
|
||||
//AddModuleCommand(app, command, w)
|
||||
template.AddSubCommand(app, command, w)
|
||||
// Backend API
|
||||
backendAPI := command.NewSubCommand("module", "Generates a JS module for the frontend to interface with the backend")
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
backendAPI.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Generating Javascript module for Go code...")
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
p, err := parser.GenerateWailsFrontendPackage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("done.")
|
||||
logger.Println("")
|
||||
|
||||
elapsed := time.Since(start)
|
||||
packages := p.Packages
|
||||
|
||||
// Print report
|
||||
for _, pkg := range p.Packages {
|
||||
if pkg.ShouldGenerate() {
|
||||
logPackage(pkg, logger)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Println("%d packages parsed in %s.", len(packages), elapsed)
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -45,4 +85,7 @@ func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
||||
}
|
||||
}
|
||||
logger.Println("")
|
||||
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
func AddModuleCommand(app *clir.Cli, parent *clir.Command, w io.Writer) {
|
||||
|
||||
// Backend API
|
||||
backendAPI := parent.NewSubCommand("module", "Generates a JS module for the frontend to interface with the backend")
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
backendAPI.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
|
||||
backendAPI.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Generating Javascript module for Go code...")
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
p, err := parser.GenerateWailsFrontendPackage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("done.")
|
||||
logger.Println("")
|
||||
|
||||
elapsed := time.Since(start)
|
||||
packages := p.Packages
|
||||
|
||||
// Print report
|
||||
for _, pkg := range p.Packages {
|
||||
if pkg.ShouldGenerate() {
|
||||
logPackage(pkg, logger)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Println("%d packages parsed in %s.", len(packages), elapsed)
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
# Next Steps
|
||||
|
||||
Congratulations on generating your template!
|
||||
|
||||
## Completing your template
|
||||
|
||||
The next steps to complete the template are:
|
||||
|
||||
1. Complete the fields in the `template.json` file.
|
||||
2. Update `README.md`.
|
||||
3. Edit `wails.tmpl.json` and ensure all fields are correct, especially:
|
||||
- `html` - path to your `index.html`
|
||||
- `frontend:install` - The command to install your frontend dependencies
|
||||
- `frontend:build` - The command to build your frontend
|
||||
4. Remove any `public` or `dist` directories.
|
||||
5. Delete this file.
|
||||
|
||||
## Testing your template
|
||||
|
||||
You can test your template by running this command:
|
||||
|
||||
`wails init -name test -t {{.TemplateDir}}`
|
||||
|
||||
### Checklist
|
||||
Once generated, do the following tests:
|
||||
- Change into the new project directory and run `wails build`. A working binary should be generated in the `build/bin` project directory.
|
||||
- Run `wails dev`. This will compile your backend and run it. You should be able to go into the frontend directory and run `npm run dev` (or whatever your dev command is) and this should run correctly. You should be able to then open a browser to your local dev server and the application should work.
|
||||
|
||||
## Publishing your template
|
||||
|
||||
You can publish a template to a git repository and use it as follows:
|
||||
|
||||
`wails init -name test -t https://your/git/url`
|
||||
|
||||
EG:
|
||||
|
||||
`wails init -name test -t https://github.com/leaanthony/testtemplate`
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# README
|
||||
|
||||
## About
|
||||
|
||||
About your template
|
||||
|
||||
## Building
|
||||
|
||||
To build this project in debug mode, use `wails build`. For production, use `wails build -production`.
|
||||
To generate a platform native package, add the `-package` flag.
|
||||
|
||||
## Live Development
|
||||
|
||||
To run in live development mode, run `wails dev` in the project directory. In another terminal, go into the `frontend`
|
||||
directory and run `npm run dev`. The frontend dev server will run on http://localhost:5000. Connect to this
|
||||
in your browser and connect to your application.
|
||||
@@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// startup is called at application startup
|
||||
func (b *App) startup(runtime *wails.Runtime) {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
}
|
||||
|
||||
// shutdown is called at application termination
|
||||
func (b *App) shutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "vanilla",
|
||||
"version": "1.0.0",
|
||||
"description": "Vanilla Wails v2 template",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv dist"
|
||||
},
|
||||
"author": "{{.AuthorName}}",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-commonjs": "^19.0.0",
|
||||
"@rollup/plugin-image": "^2.0.6",
|
||||
"@rollup/plugin-node-resolve": "^13.0.0",
|
||||
"@rollup/plugin-url": "^6.0.0",
|
||||
"@wails/runtime": "^1.3.20",
|
||||
"rollup": "^2.50.4",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sirv-cli": "^1.0.12"
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import image from '@rollup/plugin-image';
|
||||
import url from '@rollup/plugin-url';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'dist/main.js'
|
||||
},
|
||||
onwarn: handleRollupWarning,
|
||||
plugins: [
|
||||
|
||||
image(),
|
||||
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'src/index.html', dest: 'dist' },
|
||||
{ src: 'src/main.css', dest: 'dist' },
|
||||
]
|
||||
}),
|
||||
|
||||
// Embed binary files
|
||||
url({
|
||||
include: ['**/*.woff', '**/*.woff2'],
|
||||
limit: Infinity,
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// PostCSS preprocessing
|
||||
postcss({
|
||||
extensions: ['.css', '.scss'],
|
||||
extract: true,
|
||||
minimize: false,
|
||||
use: [
|
||||
['sass', {
|
||||
includePaths: [
|
||||
'./src',
|
||||
'./node_modules'
|
||||
]
|
||||
}]
|
||||
],
|
||||
}),
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('dist'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
|
||||
function handleRollupWarning(warning) {
|
||||
console.error('ERROR: ' + warning.toString());
|
||||
}
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,21 +0,0 @@
|
||||
|
||||
import {ready} from '@wails/runtime';
|
||||
|
||||
ready( () => {
|
||||
// Get input + focus
|
||||
let nameElement = document.getElementById("name");
|
||||
nameElement.focus();
|
||||
|
||||
// Setup the greet function
|
||||
window.greet = function () {
|
||||
|
||||
// Get name
|
||||
let name = nameElement.value;
|
||||
|
||||
// Call Basic.Greet(name)
|
||||
window.backend.main.Basic.Greet(name).then((result) => {
|
||||
// Update result with data back from Basic.Greet()
|
||||
document.getElementById("result").innerText = result;
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
|
||||
"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() {
|
||||
|
||||
// Create application with options
|
||||
app := NewApp()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "{{.ProjectName}}",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
MinWidth: 400,
|
||||
MinHeight: 400,
|
||||
MaxWidth: 1280,
|
||||
MaxHeight: 1024,
|
||||
DisableResize: false,
|
||||
Fullscreen: false,
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
DevTools: false,
|
||||
RGBA: 0x000000FF,
|
||||
Windows: &windows.Options{
|
||||
WebviewIsTransparent: true,
|
||||
WindowBackgroundIsTranslucent: true,
|
||||
DisableWindowIcon: 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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "Long name",
|
||||
"shortname": "{{.Name}}",
|
||||
"author": "",
|
||||
"description": "Description of the template",
|
||||
"helpurl": "URL for help with the template, eg homepage"
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/leaanthony/debme"
|
||||
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
//go:embed base
|
||||
var base embed.FS
|
||||
|
||||
func AddSubCommand(app *clir.Cli, parent *clir.Command, w io.Writer) {
|
||||
|
||||
// command
|
||||
command := parent.NewSubCommand("template", "Generates a wails template")
|
||||
|
||||
name := ""
|
||||
command.StringFlag("name", "The name of the template", &name)
|
||||
|
||||
migrate := false
|
||||
command.BoolFlag("migrate", "This indicates that the current directory is a frontend project and should be used by the template", &migrate)
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// If the current directory is not empty, we create a new directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
templateDir := cwd
|
||||
empty, err := fs.DirIsEmpty(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !empty {
|
||||
templateDir = filepath.Join(cwd, name)
|
||||
println("Creating new template directory:", name)
|
||||
err = fs.Mkdir(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Create base template
|
||||
baseTemplate, err := debme.FS(base, "base")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g := gosod.New(baseTemplate)
|
||||
g.SetTemplateFilters([]string{".template"})
|
||||
|
||||
err = os.Chdir(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type templateData struct {
|
||||
Name string
|
||||
Description string
|
||||
TemplateDir string
|
||||
}
|
||||
|
||||
println("Extracting base template files...")
|
||||
|
||||
err = g.Extract(templateDir, &templateData{
|
||||
Name: name,
|
||||
TemplateDir: templateDir,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we aren't migrating the files, just exit
|
||||
if migrate == false {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove frontend directory
|
||||
frontendDir := filepath.Join(templateDir, "frontend")
|
||||
err = os.RemoveAll(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move the files into a new frontend directory
|
||||
println("Migrating files to frontend directory...")
|
||||
err = fs.MoveDirExtended(cwd, frontendDir, []string{name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process package.json
|
||||
err = processPackageJSON(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process package-lock.json
|
||||
err = processPackageLockJSON(frontendDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove node_modules - ignore error, eg it doesn't exist
|
||||
_ = os.RemoveAll(filepath.Join(frontendDir, "node_modules"))
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func processPackageJSON(frontendDir string) error {
|
||||
var err error
|
||||
|
||||
packageJSON := filepath.Join(frontendDir, "package.json")
|
||||
if !fs.FileExists(packageJSON) {
|
||||
println("No package.json found - cannot process.")
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(packageJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json := string(data)
|
||||
|
||||
// We will ignore these errors - it's not critical
|
||||
println("Updating package.json data...")
|
||||
json, _ = sjson.Set(json, "name", "{{.ProjectName}}")
|
||||
json, _ = sjson.Set(json, "author", "{{.AuthorName}}")
|
||||
|
||||
err = os.WriteFile(packageJSON, []byte(json), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(packageJSON)
|
||||
println("Renaming package.json -> package.tmpl.json...")
|
||||
err = os.Rename(packageJSON, filepath.Join(baseDir, "package.tmpl.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func processPackageLockJSON(frontendDir string) error {
|
||||
var err error
|
||||
|
||||
filename := filepath.Join(frontendDir, "package-lock.json")
|
||||
if !fs.FileExists(filename) {
|
||||
println("No package-lock.json found - cannot process.")
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
json := string(data)
|
||||
|
||||
// We will ignore these errors - it's not critical
|
||||
println("Updating package-lock.json data...")
|
||||
json, _ = sjson.Set(json, "name", "{{.ProjectName}}")
|
||||
|
||||
err = os.WriteFile(filename, []byte(json), 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
baseDir := filepath.Dir(filename)
|
||||
println("Renaming package-lock.json -> package-lock.tmpl.json...")
|
||||
err = os.Rename(filename, filepath.Join(baseDir, "package-lock.tmpl.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6,12 +6,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/buildassets"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/git"
|
||||
)
|
||||
@@ -19,11 +16,17 @@ import (
|
||||
// AddSubcommand adds the `init` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Load the template shortnames
|
||||
validShortNames, err := templates.TemplateShortNames()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
command := app.NewSubCommand("init", "Initialise a new Wails project")
|
||||
|
||||
// Setup template name flag
|
||||
templateName := "vanilla"
|
||||
description := "Name of built-in template to use, path to template or template url."
|
||||
description := "Name of template to use. Valid tempates: " + validShortNames.Join(" ")
|
||||
command.StringFlag("t", description, &templateName)
|
||||
|
||||
// Setup project name
|
||||
@@ -36,7 +39,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
initGit := false
|
||||
gitInstalled := git.IsInstalled()
|
||||
@@ -67,6 +70,14 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate output type
|
||||
if !validShortNames.Contains(templateName) {
|
||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
||||
logger.Println("")
|
||||
command.PrintHelp()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate name
|
||||
if len(projectName) == 0 {
|
||||
logger.Println("ERROR: Project name required")
|
||||
@@ -94,7 +105,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
}
|
||||
|
||||
// Try to discover author details from git config
|
||||
findAuthorDetails(options)
|
||||
err := findAuthorDetails(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return initProject(options)
|
||||
})
|
||||
@@ -109,13 +123,7 @@ func initProject(options *templates.Options) error {
|
||||
start := time.Now()
|
||||
|
||||
// Install the template
|
||||
remote, err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Install the default assets
|
||||
err = buildassets.Install(options.TargetDir, options.ProjectName)
|
||||
err := templates.Install(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -139,11 +147,6 @@ func initProject(options *templates.Options) error {
|
||||
if options.InitGit {
|
||||
options.Logger.Println("Git repository initialised.")
|
||||
}
|
||||
|
||||
if remote {
|
||||
options.Logger.Println("\nNOTE: You have created a project using a remote template. The Wails project takes no responsibility for 3rd party templates. Only use remote templates that you trust.")
|
||||
}
|
||||
|
||||
options.Logger.Println("")
|
||||
options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
||||
options.Logger.Println("")
|
||||
@@ -160,18 +163,20 @@ func initGit(options *templates.Options) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// findAuthorDetails tries to find the user's name and email
|
||||
// from gitconfig. If it finds them, it stores them in the project options
|
||||
func findAuthorDetails(options *templates.Options) {
|
||||
func findAuthorDetails(options *templates.Options) error {
|
||||
if git.IsInstalled() {
|
||||
name, err := git.Name()
|
||||
if err == nil {
|
||||
options.AuthorName = strings.TrimSpace(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.AuthorName = strings.TrimSpace(name)
|
||||
|
||||
email, err := git.Email()
|
||||
if err == nil {
|
||||
options.AuthorEmail = strings.TrimSpace(email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.AuthorEmail = strings.TrimSpace(email)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# README
|
||||
|
||||
## About
|
||||
|
||||
This is a basic Svelte template, using rollup to bundle the assets into a single JS file.
|
||||
Rollup is configured to do the following:
|
||||
|
||||
- Convert imported images to base64 strings
|
||||
- Convert `url()` in `@font-face` declarations to base64 strings
|
||||
- Bundle all css into the JS bundle
|
||||
- Copy `index.html` from `frontend/src/` to `frontend/dist/`
|
||||
|
||||
Clicking the button will call the backend
|
||||
|
||||
## Building
|
||||
|
||||
To build this project in debug mode, use `wails build`. For production, use `wails build -production`.
|
||||
To generate a platform native package, add the `-package` flag.
|
||||
|
||||
## Live Development
|
||||
|
||||
To run in live development mode, run `wails dev` in the project directory. In another terminal, go into the `frontend`
|
||||
directory and run `npm run dev`. The frontend dev server will run on http://localhost:5000. Connect to this
|
||||
in your browser and connect to your application.
|
||||
@@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// App application struct
|
||||
type App struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// startup is called at application startup
|
||||
func (b *App) startup(runtime *wails.Runtime) {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
}
|
||||
|
||||
// shutdown is called at application termination
|
||||
func (b *App) shutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "{{.ProjectName}}",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv dist --no-clear"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-image": "^2.0.6",
|
||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||
"postcss": "^8.3.5",
|
||||
"postcss-url": "^10.1.3",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^1.0.0"
|
||||
}
|
||||
,"author":"{{.AuthorName}}"}
|
||||
@@ -1,121 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
/** This script modifies the project to support TS code in .svelte files like:
|
||||
|
||||
<script lang="ts">
|
||||
export let name: string;
|
||||
</script>
|
||||
|
||||
As well as validating the code for CI.
|
||||
*/
|
||||
|
||||
/** To work on this script:
|
||||
rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template
|
||||
*/
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { argv } = require("process")
|
||||
|
||||
const projectRoot = argv[2] || path.join(__dirname, "..")
|
||||
|
||||
// Add deps to pkg.json
|
||||
const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
|
||||
packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, {
|
||||
"svelte-check": "^2.0.0",
|
||||
"svelte-preprocess": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.0.0",
|
||||
"typescript": "^4.0.0",
|
||||
"tslib": "^2.0.0",
|
||||
"@tsconfig/svelte": "^2.0.0"
|
||||
})
|
||||
|
||||
// Add script for checking
|
||||
packageJSON.scripts = Object.assign(packageJSON.scripts, {
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||
})
|
||||
|
||||
// Write the package JSON
|
||||
fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " "))
|
||||
|
||||
// mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too
|
||||
const beforeMainJSPath = path.join(projectRoot, "src", "main.js")
|
||||
const afterMainTSPath = path.join(projectRoot, "src", "main.ts")
|
||||
fs.renameSync(beforeMainJSPath, afterMainTSPath)
|
||||
|
||||
// Switch the app.svelte file to use TS
|
||||
const appSveltePath = path.join(projectRoot, "src", "App.svelte")
|
||||
let appFile = fs.readFileSync(appSveltePath, "utf8")
|
||||
appFile = appFile.replace("<script>", '<script lang="ts">')
|
||||
appFile = appFile.replace("export let name;", 'export let name: string;')
|
||||
fs.writeFileSync(appSveltePath, appFile)
|
||||
|
||||
// Edit rollup config
|
||||
const rollupConfigPath = path.join(projectRoot, "rollup.config.js")
|
||||
let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8")
|
||||
|
||||
// Edit imports
|
||||
rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser';
|
||||
import sveltePreprocess from 'svelte-preprocess';
|
||||
import typescript from '@rollup/plugin-typescript';`)
|
||||
|
||||
// Replace name of entry point
|
||||
rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`)
|
||||
|
||||
// Add preprocessor
|
||||
rollupConfig = rollupConfig.replace(
|
||||
'compilerOptions:',
|
||||
'preprocess: sveltePreprocess({ sourceMap: !production }),\n\t\t\tcompilerOptions:'
|
||||
);
|
||||
|
||||
// Add TypeScript
|
||||
rollupConfig = rollupConfig.replace(
|
||||
'commonjs(),',
|
||||
'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),'
|
||||
);
|
||||
fs.writeFileSync(rollupConfigPath, rollupConfig)
|
||||
|
||||
// Add TSConfig
|
||||
const tsconfig = `{
|
||||
"extends": "@tsconfig/svelte/tsconfig.json",
|
||||
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/*", "__sapper__/*", "public/*"]
|
||||
}`
|
||||
const tsconfigPath = path.join(projectRoot, "tsconfig.json")
|
||||
fs.writeFileSync(tsconfigPath, tsconfig)
|
||||
|
||||
// Add global.d.ts
|
||||
const dtsPath = path.join(projectRoot, "src", "global.d.ts")
|
||||
fs.writeFileSync(dtsPath, `/// <reference types="svelte" />`)
|
||||
|
||||
// Delete this script, but not during testing
|
||||
if (!argv[2]) {
|
||||
// Remove the script
|
||||
fs.unlinkSync(path.join(__filename))
|
||||
|
||||
// Check for Mac's DS_store file, and if it's the only one left remove it
|
||||
const remainingFiles = fs.readdirSync(path.join(__dirname))
|
||||
if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') {
|
||||
fs.unlinkSync(path.join(__dirname, '.DS_store'))
|
||||
}
|
||||
|
||||
// Check if the scripts folder is empty
|
||||
if (fs.readdirSync(path.join(__dirname)).length === 0) {
|
||||
// Remove the scripts folder
|
||||
fs.rmdirSync(path.join(__dirname))
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the extension recommendation
|
||||
fs.mkdirSync(path.join(projectRoot, ".vscode"), { recursive: true })
|
||||
fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{
|
||||
"recommendations": ["svelte.svelte-vscode"]
|
||||
}
|
||||
`)
|
||||
|
||||
console.log("Converted to TypeScript.")
|
||||
|
||||
if (fs.existsSync(path.join(projectRoot, "node_modules"))) {
|
||||
console.log("\nYou will need to re-run your dependency manager to get started.")
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<script>
|
||||
let name = "";
|
||||
let greeting = "";
|
||||
|
||||
function greet() {
|
||||
window.backend.main.App.Greet(name).then((result) => {
|
||||
greeting = result;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div id="logo"></div>
|
||||
<div id="input" data-wails-no-drag>
|
||||
<input id="name" type="text" bind:value={name}>
|
||||
<button class="button" on:click={greet}>Greet</button>
|
||||
</div>
|
||||
{#if greeting}
|
||||
<div id="result">{greeting}</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
||||
main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#result {
|
||||
margin-top: 1rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
-webkit-appearance: default-button;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
#name {
|
||||
border-radius: 3px;
|
||||
outline: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
padding-top: 20%;
|
||||
margin: auto;
|
||||
display: block;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("assets/images/logo-dark.svg");
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,93 +0,0 @@
|
||||
Copyright 2016 The Nunito Project Authors (contact@sansoxygen.com),
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 551 436" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1.27527,0,0,1.27527,104.01,410.563)">
|
||||
<path d="M0,-51.891L14.429,-51.891L13.043,-21.183L22.568,-51.891L34.226,-51.891L34.084,-21.183L42.365,-51.891L56.794,-51.891L38.526,0L25.198,0L25.34,-32.45L15.211,0L1.919,0L0,-51.891Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1.27527,0,0,1.27527,224.985,367.503)">
|
||||
<path d="M0,15.639L5.793,15.639L5.971,-3.589L0,15.639ZM-20.187,33.765L-0.675,-18.126L16.42,-18.126L20.08,33.765L5.437,33.765L5.509,26.123L-3.057,26.123L-5.332,33.765L-20.187,33.765Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.19929,-1.2596,-1.2596,-0.19929,332.323,396.949)">
|
||||
<path d="M-16.046,33.107L36.491,33.107L38.757,18.784L-13.785,18.82L-16.046,33.107Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1.27527,0,0,1.27527,353.217,344.388)">
|
||||
<path d="M0,51.891L8.246,0L22.781,0L16.597,39.024L27.224,39.024L25.199,51.891L0,51.891Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(1.27527,0,0,1.27527,427.939,364.922)">
|
||||
<path d="M0,19.83C1.611,21.181 3.305,22.224 5.083,22.959C6.859,23.693 8.565,24.06 10.2,24.06C11.645,24.06 12.794,23.663 13.647,22.87C14.5,22.076 14.927,20.992 14.927,19.617C14.927,18.434 14.571,17.254 13.861,16.081C13.15,14.908 11.775,13.351 9.738,11.408C7.273,9.015 5.58,6.906 4.655,5.081C3.731,3.257 3.27,1.243 3.27,-0.96C3.27,-5.912 4.839,-9.846 7.979,-12.76C11.118,-15.674 15.377,-17.132 20.756,-17.132C22.936,-17.132 25.008,-16.889 26.975,-16.403C28.941,-15.917 30.943,-15.165 32.982,-14.146L30.92,-1.493C29.356,-2.583 27.834,-3.412 26.354,-3.981C24.872,-4.551 23.457,-4.835 22.106,-4.835C20.898,-4.835 19.943,-4.521 19.245,-3.894C18.546,-3.265 18.196,-2.406 18.196,-1.316C18.196,0.154 19.535,2.215 22.213,4.868C22.544,5.2 22.805,5.46 22.995,5.649C25.696,8.304 27.473,10.578 28.326,12.475C29.179,14.37 29.605,16.56 29.605,19.049C29.605,24.594 27.893,28.965 24.469,32.163C21.046,35.361 16.36,36.962 10.413,36.962C7.877,36.962 5.479,36.66 3.216,36.056C0.953,35.45 -0.948,34.615 -2.488,33.549L0,19.83Z" style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-166.599,4.57132,4.57132,166.599,147.403,167.648)">
|
||||
<path d="M0.883,-0.081L0.121,0.081L0.256,-0.063L0.883,-0.081Z" style="fill:url(#_Linear1);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-106.443,-16.0669,-16.0669,106.443,428.19,188.033)">
|
||||
<path d="M0.878,-0.285L-0.073,0.71L-1.186,0.542L0.015,0.207L-0.846,0.077L0.355,-0.258L-0.505,-0.388L0.649,-0.71L0.878,-0.285Z" style="fill:url(#_Linear2);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-114.484,-162.408,-162.408,114.484,333.291,285.804)">
|
||||
<path d="M0.44,-0.04L0.44,-0.04L0.44,-0.04L0.265,-0.056L0.177,0.437L-0.311,-0.255L0.262,-0.437L0.568,-0.437L0.44,-0.04Z" style="fill:url(#_Linear3);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(61.6919,58.8091,58.8091,-61.6919,258.631,180.413)">
|
||||
<path d="M0.5,0L0.5,-0L0.5,0L0.5,0Z" style="fill:url(#_Linear4);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(238.126,298.893,298.893,-238.126,113.516,-150.536)">
|
||||
<path d="M0.622,-0.115L0.761,-0.115L0.806,-0.013L0.826,0.182L0.622,-0.115Z" style="fill:url(#_Linear5);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-369.529,-97.4118,-97.4118,369.529,582.38,94.027)">
|
||||
<path d="M0.467,0.005L0.49,0.062L0.271,-0.062L0.467,0.005Z" style="fill:url(#_Linear6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-496.156,-53.9751,-53.9751,496.156,367.888,125.085)">
|
||||
<path d="M0.2,0.001L0.219,-0.018L0.614,0.012L0.519,0.089L0.282,0.068L0.2,0.135L0.463,0.194L0.374,0.266L0.138,0.186L0.138,0.186L0.138,0.186L0.047,0.033L-0.131,-0.266L0.2,0.001Z" style="fill:url(#_Linear7);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(185.076,176.427,176.427,-185.076,153.446,80.1488)">
|
||||
<path d="M0.735,-0L0.735,-0L0.735,0L0.735,-0Z" style="fill:url(#_Linear8);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,-3.46945e-18,-3.46945e-18,-1,0,-3.05761e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear2" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,-2.75467e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear3" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,-1.11022e-16,-1.11022e-16,-1,0,-2.61861e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear4" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,-5.55112e-17,-5.55112e-17,-1,0,-1.57562e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear5" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-0.801899,-0.59746,-0.59746,0.801899,1.3495,0.447457)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear6" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,-2.77556e-17,-2.77556e-17,-1,0,-1.92826e-06)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear7" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,9.68429e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
<linearGradient id="_Linear8" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,-1,0,1.43665e-07)"><stop offset="0" style="stop-color:rgb(227,50,50);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(107,0,13);stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 6.8 KiB |
@@ -1,24 +0,0 @@
|
||||
|
||||
html {
|
||||
text-align: center;
|
||||
color: white;
|
||||
background-color: rgba(1,1,1,0.1);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
color: white;
|
||||
font-family: 'Nunito', -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local(''),
|
||||
url('./assets/fonts/nunito-v16-latin-regular.woff2') format('woff2')
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte app</title>
|
||||
|
||||
<script defer src='bundle.js'></script>
|
||||
</head>
|
||||
|
||||
<body data-wails-drag>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,56 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
|
||||
"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() {
|
||||
|
||||
// Create application with options
|
||||
app := NewApp()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "{{.ProjectName}}",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
MinWidth: 400,
|
||||
MinHeight: 400,
|
||||
MaxWidth: 1280,
|
||||
MaxHeight: 1024,
|
||||
DisableResize: false,
|
||||
Fullscreen: false,
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
DevTools: false,
|
||||
RGBA: 0x000000FF,
|
||||
Windows: &windows.Options{
|
||||
WebviewIsTransparent: true,
|
||||
WindowBackgroundIsTranslucent: true,
|
||||
DisableWindowIcon: 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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "Basic Svelte + Rollup",
|
||||
"shortname": "svelte",
|
||||
"author": "Lea Anthony",
|
||||
"description": "Svelte template using rollup to bundle css, images and fonts",
|
||||
"helpurl": "https://github.com/wailsapp/wails"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
# README
|
||||
|
||||
## About
|
||||
|
||||
This template uses vanilla JS / HTML and CSS. [Rollup](https://rollupjs.org/guide/en/) is used for processing and bundling the files.
|
||||
|
||||
## Building
|
||||
|
||||
To build this project in debug mode, use `wails build`. For production, use `wails build -production`.
|
||||
To generate a platform native package, add the `-package` flag.
|
||||
|
||||
## Live Development
|
||||
|
||||
To run in live development mode, run `wails dev` in the project directory. In another terminal, go into the `frontend`
|
||||
directory and run `npm run dev`. The frontend dev server will run on http://localhost:5000. Connect to this
|
||||
in your browser and connect to your application.
|
||||
@@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// App struct
|
||||
type App struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// NewApp creates a new App application struct
|
||||
func NewApp() *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// startup is called at application startup
|
||||
func (b *App) startup(runtime *wails.Runtime) {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
}
|
||||
|
||||
// shutdown is called at application termination
|
||||
func (b *App) shutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *App) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "vanilla",
|
||||
"version": "1.0.0",
|
||||
"description": "Vanilla Wails v2 template",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv dist"
|
||||
},
|
||||
"author": "{{.AuthorName}}",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-commonjs": "^19.0.0",
|
||||
"@rollup/plugin-image": "^2.0.6",
|
||||
"@rollup/plugin-node-resolve": "^13.0.0",
|
||||
"@rollup/plugin-url": "^6.0.0",
|
||||
"@wails/runtime": "^1.3.20",
|
||||
"rollup": "^2.50.4",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sirv-cli": "^1.0.12"
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import image from '@rollup/plugin-image';
|
||||
import url from '@rollup/plugin-url';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'dist/main.js'
|
||||
},
|
||||
onwarn: handleRollupWarning,
|
||||
plugins: [
|
||||
|
||||
image(),
|
||||
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'src/index.html', dest: 'dist' },
|
||||
{ src: 'src/main.css', dest: 'dist' },
|
||||
]
|
||||
}),
|
||||
|
||||
// Embed binary files
|
||||
url({
|
||||
include: ['**/*.woff', '**/*.woff2'],
|
||||
limit: Infinity,
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// PostCSS preprocessing
|
||||
postcss({
|
||||
extensions: ['.css', '.scss'],
|
||||
extract: true,
|
||||
minimize: false,
|
||||
use: [
|
||||
['sass', {
|
||||
includePaths: [
|
||||
'./src',
|
||||
'./node_modules'
|
||||
]
|
||||
}]
|
||||
],
|
||||
}),
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('dist'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
|
||||
function handleRollupWarning(warning) {
|
||||
console.error('ERROR: ' + warning.toString());
|
||||
}
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="/main.css">
|
||||
</head>
|
||||
|
||||
<body data-wails-drag>
|
||||
<div id="logo"></div>
|
||||
<div id="input" data-wails-no-drag>
|
||||
<input id="name" type="text">
|
||||
<button onclick="greet()">Greet</button>
|
||||
</div>
|
||||
<div id="result"></div>
|
||||
|
||||
<script src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -1,21 +0,0 @@
|
||||
|
||||
import {ready} from '@wails/runtime';
|
||||
|
||||
ready( () => {
|
||||
// Get input + focus
|
||||
let nameElement = document.getElementById("name");
|
||||
nameElement.focus();
|
||||
|
||||
// Setup the greet function
|
||||
window.greet = function () {
|
||||
|
||||
// Get name
|
||||
let name = nameElement.value;
|
||||
|
||||
// Call App.Greet(name)
|
||||
window.backend.main.App.Greet(name).then((result) => {
|
||||
// Update result with data back from App.Greet()
|
||||
document.getElementById("result").innerText = result;
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/windows"
|
||||
|
||||
"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() {
|
||||
|
||||
// Create application with options
|
||||
app := NewApp()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "{{.ProjectName}}",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
MinWidth: 400,
|
||||
MinHeight: 400,
|
||||
MaxWidth: 1280,
|
||||
MaxHeight: 1024,
|
||||
DisableResize: false,
|
||||
Fullscreen: false,
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
HideWindowOnClose: false,
|
||||
DevTools: false,
|
||||
RGBA: 0x000000FF,
|
||||
Windows: &windows.Options{
|
||||
WebviewIsTransparent: true,
|
||||
WindowBackgroundIsTranslucent: true,
|
||||
DisableWindowIcon: 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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "{{.ProjectName}}",
|
||||
"outputfilename": "{{.BinaryName}}",
|
||||
"html": "frontend/dist/index.html",
|
||||
"frontend:build": "npm run build",
|
||||
"frontend:install": "npm install",
|
||||
"author": {
|
||||
"name": "{{.AuthorName}}",
|
||||
"email": "{{.AuthorEmail}}"
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
templates, err := List()
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found these templates:")
|
||||
for _, template := range templates {
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortname(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
template, err := getTemplateByShortname("vanilla")
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found this template:")
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
options := &Options{
|
||||
ProjectName: "test",
|
||||
TemplateName: "vanilla",
|
||||
AuthorName: "Lea Anthony",
|
||||
AuthorEmail: "lea.anthony@gmail.com",
|
||||
}
|
||||
|
||||
err := Install(options)
|
||||
is2.NoErr(err)
|
||||
}
|
||||
@@ -18,14 +18,14 @@ import (
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
||||
|
||||
command := app.NewSubCommand("update", "Update the Wails CLI")
|
||||
command.LongDescription(`This command allows you to update your version of the Wails CLI.`)
|
||||
command.LongDescription(`This command allows you to update your version of Wails.`)
|
||||
|
||||
// Setup flags
|
||||
var prereleaseRequired bool
|
||||
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
||||
command.BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
|
||||
|
||||
var specificVersion string
|
||||
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
||||
command.StringFlag("version", "Install a specific version (Overrides other flags)", &specificVersion)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
@@ -143,7 +143,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
||||
logger.Print("Installing Wails " + desiredVersion + "...")
|
||||
|
||||
// Run command in non module directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
@@ -158,7 +158,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
||||
return err
|
||||
}
|
||||
fmt.Println()
|
||||
logger.Println("Wails CLI updated to " + desiredVersion)
|
||||
logger.Println("Wails updated to " + desiredVersion)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
||||
@@ -21,15 +22,27 @@ func fatal(message string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func banner(_ *clir.Cli) string {
|
||||
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
||||
func col(colour ctc.Color, text string) string {
|
||||
return fmt.Sprintf("%s%s%s", colour, text, ctc.Reset)
|
||||
}
|
||||
|
||||
func Yellow(str string) string {
|
||||
return col(ctc.ForegroundBrightYellow, str)
|
||||
}
|
||||
|
||||
func Red(str string) string {
|
||||
return col(ctc.ForegroundBrightRed, str)
|
||||
}
|
||||
|
||||
func banner(cli *clir.Cli) string {
|
||||
return fmt.Sprintf("%s %s - Go/HTML Application Framework", Yellow("Wails"), Red(version))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
app.SetBannerFunction(banner)
|
||||
|
||||
@@ -39,6 +52,10 @@ func main() {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = debug.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
err = doctor.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
@@ -62,6 +79,5 @@ func main() {
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
println("\n\nERROR: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v2.0.0-alpha.71"
|
||||
var version = "v2.0.0-alpha.26"
|
||||
|
||||
29
v2/go.mod
29
v2/go.mod
@@ -4,40 +4,27 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-git/go-billy/v5 v5.2.0 // indirect
|
||||
github.com/go-git/go-git/v5 v5.3.0
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/jackmordaunt/icns v1.0.0
|
||||
github.com/klauspost/compress v1.11.3 // indirect
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/debme v1.2.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3
|
||||
github.com/leaanthony/gosod v1.0.1
|
||||
github.com/leaanthony/gosod v0.0.4
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/leaanthony/webview2runtime v1.1.0
|
||||
github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.7.0 // indirect
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/tidwall/sjson v1.1.7
|
||||
github.com/wzshiming/ctc v1.2.3
|
||||
github.com/wzshiming/ctc v1.2.3 // indirect
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
|
||||
golang.org/x/mod v0.4.1 // indirect
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273
|
||||
golang.org/x/text v0.3.5 // indirect
|
||||
golang.org/x/tools v0.1.0
|
||||
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/tools v0.0.0-20200902012652-d1954cc86c82
|
||||
nhooyr.io/websocket v1.8.6
|
||||
)
|
||||
|
||||
149
v2/go.sum
149
v2/go.sum
@@ -1,43 +1,16 @@
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
|
||||
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.1.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.2.0 h1:GcoouCP9J+5slw2uXAocL70z8ml4A8B/H8nEPt6CLPk=
|
||||
github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc=
|
||||
github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
@@ -52,59 +25,27 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
|
||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
|
||||
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3 h1:O0uGjKnWtdEADGrkg+TyAAbZylykMwwx/MNEXn9fp+Y=
|
||||
github.com/leaanthony/go-common-file-dialog v1.0.3/go.mod h1:TGhEc9eSJgRsupZ+iH1ZgAOnEo9zp05cRH2j08RPrF0=
|
||||
github.com/leaanthony/gosod v1.0.1 h1:F+4c3DmEBfigi7oAswCV2RpQ+k4DcNbhuCZUGdBHacQ=
|
||||
github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU=
|
||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/webview2runtime v1.1.0 h1:N0pv55ift8XtqozIp4PNOtRCJ/Qdd/qzx80lUpalS4c=
|
||||
github.com/leaanthony/webview2runtime v1.1.0/go.mod h1:hH9GnWCve3DYzNaPOcPbhHQ7fodXR1QJNsnwixid4Tk=
|
||||
github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0 h1:FPGYnfxuuxqCZhrGq8nKjthEcYHgHmFbyY953Xv9cNI=
|
||||
github.com/leaanthony/winicon v0.0.0-20200606125418-4419cea822a0/go.mod h1:en5xhijl92aphrJdmRPlh4NI1L6wq3gEm0LpXAPghjU=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
@@ -113,46 +54,26 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||
github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
|
||||
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
|
||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
|
||||
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8=
|
||||
github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs=
|
||||
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/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
@@ -161,80 +82,46 @@ 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/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
|
||||
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
|
||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs=
|
||||
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package wails
|
||||
|
||||
func Init() error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package wails
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Init is called at the start of the application
|
||||
func Init() error {
|
||||
status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call()
|
||||
if status == 0 {
|
||||
return fmt.Errorf("exit status %d: %v %v", status, r, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -20,10 +20,10 @@ func (a *App) Init() error {
|
||||
}
|
||||
|
||||
// Set log levels
|
||||
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
greeting := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
flag.Parse()
|
||||
if len(*loglevel) > 0 {
|
||||
switch strings.ToLower(*loglevel) {
|
||||
if len(*greeting) > 0 {
|
||||
switch strings.ToLower(*greeting) {
|
||||
case "trace":
|
||||
a.logger.SetLogLevel(logger.TRACE)
|
||||
case "info":
|
||||
|
||||
@@ -35,7 +35,6 @@ type App struct {
|
||||
//binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
menu *subsystem.Menu
|
||||
url *subsystem.URL
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
menuManager *menumanager.Manager
|
||||
@@ -103,17 +102,8 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
// Initialise the app
|
||||
err := result.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Preflight Checks
|
||||
err = result.PreflightChecks(appoptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
return result, err
|
||||
|
||||
}
|
||||
|
||||
@@ -127,6 +117,14 @@ func (a *App) Run() error {
|
||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||
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
|
||||
a.servicebus.Debug()
|
||||
err = a.servicebus.Start()
|
||||
@@ -134,7 +132,7 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -170,19 +168,6 @@ func (a *App) Run() error {
|
||||
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
|
||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
@@ -222,14 +207,6 @@ func (a *App) Run() error {
|
||||
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)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
if err != nil {
|
||||
@@ -254,10 +231,5 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown callback
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(appoptions.Logger)
|
||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||
|
||||
// Create the menu manager
|
||||
menuManager := menumanager.NewManager()
|
||||
@@ -117,7 +118,14 @@ func (a *App) Run() error {
|
||||
var subsystemWaitGroup sync.WaitGroup
|
||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||
ctx, cancel := context.WithCancel(parentContext)
|
||||
defer cancel()
|
||||
|
||||
// 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
|
||||
a.servicebus.Debug()
|
||||
@@ -126,7 +134,7 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -204,14 +212,6 @@ func (a *App) Run() error {
|
||||
// Generate backend.js
|
||||
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)
|
||||
a.logger.Trace("Bridge.Run() exited")
|
||||
if err != nil {
|
||||
@@ -236,10 +236,6 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown callback
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback()
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build !windows
|
||||
|
||||
package app
|
||||
|
||||
import "github.com/wailsapp/wails/v2/pkg/options"
|
||||
|
||||
func (a *App) PreflightChecks(options *options.App) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//+build windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/ffenestri/windows/wv2runtime"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
func (a *App) PreflightChecks(options *options.App) error {
|
||||
|
||||
// Process the webview2 runtime situation. We can pass a strategy in via the `webview2` flag for `wails build`.
|
||||
// This will determine how wv2runtime.Process will handle a lack of valid runtime.
|
||||
err := wv2runtime.Process()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func (a *App) Run() error {
|
||||
// Default app options
|
||||
var port = 8080
|
||||
var ip = "localhost"
|
||||
var SuppressLogging = false
|
||||
var supressLogging = false
|
||||
var debugMode = false
|
||||
|
||||
// Create CLI
|
||||
@@ -89,7 +89,7 @@ func (a *App) Run() error {
|
||||
cli.IntFlag("p", "Port to serve on", &port)
|
||||
cli.StringFlag("i", "IP to serve on", &ip)
|
||||
cli.BoolFlag("d", "Debug mode", &debugMode)
|
||||
cli.BoolFlag("q", "Suppress logging", &SuppressLogging)
|
||||
cli.BoolFlag("q", "Supress logging", &supressLogging)
|
||||
|
||||
// Setup main action
|
||||
cli.Action(func() error {
|
||||
@@ -98,8 +98,8 @@ func (a *App) Run() error {
|
||||
a.webserver.SetPort(port)
|
||||
a.webserver.SetIP(ip)
|
||||
a.webserver.SetBindings(a.bindings)
|
||||
// Log information (if we aren't Suppressing it)
|
||||
if !SuppressLogging {
|
||||
// Log information (if we aren't supressing it)
|
||||
if !supressLogging {
|
||||
cli.PrintBanner()
|
||||
a.logger.Info("Running server at %s", a.webserver.URL())
|
||||
}
|
||||
|
||||
@@ -14,6 +14,30 @@ import (
|
||||
"github.com/leaanthony/slicer"
|
||||
)
|
||||
|
||||
const _comment = `
|
||||
|
||||
const backend = {
|
||||
main: {
|
||||
"xbarApp": {
|
||||
"GetCategories": () => {
|
||||
window.backend.main.xbarApp.GetCategories.call(arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} arg1
|
||||
*/
|
||||
"InstallPlugin": (arg1) => {
|
||||
window.backend.main.xbarApp.InstallPlugin.call(arguments);
|
||||
},
|
||||
"GetPlugins": () => {
|
||||
window.backend.main.xbarApp.GetPlugins.call(arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default backend;`
|
||||
|
||||
//go:embed assets/package.json
|
||||
var packageJSON []byte
|
||||
|
||||
@@ -34,7 +58,7 @@ const backend = {`)
|
||||
sortedPackageNames.Add(packageName)
|
||||
}
|
||||
sortedPackageNames.Sort()
|
||||
sortedPackageNames.Each(func(packageName string) {
|
||||
for _, packageName := range sortedPackageNames.AsSlice() {
|
||||
packages := store[packageName]
|
||||
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||
output.WriteString("\n")
|
||||
@@ -43,8 +67,7 @@ const backend = {`)
|
||||
sortedStructNames.Add(structName)
|
||||
}
|
||||
sortedStructNames.Sort()
|
||||
|
||||
sortedStructNames.Each(func(structName string) {
|
||||
for _, structName := range sortedStructNames.AsSlice() {
|
||||
structs := packages[structName]
|
||||
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||
output.WriteString("\n")
|
||||
@@ -55,7 +78,7 @@ const backend = {`)
|
||||
}
|
||||
sortedMethodNames.Sort()
|
||||
|
||||
sortedMethodNames.Each(func(methodName string) {
|
||||
for _, methodName := range sortedMethodNames.AsSlice() {
|
||||
methodDetails := structs[methodName]
|
||||
output.WriteString(" /**\n")
|
||||
output.WriteString(" * " + methodName + "\n")
|
||||
@@ -76,8 +99,6 @@ const backend = {`)
|
||||
}
|
||||
returnType += ">"
|
||||
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
||||
} else {
|
||||
returnType = "Promise<void>"
|
||||
}
|
||||
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
||||
output.WriteString(" */\n")
|
||||
@@ -88,29 +109,25 @@ const backend = {`)
|
||||
output.WriteString("\n")
|
||||
output.WriteString(fmt.Sprintf(" },"))
|
||||
output.WriteString("\n")
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
output.WriteString(fmt.Sprintf(" }"))
|
||||
output.WriteString("\n")
|
||||
})
|
||||
|
||||
}
|
||||
output.WriteString(fmt.Sprintf(" }\n"))
|
||||
output.WriteString("\n")
|
||||
})
|
||||
}
|
||||
|
||||
output.WriteString(`};
|
||||
export default backend;`)
|
||||
output.WriteString("\n")
|
||||
|
||||
// TODO: Make this configurable in wails.json
|
||||
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !fs.DirExists(dirname) {
|
||||
err := fs.MkDirs(dirname)
|
||||
err := fs.Mkdir(dirname)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -144,8 +161,6 @@ func goTypeToJSDocType(input string) string {
|
||||
return "number"
|
||||
case input == "bool":
|
||||
return "boolean"
|
||||
case input == "[]byte":
|
||||
return "string"
|
||||
case strings.HasPrefix(input, "[]"):
|
||||
arrayType := goTypeToJSDocType(input[2:])
|
||||
return "Array.<" + arrayType + ">"
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_goTypeToJSDocType(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "string",
|
||||
input: "string",
|
||||
want: "string",
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
input: "error",
|
||||
want: "Error",
|
||||
},
|
||||
{
|
||||
name: "int",
|
||||
input: "int",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "int32",
|
||||
input: "int32",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "uint",
|
||||
input: "uint",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "uint32",
|
||||
input: "uint32",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "float32",
|
||||
input: "float32",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "float64",
|
||||
input: "float64",
|
||||
want: "number",
|
||||
},
|
||||
{
|
||||
name: "bool",
|
||||
input: "bool",
|
||||
want: "boolean",
|
||||
},
|
||||
{
|
||||
name: "[]byte",
|
||||
input: "[]byte",
|
||||
want: "string",
|
||||
},
|
||||
{
|
||||
name: "[]int",
|
||||
input: "[]int",
|
||||
want: "Array.<number>",
|
||||
},
|
||||
{
|
||||
name: "[]bool",
|
||||
input: "[]bool",
|
||||
want: "Array.<boolean>",
|
||||
},
|
||||
{
|
||||
name: "anything else",
|
||||
input: "foo",
|
||||
want: "any",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := goTypeToJSDocType(tt.input); got != tt.want {
|
||||
t.Errorf("goTypeToJSDocType() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,6 @@ type BridgeClient struct {
|
||||
messageCache chan string
|
||||
}
|
||||
|
||||
func (b BridgeClient) DeleteTrayMenuByID(id string) {
|
||||
b.session.sendMessage("TD" + id)
|
||||
}
|
||||
|
||||
func NewBridgeClient() *BridgeClient {
|
||||
return &BridgeClient{
|
||||
messageCache: make(chan string, 100),
|
||||
@@ -26,23 +22,16 @@ func (b BridgeClient) Quit() {
|
||||
}
|
||||
|
||||
func (b BridgeClient) NotifyEvent(message string) {
|
||||
b.session.sendMessage("n" + message)
|
||||
//b.session.sendMessage("n" + message)
|
||||
b.session.log.Info("NotifyEvent: %s", message)
|
||||
b.session.log.Info("NotifyEvent unsupported in Bridge mode")
|
||||
}
|
||||
|
||||
func (b BridgeClient) CallResult(message string) {
|
||||
b.session.sendMessage("c" + message)
|
||||
}
|
||||
|
||||
func (b BridgeClient) OpenFileDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
// Handled by dialog_client
|
||||
}
|
||||
|
||||
func (b BridgeClient) OpenMultipleFilesDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
// Handled by dialog_client
|
||||
}
|
||||
|
||||
func (b BridgeClient) OpenDirectoryDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
func (b BridgeClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
// Handled by dialog_client
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -19,9 +19,6 @@ type DialogClient struct {
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func (d *DialogClient) DeleteTrayMenuByID(id string) {
|
||||
}
|
||||
|
||||
func NewDialogClient(log *logger.Logger) *DialogClient {
|
||||
return &DialogClient{
|
||||
log: log,
|
||||
@@ -37,11 +34,7 @@ func (d *DialogClient) NotifyEvent(message string) {
|
||||
func (d *DialogClient) CallResult(message string) {
|
||||
}
|
||||
|
||||
func (d *DialogClient) OpenDirectoryDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
}
|
||||
func (d *DialogClient) OpenFileDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
}
|
||||
func (d *DialogClient) OpenMultipleFilesDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
func (d *DialogClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
}
|
||||
|
||||
func (d *DialogClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -18,12 +18,6 @@ import (
|
||||
//go:embed darwin.js
|
||||
var darwinRuntime string
|
||||
|
||||
//go:embed windows.js
|
||||
var windowsRuntime string
|
||||
|
||||
//go:embed linux.js
|
||||
var linuxRuntime string
|
||||
|
||||
// session represents a single websocket session
|
||||
type session struct {
|
||||
bindings string
|
||||
@@ -90,10 +84,6 @@ func (s *session) start(firstSession bool) {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
wailsRuntime = darwinRuntime
|
||||
case "windows":
|
||||
wailsRuntime = windowsRuntime
|
||||
case "linux":
|
||||
wailsRuntime = linuxRuntime
|
||||
default:
|
||||
log.Fatal("platform not supported")
|
||||
}
|
||||
@@ -102,10 +92,7 @@ func (s *session) start(firstSession bool) {
|
||||
s.log.Info(bindingsMessage)
|
||||
bootstrapMessage := bindingsMessage + wailsRuntime
|
||||
|
||||
err := s.sendMessage("b" + bootstrapMessage)
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
}
|
||||
s.sendMessage("b" + bootstrapMessage)
|
||||
|
||||
// Send menus
|
||||
traymenus, err := s.menumanager.GetTrayMenus()
|
||||
@@ -114,10 +101,7 @@ func (s *session) start(firstSession bool) {
|
||||
}
|
||||
|
||||
for _, trayMenu := range traymenus {
|
||||
err := s.sendMessage("TS" + trayMenu)
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
}
|
||||
s.sendMessage("TS" + trayMenu)
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -146,10 +130,7 @@ func (s *session) start(firstSession bool) {
|
||||
|
||||
// Shutdown
|
||||
func (s *session) Shutdown() {
|
||||
err := s.conn.Close()
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
}
|
||||
s.conn.Close()
|
||||
s.done = true
|
||||
s.log.Info("session %v exit", s.Identifier())
|
||||
}
|
||||
@@ -165,16 +146,10 @@ func (s *session) writePump() {
|
||||
s.Shutdown()
|
||||
return
|
||||
case msg, ok := <-s.writeChan:
|
||||
err := s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
}
|
||||
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||
if !ok {
|
||||
s.log.Debug("writeChan was closed!")
|
||||
err := s.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
if err != nil {
|
||||
s.log.Error(err.Error())
|
||||
}
|
||||
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,89 +0,0 @@
|
||||
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()
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
# 3rd Party Licenses
|
||||
|
||||
## Webview
|
||||
Whilst not using the library directly, there is certainly some code that is inspired by or used from the webview library.
|
||||
|
||||
Homepage: https://github.com/webview/webview
|
||||
License: https://github.com/webview/webview/blob/master/LICENSE
|
||||
|
||||
## vec
|
||||
Homepage: https://github.com/rxi/vec
|
||||
License: https://github.com/rxi/vec/blob/master/LICENSE
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// +build !windows
|
||||
|
||||
|
||||
//
|
||||
// Created by Lea Anthony on 6/1/21.
|
||||
//
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
||||
#include <objc/objc-runtime.h>
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "string.h"
|
||||
|
||||
@@ -82,10 +82,10 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
||||
FREE_AND_SET(contextMenu->contextMenuData, contextMenuData);
|
||||
|
||||
// Grab the content view and show the menu
|
||||
id contentView = msg_reg(mainWindow, s("contentView"));
|
||||
id contentView = msg(mainWindow, s("contentView"));
|
||||
|
||||
// Get the triggering event
|
||||
id menuEvent = msg_reg(mainWindow, s("currentEvent"));
|
||||
id menuEvent = msg(mainWindow, s("currentEvent"));
|
||||
|
||||
if( contextMenu->nsmenu == NULL ) {
|
||||
// GetMenu creates the NSMenu
|
||||
@@ -93,7 +93,7 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
||||
}
|
||||
|
||||
// Show popup
|
||||
((id(*)(id, SEL, id, id, id))objc_msgSend)(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
||||
msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// Credit: https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf
|
||||
#pragma once
|
||||
|
||||
typedef enum _WINDOWCOMPOSITIONATTRIB
|
||||
{
|
||||
WCA_UNDEFINED = 0,
|
||||
WCA_NCRENDERING_ENABLED = 1,
|
||||
WCA_NCRENDERING_POLICY = 2,
|
||||
WCA_TRANSITIONS_FORCEDISABLED = 3,
|
||||
WCA_ALLOW_NCPAINT = 4,
|
||||
WCA_CAPTION_BUTTON_BOUNDS = 5,
|
||||
WCA_NONCLIENT_RTL_LAYOUT = 6,
|
||||
WCA_FORCE_ICONIC_REPRESENTATION = 7,
|
||||
WCA_EXTENDED_FRAME_BOUNDS = 8,
|
||||
WCA_HAS_ICONIC_BITMAP = 9,
|
||||
WCA_THEME_ATTRIBUTES = 10,
|
||||
WCA_NCRENDERING_EXILED = 11,
|
||||
WCA_NCADORNMENTINFO = 12,
|
||||
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
|
||||
WCA_VIDEO_OVERLAY_ACTIVE = 14,
|
||||
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
|
||||
WCA_DISALLOW_PEEK = 16,
|
||||
WCA_CLOAK = 17,
|
||||
WCA_CLOAKED = 18,
|
||||
WCA_ACCENT_POLICY = 19,
|
||||
WCA_FREEZE_REPRESENTATION = 20,
|
||||
WCA_EVER_UNCLOAKED = 21,
|
||||
WCA_VISUAL_OWNER = 22,
|
||||
WCA_HOLOGRAPHIC = 23,
|
||||
WCA_EXCLUDED_FROM_DDA = 24,
|
||||
WCA_PASSIVEUPDATEMODE = 25,
|
||||
WCA_USEDARKMODECOLORS = 26,
|
||||
WCA_LAST = 27
|
||||
} WINDOWCOMPOSITIONATTRIB;
|
||||
|
||||
typedef struct _WINDOWCOMPOSITIONATTRIBDATA
|
||||
{
|
||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
||||
PVOID pvData;
|
||||
SIZE_T cbData;
|
||||
} WINDOWCOMPOSITIONATTRIBDATA;
|
||||
|
||||
typedef enum _ACCENT_STATE
|
||||
{
|
||||
ACCENT_DISABLED = 0,
|
||||
ACCENT_ENABLE_GRADIENT = 1,
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
||||
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
|
||||
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
|
||||
ACCENT_INVALID_STATE = 6
|
||||
} ACCENT_STATE;
|
||||
|
||||
typedef struct _ACCENT_POLICY
|
||||
{
|
||||
ACCENT_STATE AccentState;
|
||||
DWORD AccentFlags;
|
||||
DWORD GradientColor;
|
||||
DWORD AnimationId;
|
||||
} ACCENT_POLICY;
|
||||
|
||||
typedef BOOL (WINAPI *pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
|
||||
|
||||
typedef BOOL (WINAPI *pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
|
||||
@@ -13,17 +13,17 @@ import (
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
#cgo windows CXXFLAGS: -std=c++11
|
||||
#cgo windows,amd64 LDFLAGS: -L./windows/x64 -lWebView2Loader -lgdi32 -lole32 -lShlwapi -luser32 -loleaut32 -ldwmapi
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@@ -65,6 +65,14 @@ func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuMa
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplication creates a new Application with the default config
|
||||
func NewApplication(logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: options.Default,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Application) freeMemory() {
|
||||
for _, mem := range a.memory {
|
||||
// fmt.Printf("Freeing memory: %+v\n", mem)
|
||||
@@ -130,9 +138,10 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
||||
// Set debug if needed
|
||||
C.SetDebug(app, a.bool2Cint(debug))
|
||||
|
||||
if a.config.Frameless {
|
||||
C.DisableFrame(a.app)
|
||||
}
|
||||
// TODO: Move frameless to Linux options
|
||||
// if a.config.Frameless {
|
||||
// C.DisableFrame(a.app)
|
||||
// }
|
||||
|
||||
if a.config.RGBA != 0 {
|
||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
||||
@@ -164,7 +173,7 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
||||
// Oh no! We couldn't initialise the application
|
||||
a.logger.Fatal("Cannot initialise Application.")
|
||||
}
|
||||
//println("\n\n\n\n\n\nhererererer\n\n\n\n")
|
||||
|
||||
a.freeMemory()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
#ifndef __FFENESTRI_H__
|
||||
#define __FFENESTRI_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
struct Application;
|
||||
|
||||
@@ -41,16 +37,8 @@ extern void DarkModeEnabled(struct Application*, char *callbackID);
|
||||
extern void SetApplicationMenu(struct Application*, const char *);
|
||||
extern void AddTrayMenu(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 AddContextMenu(struct Application*, char *contextMenuJSON);
|
||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
||||
extern void WebviewIsTransparent(struct Application*);
|
||||
extern void WindowBackgroundIsTranslucent(struct Application*);
|
||||
extern void* GetWindowHandle(struct Application*);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
// +build !windows
|
||||
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
// Client is our implementation of messageDispatcher.Client
|
||||
// Client is our implentation of messageDispatcher.Client
|
||||
type Client struct {
|
||||
app *Application
|
||||
logger logger.CustomLogger
|
||||
@@ -126,72 +128,17 @@ func (c *Client) WindowSetColour(colour int) {
|
||||
C.SetColour(c.app.app, r, g, b, a)
|
||||
}
|
||||
|
||||
// OpenFileDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenFileDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
filters := []string{}
|
||||
if runtime.GOOS == "darwin" {
|
||||
for _, filter := range dialogOptions.Filters {
|
||||
filters = append(filters, strings.Split(filter.Pattern, ",")...)
|
||||
}
|
||||
}
|
||||
// OpenDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
C.OpenDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(strings.Join(filters, ";")),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
||||
c.app.bool2Cint(false),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.ResolvesAliases),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// OpenDirectoryDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDirectoryDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
filters := []string{}
|
||||
if runtime.GOOS == "darwin" {
|
||||
for _, filter := range dialogOptions.Filters {
|
||||
filters = append(filters, strings.Split(filter.Pattern, ",")...)
|
||||
}
|
||||
}
|
||||
C.OpenDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(strings.Join(filters, ";")),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(false), // Files
|
||||
c.app.bool2Cint(true), // Directories
|
||||
c.app.bool2Cint(false), // Multiple
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.ResolvesAliases),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
// OpenMultipleFilesDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenMultipleFilesDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
filters := []string{}
|
||||
if runtime.GOOS == "darwin" {
|
||||
for _, filter := range dialogOptions.Filters {
|
||||
filters = append(filters, strings.Split(filter.Pattern, ",")...)
|
||||
}
|
||||
}
|
||||
C.OpenDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(strings.Join(filters, ";")),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
||||
c.app.bool2Cint(true),
|
||||
c.app.bool2Cint(dialogOptions.AllowMultiple),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.ResolvesAliases),
|
||||
@@ -201,16 +148,10 @@ func (c *Client) OpenMultipleFilesDialog(dialogOptions *dialog.OpenDialog, callb
|
||||
|
||||
// SaveDialog will open a dialog with the given title and filter
|
||||
func (c *Client) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||
filters := []string{}
|
||||
if runtime.GOOS == "darwin" {
|
||||
for _, filter := range dialogOptions.Filters {
|
||||
filters = append(filters, strings.Split(filter.Pattern, ",")...)
|
||||
}
|
||||
}
|
||||
C.SaveDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(strings.Join(filters, ";")),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
@@ -267,7 +208,3 @@ func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
||||
}
|
||||
|
||||
func (c *Client) DeleteTrayMenuByID(id string) {
|
||||
C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id))
|
||||
}
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#include "ffenestri.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/leaanthony/go-common-file-dialog/cfd"
|
||||
"golang.org/x/sys/windows"
|
||||
"log"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
// Client is our implementation of messageDispatcher.Client
|
||||
type Client struct {
|
||||
app *Application
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
func newClient(app *Application) *Client {
|
||||
return &Client{
|
||||
app: app,
|
||||
logger: app.logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Quit the application
|
||||
func (c *Client) Quit() {
|
||||
c.app.logger.Trace("Got shutdown message")
|
||||
C.Quit(c.app.app)
|
||||
}
|
||||
|
||||
// NotifyEvent will pass on the event message to the frontend
|
||||
func (c *Client) NotifyEvent(message string) {
|
||||
eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("eventMessage = %+v", eventMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(eventMessage))
|
||||
}
|
||||
|
||||
// CallResult contains the result of the call from JS
|
||||
func (c *Client) CallResult(message string) {
|
||||
callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("callbackMessage = %+v", callbackMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(callbackMessage))
|
||||
}
|
||||
|
||||
// WindowSetTitle sets the window title to the given string
|
||||
func (c *Client) WindowSetTitle(title string) {
|
||||
C.SetTitle(c.app.app, c.app.string2CString(title))
|
||||
}
|
||||
|
||||
// WindowFullscreen will set the window to be fullscreen
|
||||
func (c *Client) WindowFullscreen() {
|
||||
C.Fullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnFullscreen will unfullscreen the window
|
||||
func (c *Client) WindowUnFullscreen() {
|
||||
C.UnFullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowShow will show the window
|
||||
func (c *Client) WindowShow() {
|
||||
C.Show(c.app.app)
|
||||
}
|
||||
|
||||
// WindowHide will hide the window
|
||||
func (c *Client) WindowHide() {
|
||||
C.Hide(c.app.app)
|
||||
}
|
||||
|
||||
// WindowCenter will hide the window
|
||||
func (c *Client) WindowCenter() {
|
||||
C.Center(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMaximise will maximise the window
|
||||
func (c *Client) WindowMaximise() {
|
||||
C.Maximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMinimise will minimise the window
|
||||
func (c *Client) WindowMinimise() {
|
||||
C.Minimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnmaximise will unmaximise the window
|
||||
func (c *Client) WindowUnmaximise() {
|
||||
C.Unmaximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnminimise will unminimise the window
|
||||
func (c *Client) WindowUnminimise() {
|
||||
C.Unminimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowPosition will position the window to x,y on the
|
||||
// monitor that the window is mostly on
|
||||
func (c *Client) WindowPosition(x int, y int) {
|
||||
C.SetPosition(c.app.app, C.int(x), C.int(y))
|
||||
}
|
||||
|
||||
// WindowSize will resize the window to the given
|
||||
// width and height
|
||||
func (c *Client) WindowSize(width int, height int) {
|
||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
// WindowSetMinSize sets the minimum window size
|
||||
func (c *Client) WindowSetMinSize(width int, height int) {
|
||||
C.SetMinWindowSize(c.app.app, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
// WindowSetMaxSize sets the maximum window size
|
||||
func (c *Client) WindowSetMaxSize(width int, height int) {
|
||||
C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
// WindowSetColour sets the window colour
|
||||
func (c *Client) WindowSetColour(colour int) {
|
||||
r, g, b, a := intToColour(colour)
|
||||
C.SetColour(c.app.app, r, g, b, a)
|
||||
}
|
||||
|
||||
func convertFilters(filters []dialog.FileFilter) []cfd.FileFilter {
|
||||
var result []cfd.FileFilter
|
||||
for _, filter := range filters {
|
||||
result = append(result, cfd.FileFilter(filter))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// OpenFileDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenFileDialog(options *dialog.OpenDialog, callbackID string) {
|
||||
config := cfd.DialogConfig{
|
||||
Folder: options.DefaultDirectory,
|
||||
FileFilters: convertFilters(options.Filters),
|
||||
FileName: options.DefaultFilename,
|
||||
}
|
||||
thisdialog, err := cfd.NewOpenFileDialog(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
thisdialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app)))
|
||||
defer func(thisdialog cfd.OpenFileDialog) {
|
||||
err := thisdialog.Release()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(thisdialog)
|
||||
result, err := thisdialog.ShowAndGetResult()
|
||||
if err != nil && err != cfd.ErrorCancelled {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dispatcher.DispatchMessage("DO" + callbackID + "|" + result)
|
||||
}
|
||||
|
||||
// OpenDirectoryDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDirectoryDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
config := cfd.DialogConfig{
|
||||
Title: dialogOptions.Title,
|
||||
Role: "PickFolder",
|
||||
Folder: dialogOptions.DefaultDirectory,
|
||||
}
|
||||
thisDialog, err := cfd.NewSelectFolderDialog(config)
|
||||
if err != nil {
|
||||
log.Fatal()
|
||||
}
|
||||
thisDialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app)))
|
||||
defer func(thisDialog cfd.SelectFolderDialog) {
|
||||
err := thisDialog.Release()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(thisDialog)
|
||||
result, err := thisDialog.ShowAndGetResult()
|
||||
if err != nil && err != cfd.ErrorCancelled {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dispatcher.DispatchMessage("DD" + callbackID + "|" + result)
|
||||
}
|
||||
|
||||
// OpenMultipleFilesDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenMultipleFilesDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||
config := cfd.DialogConfig{
|
||||
Title: dialogOptions.Title,
|
||||
Role: "OpenMultipleFiles",
|
||||
FileFilters: convertFilters(dialogOptions.Filters),
|
||||
FileName: dialogOptions.DefaultFilename,
|
||||
Folder: dialogOptions.DefaultDirectory,
|
||||
}
|
||||
thisdialog, err := cfd.NewOpenMultipleFilesDialog(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
thisdialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app)))
|
||||
defer func(thisdialog cfd.OpenMultipleFilesDialog) {
|
||||
err := thisdialog.Release()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}(thisdialog)
|
||||
result, err := thisdialog.ShowAndGetResults()
|
||||
if err != nil && err != cfd.ErrorCancelled {
|
||||
log.Fatal(err)
|
||||
}
|
||||
resultJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dispatcher.DispatchMessage("D*" + callbackID + "|" + string(resultJSON))
|
||||
}
|
||||
|
||||
// SaveDialog will open a dialog with the given title and filter
|
||||
func (c *Client) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||
saveDialog, err := cfd.NewSaveFileDialog(cfd.DialogConfig{
|
||||
Title: dialogOptions.Title,
|
||||
Role: "SaveFile",
|
||||
FileFilters: convertFilters(dialogOptions.Filters),
|
||||
FileName: dialogOptions.DefaultFilename,
|
||||
Folder: dialogOptions.DefaultDirectory,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
saveDialog.SetParentWindowHandle(uintptr(C.GetWindowHandle(c.app.app)))
|
||||
err = saveDialog.Show()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
result, err := saveDialog.GetResult()
|
||||
if err != nil && err != cfd.ErrorCancelled {
|
||||
log.Fatal(err)
|
||||
}
|
||||
dispatcher.DispatchMessage("DS" + callbackID + "|" + result)
|
||||
}
|
||||
|
||||
// MessageDialog will open a message dialog with the given options
|
||||
func (c *Client) MessageDialog(options *dialog.MessageDialog, callbackID string) {
|
||||
|
||||
title, err := syscall.UTF16PtrFromString(options.Title)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
message, err := syscall.UTF16PtrFromString(options.Message)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
var flags uint32
|
||||
switch options.Type {
|
||||
case dialog.InfoDialog:
|
||||
flags = windows.MB_OK | windows.MB_ICONINFORMATION
|
||||
case dialog.ErrorDialog:
|
||||
flags = windows.MB_ICONERROR | windows.MB_OK
|
||||
case dialog.QuestionDialog:
|
||||
flags = windows.MB_YESNO
|
||||
case dialog.WarningDialog:
|
||||
flags = windows.MB_OK | windows.MB_ICONWARNING
|
||||
}
|
||||
|
||||
button, _ := windows.MessageBox(windows.HWND(C.GetWindowHandle(c.app.app)), message, title, flags|windows.MB_SYSTEMMODAL)
|
||||
// This maps MessageBox return values to strings
|
||||
responses := []string{"", "Ok", "Cancel", "Abort", "Retry", "Ignore", "Yes", "No", "", "", "Try Again", "Continue"}
|
||||
result := "Error"
|
||||
if int(button) < len(responses) {
|
||||
result = responses[button]
|
||||
}
|
||||
dispatcher.DispatchMessage("DM" + callbackID + "|" + result)
|
||||
}
|
||||
|
||||
// DarkModeEnabled sets the application to use dark mode
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
|
||||
// SetApplicationMenu sets the application menu
|
||||
func (c *Client) SetApplicationMenu(applicationMenuJSON string) {
|
||||
C.SetApplicationMenu(c.app.app, c.app.string2CString(applicationMenuJSON))
|
||||
}
|
||||
|
||||
// SetTrayMenu sets the tray menu
|
||||
func (c *Client) SetTrayMenu(trayMenuJSON string) {
|
||||
C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON))
|
||||
}
|
||||
|
||||
// UpdateTrayMenuLabel updates a tray menu label
|
||||
func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
||||
C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON))
|
||||
}
|
||||
|
||||
// UpdateContextMenu will update the current context menu
|
||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
||||
}
|
||||
|
||||
// DeleteTrayMenuByID will remove a tray menu based on the given id
|
||||
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,9 +48,6 @@ func (a *Application) processPlatformSettings() error {
|
||||
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
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
@@ -90,10 +87,5 @@ func (a *Application) processPlatformSettings() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Process URL Handlers
|
||||
if a.config.Mac.URLHandlers != nil {
|
||||
C.HasURLHandlers(a.app)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,51 +11,24 @@
|
||||
#include "hashmap.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
typedef struct {
|
||||
long maj;
|
||||
long min;
|
||||
long patch;
|
||||
} OSVersion;
|
||||
|
||||
// Macros to make it slightly more sane
|
||||
#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 s(str) sel_registerName(str)
|
||||
#define u(str) sel_getUid(str)
|
||||
#define str(input) ((id(*)(id, SEL, const char *))objc_msgSend)(c("NSString"), s("stringWithUTF8String:"), 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_reg(input, s("UTF8String"))
|
||||
#define url(input) msg_id(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||
#define ALLOC(classname) msg_reg(c(classname), s("alloc"))
|
||||
#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"))
|
||||
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend)(processInfo, s("operatingSystemVersion"));
|
||||
#endif
|
||||
|
||||
#if defined (__x86_64__)
|
||||
#define str(input) msg(c("NSString"), s("stringWithUTF8String:"), input)
|
||||
#define strunicode(input) msg(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
||||
#define cstr(input) (const char *)msg(input, s("UTF8String"))
|
||||
#define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||
#define ALLOC(classname) msg(c(classname), s("alloc"))
|
||||
#define ALLOC_INIT(classname) msg(msg(c(classname), s("alloc")), s("init"))
|
||||
#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_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend_stret)(processInfo, s("operatingSystemVersion"));
|
||||
#endif
|
||||
|
||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))
|
||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))msg)(receiver, s("backingScaleFactor"))
|
||||
|
||||
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
|
||||
#define MAIN_WINDOW_CALL(str) msg_reg(app->mainWindow, s((str)))
|
||||
#define MAIN_WINDOW_CALL(str) msg(app->mainWindow, s((str)))
|
||||
|
||||
#define NSBackingStoreBuffered 2
|
||||
|
||||
@@ -93,10 +66,6 @@ typedef struct {
|
||||
#define NSControlStateValueOff 0
|
||||
#define NSControlStateValueOn 1
|
||||
|
||||
#define NSApplicationActivationPolicyRegular 0
|
||||
#define NSApplicationActivationPolicyAccessory 1
|
||||
#define NSApplicationActivationPolicyProhibited 2
|
||||
|
||||
// Unbelievably, if the user swaps their button preference
|
||||
// then right buttons are reported as left buttons
|
||||
#define NSEventMaskLeftMouseDown 1 << 1
|
||||
@@ -125,8 +94,6 @@ typedef struct {
|
||||
#define NSAlertSecondButtonReturn 1001
|
||||
#define NSAlertThirdButtonReturn 1002
|
||||
|
||||
#define BrokenImage "iVBORw0KGgoAAAANSUhEUgAAABAAAAASCAMAAABl5a5YAAABj1BMVEWopan///+koqSWk5P9/v3///////////+AgACMiovz8/PB0fG9z+3i4+WysbGBfX1Erh80rACLiYqBxolEsDhHlDEbqQDDx+CNho7W1tj4+/bw+O3P5Mn4/f/W1tbK6sX////b2dn////////////8/Pz6+vro6Ojj4+P////G1PL////EzNydmp2cmZnd3eDF1PHs8v/o8P/Q3vrS3vfE0vCdmpqZkpr19/3N2vXI1vPH1fOgnqDg6frP3PbCytvHx8irqq6HhIZtuGtjnlZetU1Xs0NWskBNsi7w9v/d6P7w9P3S4Pzr8Pvl7PrY5PrU4PjQ3fjD1Ozo6Om30NjGzNi7ubm34K+UxKmbnaWXlJeUjpSPi4tppF1TtjxSsTf2+f7L2PTr7e3H2+3V7+q+0uXg4OPg4eLR1uG7z+Hg4ODGzODV2N7V1trP5dmxzs65vcfFxMWq0cKxxr+/vr+0s7apxbWaxrCv2qao05+dlp2Uuo2Dn4F8vIB6xnyAoHmAym9zqGpctENLryNFsgoblJpnAAAAKnRSTlP+hP7+5ZRmYgL+/f39/f39/f38/Pz8/Pv69+7j083My8GocnBPTTMWEgjxeITOAAABEklEQVQY0y3KZXuCYBiG4ceYuu7u3nyVAaKOMBBQ7O5Yd3f3fvheDnd9u8/jBkGwNxP6sjOWVQvY/ftrzfT6bd3yEhCnYZqiaYoKiwX/gXkFiHySTcUTLJMsZ9v8nQvgssWYOEKedKpcOO6CUXD5IlGEY5hLUbyDAAZ6HRf1bnkoavOsFQibg+Q4nuNYL+ON5PHD5nBaraRVyxnzGf6BJzUi2QQCQgMyk8tleL7dg1owpJ17D5IkvV100EingeOopPyo6vfAuXF+9hbDTknZCIaUoeK4efKwG4iT6xDewd7imGlid7gGwv37b6Oh9jwaTdOf/Tc1qH7UZVmuP6G5qZfBr9cAGNy4KiDd4tXIs7tS+QO9aUKvPAIKuQAAAABJRU5ErkJggg=="
|
||||
|
||||
struct Application;
|
||||
int releaseNSObject(void *const context, struct hashmap_element_s *const e);
|
||||
void TitlebarAppearsTransparent(struct Application* app);
|
||||
@@ -143,12 +110,6 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
||||
//void SetContextMenus(struct Application* app, const char *);
|
||||
void AddTrayMenu(struct Application* app, const char *);
|
||||
|
||||
void SetActivationPolicy(struct Application* app, int policy);
|
||||
|
||||
void* lookupStringConstant(id constantName);
|
||||
|
||||
void HasURLHandlers(struct Application* app);
|
||||
|
||||
id createImageFromBase64Data(const char *data, bool isTemplateImage);
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,6 @@
|
||||
#ifndef __FFENESTRI_LINUX_H__
|
||||
#define __FFENESTRI_LINUX_H__
|
||||
|
||||
#include "common.h"
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include <time.h>
|
||||
@@ -12,10 +11,9 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
// References to assets
|
||||
extern const unsigned char *assets[];
|
||||
extern const unsigned char runtime;
|
||||
|
||||
#include "icon.h"
|
||||
#include "assets.h"
|
||||
extern const char *icon[];
|
||||
|
||||
// Constants
|
||||
#define PRIMARY_MOUSE_BUTTON 1
|
||||
@@ -25,6 +23,17 @@ extern const unsigned char runtime;
|
||||
// MAIN DEBUG FLAG
|
||||
int debug;
|
||||
|
||||
// Credit: https://stackoverflow.com/a/8465083
|
||||
char *concat(const char *s1, const char *s2)
|
||||
{
|
||||
const size_t len1 = strlen(s1);
|
||||
const size_t len2 = strlen(s2);
|
||||
char *result = malloc(len1 + len2 + 1);
|
||||
memcpy(result, s1, len1);
|
||||
memcpy(result + len1, s2, len2 + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Debug works like sprintf but mutes if the global debug flag is true
|
||||
// Credit: https://stackoverflow.com/a/20639708
|
||||
void Debug(char *message, ...)
|
||||
@@ -303,17 +312,17 @@ void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||
}
|
||||
|
||||
// SetColour sets the colour of the webview to the given colour string
|
||||
void SetColour(struct Application *app, int red, int green, int blue, int alpha)
|
||||
int SetColour(struct Application *app, const char *colourString)
|
||||
{
|
||||
// GdkRGBA rgba;
|
||||
// rgba.
|
||||
// gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||
// if (result == FALSE)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
// // Debug("Setting webview colour to: %s", colourString);
|
||||
// webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||
GdkRGBA rgba;
|
||||
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||
if (result == FALSE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Debug("Setting webview colour to: %s", colourString);
|
||||
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DisableFrame disables the window frame
|
||||
@@ -514,11 +523,6 @@ static void load_finished_cb(WebKitWebView *webView,
|
||||
Debug("Binding Methods");
|
||||
syncEval(app, app->bindings);
|
||||
|
||||
// Setup IPC commands
|
||||
Debug("Setting up IPC methods");
|
||||
const char *invoke = "window.wailsInvoke=function(message){window.webkit.messageHandlers.external.postMessage(message);};window.wailsDrag=function(message){window.webkit.messageHandlers.windowDrag.postMessage(message);};window.wailsContextMenuMessage=function(message){window.webkit.messageHandlers.contextMenu.postMessage(message);};";
|
||||
syncEval(app, invoke);
|
||||
|
||||
// Runtime
|
||||
Debug("Setting up Wails runtime");
|
||||
syncEval(app, &runtime);
|
||||
@@ -820,7 +824,7 @@ void Show(struct Application *app) {
|
||||
|
||||
// maximiseInternal maximises the main window
|
||||
void maximiseInternal(struct Application *app) {
|
||||
gtk_window_maximize(GTK_WINDOW(app->mainWindow));
|
||||
gtk_window_maximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Maximise places the maximiseInternal method onto the main thread for execution
|
||||
@@ -837,7 +841,7 @@ void Maximise(struct Application *app) {
|
||||
|
||||
// unmaximiseInternal unmaximises the main window
|
||||
void unmaximiseInternal(struct Application *app) {
|
||||
gtk_window_unmaximize(GTK_WINDOW(app->mainWindow));
|
||||
gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Unmaximise places the unmaximiseInternal method onto the main thread for execution
|
||||
@@ -853,21 +857,6 @@ void Unmaximise(struct Application *app) {
|
||||
}
|
||||
|
||||
|
||||
void DarkModeEnabled(struct Application* app, char *callbackID) {}
|
||||
void SetApplicationMenu(struct Application* app, const char *menuJSON) {}
|
||||
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) {}
|
||||
void WebviewIsTransparent(struct Application* app) {}
|
||||
void WindowBackgroundIsTranslucent(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) {}
|
||||
|
||||
|
||||
// minimiseInternal minimises the main window
|
||||
void minimiseInternal(struct Application *app) {
|
||||
gtk_window_iconify(app->mainWindow);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
|
||||
#include "ffenestri.h"
|
||||
#include "ffenestri_linux.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
#ifndef FFENESTRI_LINUX_H
|
||||
#define FFENESTRI_LINUX_H
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,845 +0,0 @@
|
||||
// Some code may be inspired by or directly used from Webview (c) zserge.
|
||||
// License included in README.md
|
||||
|
||||
#include "ffenestri_windows.h"
|
||||
#include "wv2ComHandler_windows.h"
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
#include <Shlwapi.h>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include "windows/WebView2.h"
|
||||
#include <winuser.h>
|
||||
#include "effectstructs_windows.h"
|
||||
#include <Shlobj.h>
|
||||
|
||||
int debug = 0;
|
||||
DWORD mainThread;
|
||||
|
||||
#define WS_EX_NOREDIRECTIONBITMAP 0x00200000L
|
||||
|
||||
// --- Assets
|
||||
extern const unsigned char runtime;
|
||||
extern const unsigned char *defaultDialogIcons[];
|
||||
|
||||
// dispatch will execute the given `func` pointer
|
||||
void dispatch(dispatchFunction func) {
|
||||
PostThreadMessage(mainThread, WM_APP, 0, (LPARAM) new dispatchFunction(func));
|
||||
}
|
||||
|
||||
LPWSTR cstrToLPWSTR(const char *cstr) {
|
||||
int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, NULL , 0 );
|
||||
wchar_t* wstr = new wchar_t[wchars_num+1];
|
||||
MultiByteToWideChar( CP_UTF8 , 0 , cstr , -1, wstr , wchars_num );
|
||||
return wstr;
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/9842450
|
||||
char* LPWSTRToCstr(LPWSTR input) {
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, input, -1, 0, 0, NULL, NULL);
|
||||
char* output = new char[length];
|
||||
WideCharToMultiByte(CP_UTF8, 0, input, -1, output , length, NULL, NULL);
|
||||
return output;
|
||||
}
|
||||
|
||||
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||
|
||||
// Create application
|
||||
struct Application *result = (struct Application*)malloc(sizeof(struct Application));
|
||||
|
||||
result->window = nullptr;
|
||||
result->webview = nullptr;
|
||||
result->webviewController = nullptr;
|
||||
|
||||
result->title = title;
|
||||
result->width = width;
|
||||
result->height = height;
|
||||
result->resizable = resizable;
|
||||
result->devtools = devtools;
|
||||
result->fullscreen = fullscreen;
|
||||
result->startHidden = startHidden;
|
||||
result->logLevel = logLevel;
|
||||
result->hideWindowOnClose = hideWindowOnClose;
|
||||
result->webviewIsTranparent = false;
|
||||
result->windowBackgroundIsTranslucent = false;
|
||||
result->disableWindowIcon = false;
|
||||
|
||||
// Min/Max Width/Height
|
||||
result->minWidth = 0;
|
||||
result->minHeight = 0;
|
||||
result->maxWidth = 0;
|
||||
result->maxHeight = 0;
|
||||
|
||||
// Default colour
|
||||
result->backgroundColour.R = 255;
|
||||
result->backgroundColour.G = 255;
|
||||
result->backgroundColour.B = 255;
|
||||
result->backgroundColour.A = 255;
|
||||
|
||||
// Have a frame by default
|
||||
result->frame = 1;
|
||||
|
||||
// Capture Main Thread
|
||||
mainThread = GetCurrentThreadId();
|
||||
|
||||
// Startup url
|
||||
result->startupURL = nullptr;
|
||||
|
||||
// Used to remember the window location when going fullscreen
|
||||
result->previousPlacement = { sizeof(result->previousPlacement) };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void* GetWindowHandle(struct Application *app) {
|
||||
return (void*)app->window;
|
||||
}
|
||||
|
||||
void SetMinWindowSize(struct Application* app, int minWidth, int minHeight) {
|
||||
app->minWidth = (LONG)minWidth;
|
||||
app->minHeight = (LONG)minHeight;
|
||||
}
|
||||
|
||||
void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) {
|
||||
app->maxWidth = (LONG)maxWidth;
|
||||
app->maxHeight = (LONG)maxHeight;
|
||||
}
|
||||
|
||||
void SetBindings(struct Application *app, const char *bindings) {
|
||||
std::string temp = std::string("window.wailsbindings = \"") + std::string(bindings) + std::string("\";");
|
||||
app->bindings = new char[temp.length()+1];
|
||||
memcpy(app->bindings, temp.c_str(), temp.length()+1);
|
||||
}
|
||||
|
||||
void performShutdown(struct Application *app) {
|
||||
if( app->startupURL != nullptr ) {
|
||||
delete[] app->startupURL;
|
||||
}
|
||||
messageFromWindowCallback("WC");
|
||||
}
|
||||
|
||||
// Credit: https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf
|
||||
void enableTranslucentBackground(struct Application *app) {
|
||||
HMODULE hUser = GetModuleHandleA("user32.dll");
|
||||
if (hUser)
|
||||
{
|
||||
pfnSetWindowCompositionAttribute setWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
|
||||
if (setWindowCompositionAttribute)
|
||||
{
|
||||
ACCENT_POLICY accent = { ACCENT_ENABLE_BLURBEHIND, 0, 0, 0 };
|
||||
WINDOWCOMPOSITIONATTRIBDATA data;
|
||||
data.Attrib = WCA_ACCENT_POLICY;
|
||||
data.pvData = &accent;
|
||||
data.cbData = sizeof(accent);
|
||||
setWindowCompositionAttribute(app->window, &data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
struct Application *app = (struct Application *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
||||
|
||||
switch(msg) {
|
||||
|
||||
case WM_CLOSE: {
|
||||
DestroyWindow( app->window );
|
||||
break;
|
||||
}
|
||||
case WM_DESTROY: {
|
||||
if( app->hideWindowOnClose ) {
|
||||
Hide(app);
|
||||
} else {
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SIZE: {
|
||||
if ( app == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
if( app->webviewController != nullptr) {
|
||||
RECT bounds;
|
||||
GetClientRect(app->window, &bounds);
|
||||
app->webviewController->put_Bounds(bounds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_GETMINMAXINFO: {
|
||||
// Exit early if this is called before the window is created.
|
||||
if ( app == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get pixel density
|
||||
HDC hDC = GetDC(NULL);
|
||||
double DPIScaleX = GetDeviceCaps(hDC, 88)/96.0;
|
||||
double DPIScaleY = GetDeviceCaps(hDC, 90)/96.0;
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
RECT rcClient, rcWind;
|
||||
POINT ptDiff;
|
||||
GetClientRect(hwnd, &rcClient);
|
||||
GetWindowRect(hwnd, &rcWind);
|
||||
|
||||
int widthExtra = (rcWind.right - rcWind.left) - rcClient.right;
|
||||
int heightExtra = (rcWind.bottom - rcWind.top) - rcClient.bottom;
|
||||
|
||||
LPMINMAXINFO mmi = (LPMINMAXINFO) lParam;
|
||||
if (app->minWidth > 0 && app->minHeight > 0) {
|
||||
mmi->ptMinTrackSize.x = app->minWidth * DPIScaleX + widthExtra;
|
||||
mmi->ptMinTrackSize.y = app->minHeight * DPIScaleY + heightExtra;
|
||||
}
|
||||
if (app->maxWidth > 0 && app->maxHeight > 0) {
|
||||
mmi->ptMaxSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
||||
mmi->ptMaxSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
||||
mmi->ptMaxTrackSize.x = app->maxWidth * DPIScaleX + widthExtra;
|
||||
mmi->ptMaxTrackSize.y = app->maxHeight * DPIScaleY + heightExtra;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init(struct Application *app, const char* js) {
|
||||
LPCWSTR wjs = cstrToLPWSTR(js);
|
||||
app->webview->AddScriptToExecuteOnDocumentCreated(wjs, nullptr);
|
||||
delete[] wjs;
|
||||
}
|
||||
|
||||
void execJS(struct Application* app, const char *script) {
|
||||
LPWSTR s = cstrToLPWSTR(script);
|
||||
app->webview->ExecuteScript(s, nullptr);
|
||||
delete[] s;
|
||||
}
|
||||
|
||||
void loadAssets(struct Application* app) {
|
||||
|
||||
// setup window.wailsInvoke
|
||||
std::string initialCode = std::string("window.wailsInvoke=function(m){window.chrome.webview.postMessage(m)};");
|
||||
|
||||
// Load bindings
|
||||
initialCode += std::string(app->bindings);
|
||||
delete[] app->bindings;
|
||||
|
||||
// Load runtime
|
||||
initialCode += std::string((const char*)&runtime);
|
||||
|
||||
int index = 1;
|
||||
while(1) {
|
||||
// Get next asset pointer
|
||||
const unsigned char *asset = assets[index];
|
||||
|
||||
// If we have no more assets, break
|
||||
if (asset == 0x00) {
|
||||
break;
|
||||
}
|
||||
|
||||
initialCode += std::string((const char*)asset);
|
||||
index++;
|
||||
};
|
||||
|
||||
// Disable context menu if not in debug mode
|
||||
if( debug != 1 ) {
|
||||
initialCode += std::string("wails._.DisableDefaultContextMenu();");
|
||||
}
|
||||
|
||||
initialCode += std::string("window.wailsInvoke('completed');");
|
||||
|
||||
// Keep a copy of the code
|
||||
app->initialCode = new char[initialCode.length()+1];
|
||||
memcpy(app->initialCode, initialCode.c_str(), initialCode.length()+1);
|
||||
|
||||
execJS(app, app->initialCode);
|
||||
|
||||
// Show app if we need to
|
||||
if( app->startHidden == false ) {
|
||||
Show(app);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called when all our assets are loaded into the DOM
|
||||
void completed(struct Application* app) {
|
||||
delete[] app->initialCode;
|
||||
app->initialCode = nullptr;
|
||||
|
||||
// Process whether window should show by default
|
||||
int startVisibility = SW_SHOWNORMAL;
|
||||
if ( app->startHidden == 1 ) {
|
||||
startVisibility = SW_HIDE;
|
||||
}
|
||||
|
||||
// Fix for webview2 bug: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1077
|
||||
// Will be fixed in next stable release
|
||||
app->webviewController->put_IsVisible(false);
|
||||
app->webviewController->put_IsVisible(true);
|
||||
|
||||
// Private setTitle as we're on the main thread
|
||||
if( app->frame == 1) {
|
||||
setTitle(app, app->title);
|
||||
}
|
||||
|
||||
ShowWindow(app->window, startVisibility);
|
||||
UpdateWindow(app->window);
|
||||
SetFocus(app->window);
|
||||
|
||||
if( app->startupURL == nullptr ) {
|
||||
messageFromWindowCallback("SS");
|
||||
return;
|
||||
}
|
||||
std::string readyMessage = std::string("SS") + std::string(app->startupURL);
|
||||
messageFromWindowCallback(readyMessage.c_str());
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
bool initWebView2(struct Application *app, int debugEnabled, messageCallback cb) {
|
||||
|
||||
debug = debugEnabled;
|
||||
|
||||
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
||||
|
||||
std::atomic_flag flag = ATOMIC_FLAG_INIT;
|
||||
flag.test_and_set();
|
||||
|
||||
char currentExePath[MAX_PATH];
|
||||
GetModuleFileNameA(NULL, currentExePath, MAX_PATH);
|
||||
char *currentExeName = PathFindFileNameA(currentExePath);
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> wideCharConverter;
|
||||
std::wstring userDataFolder =
|
||||
wideCharConverter.from_bytes(std::getenv("APPDATA"));
|
||||
std::wstring currentExeNameW = wideCharConverter.from_bytes(currentExeName);
|
||||
|
||||
ICoreWebView2Controller *controller;
|
||||
ICoreWebView2* webview;
|
||||
|
||||
HRESULT res = CreateCoreWebView2EnvironmentWithOptions(
|
||||
nullptr, (userDataFolder + L"/" + currentExeNameW).c_str(), nullptr,
|
||||
new wv2ComHandler(app, app->window, cb,
|
||||
[&](ICoreWebView2Controller *webviewController) {
|
||||
controller = webviewController;
|
||||
controller->get_CoreWebView2(&webview);
|
||||
webview->AddRef();
|
||||
ICoreWebView2Settings* settings;
|
||||
webview->get_Settings(&settings);
|
||||
if ( debugEnabled == 0 ) {
|
||||
settings->put_AreDefaultContextMenusEnabled(FALSE);
|
||||
}
|
||||
// Fix for invisible webview
|
||||
if( app->startHidden ) {}
|
||||
flag.clear();
|
||||
}));
|
||||
if (!SUCCEEDED(res))
|
||||
{
|
||||
switch (res)
|
||||
{
|
||||
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
|
||||
{
|
||||
MessageBox(
|
||||
app->window,
|
||||
L"Couldn't find Edge installation. "
|
||||
"Do you have a version installed that's compatible with this "
|
||||
"WebView2 SDK version?",
|
||||
nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case E_ACCESSDENIED:
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case E_FAIL:
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"Edge runtime unable to start", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res != S_OK) {
|
||||
CoUninitialize();
|
||||
return false;
|
||||
}
|
||||
|
||||
MSG msg = {};
|
||||
while (flag.test_and_set() && GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
app->webviewController = controller;
|
||||
app->webview = webview;
|
||||
// Resize WebView to fit the bounds of the parent window
|
||||
RECT bounds;
|
||||
GetClientRect(app->window, &bounds);
|
||||
app->webviewController->put_Bounds(bounds);
|
||||
|
||||
// Let the backend know we have initialised
|
||||
app->webview->AddScriptToExecuteOnDocumentCreated(L"window.chrome.webview.postMessage('initialised');", nullptr);
|
||||
// Load the HTML
|
||||
LPCWSTR html = (LPCWSTR) cstrToLPWSTR((char*)assets[0]);
|
||||
app->webview->Navigate(html);
|
||||
|
||||
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
||||
return true;
|
||||
}
|
||||
|
||||
void initialCallback(std::string message) {
|
||||
printf("MESSAGE=%s\n", message);
|
||||
}
|
||||
|
||||
void Run(struct Application* app, int argc, char **argv) {
|
||||
|
||||
// Register the window class.
|
||||
const wchar_t CLASS_NAME[] = L"Ffenestri";
|
||||
|
||||
WNDCLASSEX wc = { };
|
||||
|
||||
wc.cbSize = sizeof(WNDCLASSEX);
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hInstance = GetModuleHandle(NULL);
|
||||
wc.lpszClassName = CLASS_NAME;
|
||||
|
||||
if( app->disableWindowIcon == false ) {
|
||||
wc.hIcon = LoadIcon(wc.hInstance, MAKEINTRESOURCE(100));
|
||||
wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(100));
|
||||
}
|
||||
|
||||
// Configure translucency
|
||||
DWORD dwExStyle = 0;
|
||||
if ( app->windowBackgroundIsTranslucent) {
|
||||
dwExStyle = WS_EX_NOREDIRECTIONBITMAP;
|
||||
wc.hbrBackground = CreateSolidBrush(RGB(255,255,255));
|
||||
}
|
||||
|
||||
RegisterClassEx(&wc);
|
||||
|
||||
// Process window style
|
||||
DWORD windowStyle = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
|
||||
|
||||
if (app->resizable == 0) {
|
||||
windowStyle &= ~WS_MAXIMIZEBOX;
|
||||
windowStyle &= ~WS_THICKFRAME;
|
||||
}
|
||||
if ( app->frame == 0 ) {
|
||||
windowStyle &= ~WS_OVERLAPPEDWINDOW;
|
||||
windowStyle &= ~WS_CAPTION;
|
||||
windowStyle |= WS_POPUP;
|
||||
}
|
||||
|
||||
// Create the window.
|
||||
app->window = CreateWindowEx(
|
||||
dwExStyle, // Optional window styles.
|
||||
CLASS_NAME, // Window class
|
||||
L"", // Window text
|
||||
windowStyle, // Window style
|
||||
|
||||
// Size and position
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, app->width, app->height,
|
||||
|
||||
NULL, // Parent window
|
||||
NULL, // Menu
|
||||
wc.hInstance, // Instance handle
|
||||
NULL // Additional application data
|
||||
);
|
||||
|
||||
if (app->window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( app->fullscreen ) {
|
||||
fullscreen(app);
|
||||
}
|
||||
|
||||
// Credit: https://stackoverflow.com/a/35482689
|
||||
if( app->disableWindowIcon && app->frame == 1 ) {
|
||||
int extendedStyle = GetWindowLong(app->window, GWL_EXSTYLE);
|
||||
SetWindowLong(app->window, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
|
||||
SetWindowPos(nullptr, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
|
||||
}
|
||||
|
||||
if ( app->windowBackgroundIsTranslucent ) {
|
||||
|
||||
// Enable the translucent background effect
|
||||
enableTranslucentBackground(app);
|
||||
|
||||
// Setup transparency of main window. This allows the blur to show through.
|
||||
SetLayeredWindowAttributes(app->window,RGB(255,255,255),0,LWA_COLORKEY);
|
||||
}
|
||||
|
||||
// Store application pointer in window handle
|
||||
SetWindowLongPtr(app->window, GWLP_USERDATA, (LONG_PTR)app);
|
||||
|
||||
// private center() as we are on main thread
|
||||
center(app);
|
||||
|
||||
// Add webview2
|
||||
initWebView2(app, debug, initialCallback);
|
||||
|
||||
if( app->webviewIsTranparent ) {
|
||||
wchar_t szBuff[64];
|
||||
ICoreWebView2Controller2 *wc2;
|
||||
wc2 = nullptr;
|
||||
app->webviewController->QueryInterface(IID_ICoreWebView2Controller2, (void**)&wc2);
|
||||
|
||||
COREWEBVIEW2_COLOR wvColor;
|
||||
wvColor.R = app->backgroundColour.R;
|
||||
wvColor.G = app->backgroundColour.G;
|
||||
wvColor.B = app->backgroundColour.B;
|
||||
wvColor.A = app->backgroundColour.A == 0 ? 0 : 255;
|
||||
if( app->windowBackgroundIsTranslucent ) {
|
||||
wvColor.A = 0;
|
||||
}
|
||||
HRESULT result = wc2->put_DefaultBackgroundColor(wvColor);
|
||||
if (!SUCCEEDED(result))
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
|
||||
{
|
||||
MessageBox(
|
||||
app->window,
|
||||
L"Couldn't find Edge installation. "
|
||||
"Do you have a version installed that's compatible with this "
|
||||
"WebView2 SDK version?",
|
||||
nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case HRESULT_FROM_WIN32(ERROR_FILE_EXISTS):
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"User data folder cannot be created because a file with the same name already exists.", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case E_ACCESSDENIED:
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"Unable to create user data folder, Access Denied.", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
case E_FAIL:
|
||||
{
|
||||
MessageBox(
|
||||
app->window, L"Edge runtime unable to start", nullptr, MB_OK);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
MessageBox(app->window, L"Failed to create WebView2 environment", nullptr, MB_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Main event loop
|
||||
MSG msg;
|
||||
BOOL res;
|
||||
while ((res = GetMessage(&msg, NULL, 0, 0)) != -1) {
|
||||
if (msg.hwnd) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
continue;
|
||||
}
|
||||
if (msg.message == WM_APP) {
|
||||
dispatchFunction *f = (dispatchFunction*) msg.lParam;
|
||||
(*f)();
|
||||
delete(f);
|
||||
} else if (msg.message == WM_QUIT) {
|
||||
performShutdown(app);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetDebug(struct Application* app, int flag) {
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
void ExecJS(struct Application* app, const char *script) {
|
||||
ON_MAIN_THREAD(
|
||||
execJS(app, script);
|
||||
);
|
||||
}
|
||||
|
||||
void hide(struct Application* app) {
|
||||
ShowWindow(app->window, SW_HIDE);
|
||||
}
|
||||
|
||||
void Hide(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
hide(app);
|
||||
);
|
||||
}
|
||||
|
||||
void show(struct Application* app) {
|
||||
ShowWindow(app->window, SW_SHOW);
|
||||
}
|
||||
|
||||
void Show(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
show(app);
|
||||
);
|
||||
}
|
||||
|
||||
void DisableWindowIcon(struct Application* app) {
|
||||
app->disableWindowIcon = true;
|
||||
}
|
||||
|
||||
void center(struct Application* app) {
|
||||
|
||||
HMONITOR currentMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO info = {0};
|
||||
info.cbSize = sizeof(info);
|
||||
GetMonitorInfoA(currentMonitor, &info);
|
||||
RECT workRect = info.rcWork;
|
||||
LONG screenMiddleW = (workRect.right - workRect.left) / 2;
|
||||
LONG screenMiddleH = (workRect.bottom - workRect.top) / 2;
|
||||
RECT winRect;
|
||||
if (app->frame == 1) {
|
||||
GetWindowRect(app->window, &winRect);
|
||||
} else {
|
||||
GetClientRect(app->window, &winRect);
|
||||
}
|
||||
LONG winWidth = winRect.right - winRect.left;
|
||||
LONG winHeight = winRect.bottom - winRect.top;
|
||||
|
||||
LONG windowX = screenMiddleW - (winWidth / 2);
|
||||
LONG windowY = screenMiddleH - (winHeight / 2);
|
||||
|
||||
SetWindowPos(app->window, HWND_TOP, windowX, windowY, winWidth, winHeight, SWP_NOSIZE);
|
||||
}
|
||||
|
||||
void Center(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
center(app);
|
||||
);
|
||||
}
|
||||
|
||||
UINT getWindowPlacement(struct Application* app) {
|
||||
WINDOWPLACEMENT lpwndpl;
|
||||
lpwndpl.length = sizeof(WINDOWPLACEMENT);
|
||||
BOOL result = GetWindowPlacement(app->window, &lpwndpl);
|
||||
if( result == 0 ) {
|
||||
// TODO: Work out what this call failing means
|
||||
return -1;
|
||||
}
|
||||
return lpwndpl.showCmd;
|
||||
}
|
||||
|
||||
int isMaximised(struct Application* app) {
|
||||
return getWindowPlacement(app) == SW_SHOWMAXIMIZED;
|
||||
}
|
||||
|
||||
void maximise(struct Application* app) {
|
||||
ShowWindow(app->window, SW_MAXIMIZE);
|
||||
}
|
||||
|
||||
void Maximise(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
maximise(app);
|
||||
);
|
||||
}
|
||||
|
||||
void unmaximise(struct Application* app) {
|
||||
ShowWindow(app->window, SW_RESTORE);
|
||||
}
|
||||
|
||||
void Unmaximise(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
unmaximise(app);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void ToggleMaximise(struct Application* app) {
|
||||
if(isMaximised(app)) {
|
||||
return Unmaximise(app);
|
||||
}
|
||||
return Maximise(app);
|
||||
}
|
||||
|
||||
int isMinimised(struct Application* app) {
|
||||
return getWindowPlacement(app) == SW_SHOWMINIMIZED;
|
||||
}
|
||||
|
||||
void minimise(struct Application* app) {
|
||||
ShowWindow(app->window, SW_MINIMIZE);
|
||||
}
|
||||
|
||||
void Minimise(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
minimise(app);
|
||||
);
|
||||
}
|
||||
|
||||
void unminimise(struct Application* app) {
|
||||
ShowWindow(app->window, SW_RESTORE);
|
||||
}
|
||||
|
||||
void Unminimise(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
unminimise(app);
|
||||
);
|
||||
}
|
||||
|
||||
void ToggleMinimise(struct Application* app) {
|
||||
if(isMinimised(app)) {
|
||||
return Unminimise(app);
|
||||
}
|
||||
return Minimise(app);
|
||||
}
|
||||
|
||||
void SetColour(struct Application* app, int red, int green, int blue, int alpha) {
|
||||
app->backgroundColour.R = red;
|
||||
app->backgroundColour.G = green;
|
||||
app->backgroundColour.B = blue;
|
||||
app->backgroundColour.A = alpha;
|
||||
}
|
||||
|
||||
void SetSize(struct Application* app, int width, int height) {
|
||||
if( app->maxWidth > 0 && width > app->maxWidth ) {
|
||||
width = app->maxWidth;
|
||||
}
|
||||
if ( app->maxHeight > 0 && height > app->maxHeight ) {
|
||||
height = app->maxHeight;
|
||||
}
|
||||
SetWindowPos(app->window, nullptr, 0, 0, width, height, SWP_NOMOVE);
|
||||
}
|
||||
|
||||
void setPosition(struct Application* app, int x, int y) {
|
||||
HMONITOR currentMonitor = MonitorFromWindow(app->window, MONITOR_DEFAULTTONEAREST);
|
||||
MONITORINFO info = {0};
|
||||
info.cbSize = sizeof(info);
|
||||
GetMonitorInfoA(currentMonitor, &info);
|
||||
RECT workRect = info.rcWork;
|
||||
LONG newX = workRect.left + x;
|
||||
LONG newY = workRect.top + y;
|
||||
|
||||
SetWindowPos(app->window, HWND_TOP, newX, newY, 0, 0, SWP_NOSIZE);
|
||||
}
|
||||
|
||||
void SetPosition(struct Application* app, int x, int y) {
|
||||
ON_MAIN_THREAD(
|
||||
setPosition(app, x, y);
|
||||
);
|
||||
}
|
||||
|
||||
void Quit(struct Application* app) {
|
||||
// Override the hide window on close flag
|
||||
app->hideWindowOnClose = 0;
|
||||
ON_MAIN_THREAD(
|
||||
DestroyWindow(app->window);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Credit: https://stackoverflow.com/a/6693107
|
||||
void setTitle(struct Application* app, const char *title) {
|
||||
LPCTSTR text = cstrToLPWSTR(title);
|
||||
SetWindowText(app->window, text);
|
||||
delete[] text;
|
||||
}
|
||||
|
||||
void SetTitle(struct Application* app, const char *title) {
|
||||
ON_MAIN_THREAD(
|
||||
setTitle(app, title);
|
||||
);
|
||||
}
|
||||
|
||||
void fullscreen(struct Application* app) {
|
||||
|
||||
// Ensure we aren't in fullscreen
|
||||
if (app->isFullscreen) return;
|
||||
|
||||
app->isFullscreen = true;
|
||||
app->previousWindowStyle = GetWindowLong(app->window, GWL_STYLE);
|
||||
MONITORINFO mi = { sizeof(mi) };
|
||||
if (GetWindowPlacement(app->window, &(app->previousPlacement)) && GetMonitorInfo(MonitorFromWindow(app->window, MONITOR_DEFAULTTOPRIMARY), &mi)) {
|
||||
SetWindowLong(app->window, GWL_STYLE, app->previousWindowStyle & ~WS_OVERLAPPEDWINDOW);
|
||||
SetWindowPos(app->window, HWND_TOP,
|
||||
mi.rcMonitor.left,
|
||||
mi.rcMonitor.top,
|
||||
mi.rcMonitor.right - mi.rcMonitor.left,
|
||||
mi.rcMonitor.bottom - mi.rcMonitor.top,
|
||||
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void Fullscreen(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
fullscreen(app);
|
||||
show(app);
|
||||
);
|
||||
}
|
||||
|
||||
void unfullscreen(struct Application* app) {
|
||||
if (app->isFullscreen) {
|
||||
SetWindowLong(app->window, GWL_STYLE, app->previousWindowStyle);
|
||||
SetWindowPlacement(app->window, &(app->previousPlacement));
|
||||
SetWindowPos(app->window, NULL, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
|
||||
SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
|
||||
app->isFullscreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UnFullscreen(struct Application* app) {
|
||||
ON_MAIN_THREAD(
|
||||
unfullscreen(app);
|
||||
);
|
||||
}
|
||||
|
||||
void DisableFrame(struct Application* app) {
|
||||
app->frame = 0;
|
||||
}
|
||||
|
||||
// WebviewIsTransparent will make the webview transparent
|
||||
// revealing the window underneath
|
||||
void WebviewIsTransparent(struct Application *app) {
|
||||
app->webviewIsTranparent = true;
|
||||
}
|
||||
|
||||
void WindowBackgroundIsTranslucent(struct Application *app) {
|
||||
app->windowBackgroundIsTranslucent = true;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
import "C"
|
||||
|
||||
/*
|
||||
|
||||
#cgo windows CXXFLAGS: -std=c++11
|
||||
#cgo windows,amd64 LDFLAGS: -lgdi32 -lole32 -lShlwapi -luser32 -loleaut32 -ldwmapi
|
||||
|
||||
#include "ffenestri.h"
|
||||
|
||||
extern void DisableWindowIcon(struct Application* app);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() error {
|
||||
|
||||
config := a.config.Windows
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if config.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
}
|
||||
|
||||
if config.WindowBackgroundIsTranslucent {
|
||||
C.WindowBackgroundIsTranslucent(a.app)
|
||||
}
|
||||
|
||||
if config.DisableWindowIcon {
|
||||
C.DisableWindowIcon(a.app)
|
||||
}
|
||||
|
||||
//// Process menu
|
||||
////applicationMenu := options.GetApplicationMenu(a.config)
|
||||
//applicationMenu := a.menuManager.GetApplicationMenuJSON()
|
||||
//if applicationMenu != "" {
|
||||
// C.SetApplicationMenu(a.app, a.string2CString(applicationMenu))
|
||||
//}
|
||||
//
|
||||
//// Process tray
|
||||
//trays, err := a.menuManager.GetTrayMenus()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//if trays != nil {
|
||||
// for _, tray := range trays {
|
||||
// C.AddTrayMenu(a.app, a.string2CString(tray))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Process context menus
|
||||
//contextMenus, err := a.menuManager.GetContextMenus()
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//if contextMenus != nil {
|
||||
// for _, contextMenu := range contextMenus {
|
||||
// C.AddContextMenu(a.app, a.string2CString(contextMenu))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// Process URL Handlers
|
||||
//if a.config.Mac.URLHandlers != nil {
|
||||
// C.HasURLHandlers(a.app)
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
|
||||
#ifndef _FFENESTRI_WINDOWS_H
|
||||
#define _FFENESTRI_WINDOWS_H
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define UNICODE 1
|
||||
|
||||
#include "ffenestri.h"
|
||||
#include <windows.h>
|
||||
#include <wingdi.h>
|
||||
#include <functional>
|
||||
#include <codecvt>
|
||||
#include "windows/WebView2.h"
|
||||
|
||||
#include "assets.h"
|
||||
|
||||
// TODO:
|
||||
//#include "userdialogicons.h"
|
||||
|
||||
|
||||
struct Application{
|
||||
// Window specific
|
||||
HWND window;
|
||||
ICoreWebView2 *webview;
|
||||
ICoreWebView2Controller* webviewController;
|
||||
|
||||
// Application
|
||||
const char *title;
|
||||
int width;
|
||||
int height;
|
||||
int resizable;
|
||||
int devtools;
|
||||
int fullscreen;
|
||||
int startHidden;
|
||||
int logLevel;
|
||||
int hideWindowOnClose;
|
||||
int minSizeSet;
|
||||
LONG minWidth;
|
||||
LONG minHeight;
|
||||
int maxSizeSet;
|
||||
LONG maxWidth;
|
||||
LONG maxHeight;
|
||||
int frame;
|
||||
char *startupURL;
|
||||
bool webviewIsTranparent;
|
||||
bool windowBackgroundIsTranslucent;
|
||||
COREWEBVIEW2_COLOR backgroundColour;
|
||||
bool disableWindowIcon;
|
||||
|
||||
// Used by fullscreen/unfullscreen
|
||||
bool isFullscreen;
|
||||
WINDOWPLACEMENT previousPlacement;
|
||||
DWORD previousWindowStyle;
|
||||
|
||||
// placeholders
|
||||
char* bindings;
|
||||
char* initialCode;
|
||||
};
|
||||
|
||||
#define ON_MAIN_THREAD(code) dispatch( [=]{ code; } )
|
||||
|
||||
typedef std::function<void()> dispatchFunction;
|
||||
typedef std::function<void(const std::string)> messageCallback;
|
||||
typedef std::function<void(ICoreWebView2Controller *)> comHandlerCallback;
|
||||
|
||||
void center(struct Application*);
|
||||
void setTitle(struct Application* app, const char *title);
|
||||
void fullscreen(struct Application* app);
|
||||
void unfullscreen(struct Application* app);
|
||||
char* LPWSTRToCstr(LPWSTR input);
|
||||
|
||||
// called when the DOM is ready
|
||||
void loadAssets(struct Application* app);
|
||||
|
||||
// called when the application assets have been loaded into the DOM
|
||||
void completed(struct Application* app);
|
||||
|
||||
// Callback
|
||||
extern "C" {
|
||||
void DisableWindowIcon(struct Application* app);
|
||||
void messageFromWindowCallback(const char *);
|
||||
void* GetWindowHandle(struct Application*);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,3 @@
|
||||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
@@ -85,13 +85,14 @@ void DeleteMenu(Menu *menu) {
|
||||
menu->processedMenu = NULL;
|
||||
}
|
||||
|
||||
// Release the callback data memory + vector
|
||||
int i; MenuItemCallbackData* callbackData;
|
||||
vec_foreach(&menu->callbackDataCache, callbackData, i) {
|
||||
free(callbackData);
|
||||
}
|
||||
// Release the vector memory
|
||||
vec_deinit(&menu->callbackDataCache);
|
||||
|
||||
// Free nsmenu if we have it
|
||||
if ( menu->menu != NULL ) {
|
||||
msg(menu->menu, s("release"));
|
||||
}
|
||||
|
||||
free(menu);
|
||||
}
|
||||
|
||||
@@ -119,17 +120,17 @@ const char* createMenuClickedMessage(const char *menuItemID, const char *data, e
|
||||
|
||||
// Callback for text menu items
|
||||
void menuItemCallback(id self, SEL cmd, id sender) {
|
||||
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg_reg(msg_reg(sender, s("representedObject")), s("pointerValue"));
|
||||
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg(msg(sender, s("representedObject")), s("pointerValue"));
|
||||
const char *message;
|
||||
|
||||
// Update checkbox / radio item
|
||||
if( callbackData->menuItemType == Checkbox) {
|
||||
// Toggle state
|
||||
bool state = msg_reg(callbackData->menuItem, s("state"));
|
||||
msg_int(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
||||
bool state = msg(callbackData->menuItem, s("state"));
|
||||
msg(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
||||
} else if( callbackData->menuItemType == Radio ) {
|
||||
// Check the menu items' current state
|
||||
bool selected = (bool)msg_reg(callbackData->menuItem, s("state"));
|
||||
bool selected = msg(callbackData->menuItem, s("state"));
|
||||
|
||||
// If it's already selected, exit early
|
||||
if (selected) return;
|
||||
@@ -141,13 +142,13 @@ void menuItemCallback(id self, SEL cmd, id sender) {
|
||||
id thisMember = members[0];
|
||||
int count = 0;
|
||||
while(thisMember != NULL) {
|
||||
msg_int(thisMember, s("setState:"), NSControlStateValueOff);
|
||||
msg(thisMember, s("setState:"), NSControlStateValueOff);
|
||||
count = count + 1;
|
||||
thisMember = members[count];
|
||||
}
|
||||
|
||||
// check the selected menu item
|
||||
msg_int(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
||||
msg(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
||||
}
|
||||
|
||||
const char *menuID = callbackData->menuID;
|
||||
@@ -179,154 +180,151 @@ id processAcceleratorKey(const char *key) {
|
||||
return str("");
|
||||
}
|
||||
|
||||
if( STREQ(key, "backspace") ) {
|
||||
if( STREQ(key, "Backspace") ) {
|
||||
return strunicode(0x0008);
|
||||
}
|
||||
if( STREQ(key, "tab") ) {
|
||||
if( STREQ(key, "Tab") ) {
|
||||
return strunicode(0x0009);
|
||||
}
|
||||
if( STREQ(key, "return") ) {
|
||||
if( STREQ(key, "Return") ) {
|
||||
return strunicode(0x000d);
|
||||
}
|
||||
if( STREQ(key, "enter") ) {
|
||||
return strunicode(0x000d);
|
||||
}
|
||||
if( STREQ(key, "escape") ) {
|
||||
if( STREQ(key, "Escape") ) {
|
||||
return strunicode(0x001b);
|
||||
}
|
||||
if( STREQ(key, "left") ) {
|
||||
if( STREQ(key, "Left") ) {
|
||||
return strunicode(0x001c);
|
||||
}
|
||||
if( STREQ(key, "right") ) {
|
||||
if( STREQ(key, "Right") ) {
|
||||
return strunicode(0x001d);
|
||||
}
|
||||
if( STREQ(key, "up") ) {
|
||||
if( STREQ(key, "Up") ) {
|
||||
return strunicode(0x001e);
|
||||
}
|
||||
if( STREQ(key, "down") ) {
|
||||
if( STREQ(key, "Down") ) {
|
||||
return strunicode(0x001f);
|
||||
}
|
||||
if( STREQ(key, "space") ) {
|
||||
if( STREQ(key, "Space") ) {
|
||||
return strunicode(0x0020);
|
||||
}
|
||||
if( STREQ(key, "delete") ) {
|
||||
if( STREQ(key, "Delete") ) {
|
||||
return strunicode(0x007f);
|
||||
}
|
||||
if( STREQ(key, "home") ) {
|
||||
if( STREQ(key, "Home") ) {
|
||||
return strunicode(0x2196);
|
||||
}
|
||||
if( STREQ(key, "end") ) {
|
||||
if( STREQ(key, "End") ) {
|
||||
return strunicode(0x2198);
|
||||
}
|
||||
if( STREQ(key, "page up") ) {
|
||||
if( STREQ(key, "Page Up") ) {
|
||||
return strunicode(0x21de);
|
||||
}
|
||||
if( STREQ(key, "page down") ) {
|
||||
if( STREQ(key, "Page Down") ) {
|
||||
return strunicode(0x21df);
|
||||
}
|
||||
if( STREQ(key, "f1") ) {
|
||||
if( STREQ(key, "F1") ) {
|
||||
return strunicode(0xf704);
|
||||
}
|
||||
if( STREQ(key, "f2") ) {
|
||||
if( STREQ(key, "F2") ) {
|
||||
return strunicode(0xf705);
|
||||
}
|
||||
if( STREQ(key, "f3") ) {
|
||||
if( STREQ(key, "F3") ) {
|
||||
return strunicode(0xf706);
|
||||
}
|
||||
if( STREQ(key, "f4") ) {
|
||||
if( STREQ(key, "F4") ) {
|
||||
return strunicode(0xf707);
|
||||
}
|
||||
if( STREQ(key, "f5") ) {
|
||||
if( STREQ(key, "F5") ) {
|
||||
return strunicode(0xf708);
|
||||
}
|
||||
if( STREQ(key, "f6") ) {
|
||||
if( STREQ(key, "F6") ) {
|
||||
return strunicode(0xf709);
|
||||
}
|
||||
if( STREQ(key, "f7") ) {
|
||||
if( STREQ(key, "F7") ) {
|
||||
return strunicode(0xf70a);
|
||||
}
|
||||
if( STREQ(key, "f8") ) {
|
||||
if( STREQ(key, "F8") ) {
|
||||
return strunicode(0xf70b);
|
||||
}
|
||||
if( STREQ(key, "f9") ) {
|
||||
if( STREQ(key, "F9") ) {
|
||||
return strunicode(0xf70c);
|
||||
}
|
||||
if( STREQ(key, "f10") ) {
|
||||
if( STREQ(key, "F10") ) {
|
||||
return strunicode(0xf70d);
|
||||
}
|
||||
if( STREQ(key, "f11") ) {
|
||||
if( STREQ(key, "F11") ) {
|
||||
return strunicode(0xf70e);
|
||||
}
|
||||
if( STREQ(key, "f12") ) {
|
||||
if( STREQ(key, "F12") ) {
|
||||
return strunicode(0xf70f);
|
||||
}
|
||||
if( STREQ(key, "f13") ) {
|
||||
if( STREQ(key, "F13") ) {
|
||||
return strunicode(0xf710);
|
||||
}
|
||||
if( STREQ(key, "f14") ) {
|
||||
if( STREQ(key, "F14") ) {
|
||||
return strunicode(0xf711);
|
||||
}
|
||||
if( STREQ(key, "f15") ) {
|
||||
if( STREQ(key, "F15") ) {
|
||||
return strunicode(0xf712);
|
||||
}
|
||||
if( STREQ(key, "f16") ) {
|
||||
if( STREQ(key, "F16") ) {
|
||||
return strunicode(0xf713);
|
||||
}
|
||||
if( STREQ(key, "f17") ) {
|
||||
if( STREQ(key, "F17") ) {
|
||||
return strunicode(0xf714);
|
||||
}
|
||||
if( STREQ(key, "f18") ) {
|
||||
if( STREQ(key, "F18") ) {
|
||||
return strunicode(0xf715);
|
||||
}
|
||||
if( STREQ(key, "f19") ) {
|
||||
if( STREQ(key, "F19") ) {
|
||||
return strunicode(0xf716);
|
||||
}
|
||||
if( STREQ(key, "f20") ) {
|
||||
if( STREQ(key, "F20") ) {
|
||||
return strunicode(0xf717);
|
||||
}
|
||||
if( STREQ(key, "f21") ) {
|
||||
if( STREQ(key, "F21") ) {
|
||||
return strunicode(0xf718);
|
||||
}
|
||||
if( STREQ(key, "f22") ) {
|
||||
if( STREQ(key, "F22") ) {
|
||||
return strunicode(0xf719);
|
||||
}
|
||||
if( STREQ(key, "f23") ) {
|
||||
if( STREQ(key, "F23") ) {
|
||||
return strunicode(0xf71a);
|
||||
}
|
||||
if( STREQ(key, "f24") ) {
|
||||
if( STREQ(key, "F24") ) {
|
||||
return strunicode(0xf71b);
|
||||
}
|
||||
if( STREQ(key, "f25") ) {
|
||||
if( STREQ(key, "F25") ) {
|
||||
return strunicode(0xf71c);
|
||||
}
|
||||
if( STREQ(key, "f26") ) {
|
||||
if( STREQ(key, "F26") ) {
|
||||
return strunicode(0xf71d);
|
||||
}
|
||||
if( STREQ(key, "f27") ) {
|
||||
if( STREQ(key, "F27") ) {
|
||||
return strunicode(0xf71e);
|
||||
}
|
||||
if( STREQ(key, "f28") ) {
|
||||
if( STREQ(key, "F28") ) {
|
||||
return strunicode(0xf71f);
|
||||
}
|
||||
if( STREQ(key, "f29") ) {
|
||||
if( STREQ(key, "F29") ) {
|
||||
return strunicode(0xf720);
|
||||
}
|
||||
if( STREQ(key, "f30") ) {
|
||||
if( STREQ(key, "F30") ) {
|
||||
return strunicode(0xf721);
|
||||
}
|
||||
if( STREQ(key, "f31") ) {
|
||||
if( STREQ(key, "F31") ) {
|
||||
return strunicode(0xf722);
|
||||
}
|
||||
if( STREQ(key, "f32") ) {
|
||||
if( STREQ(key, "F32") ) {
|
||||
return strunicode(0xf723);
|
||||
}
|
||||
if( STREQ(key, "f33") ) {
|
||||
if( STREQ(key, "F33") ) {
|
||||
return strunicode(0xf724);
|
||||
}
|
||||
if( STREQ(key, "f34") ) {
|
||||
if( STREQ(key, "F34") ) {
|
||||
return strunicode(0xf725);
|
||||
}
|
||||
if( STREQ(key, "f35") ) {
|
||||
if( STREQ(key, "F35") ) {
|
||||
return strunicode(0xf726);
|
||||
}
|
||||
// if( STREQ(key, "Insert") ) {
|
||||
@@ -338,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
||||
// if( STREQ(key, "ScrollLock") ) {
|
||||
// return strunicode(0xf72f);
|
||||
// }
|
||||
if( STREQ(key, "numLock") ) {
|
||||
if( STREQ(key, "NumLock") ) {
|
||||
return strunicode(0xf739);
|
||||
}
|
||||
|
||||
@@ -347,61 +345,61 @@ id processAcceleratorKey(const char *key) {
|
||||
|
||||
|
||||
void addSeparator(id menu) {
|
||||
id item = msg_reg(c("NSMenuItem"), s("separatorItem"));
|
||||
msg_id(menu, s("addItem:"), item);
|
||||
id item = msg(c("NSMenuItem"), s("separatorItem"));
|
||||
msg(menu, s("addItem:"), item);
|
||||
}
|
||||
|
||||
id createMenuItemNoAutorelease( id title, const char *action, const char *key) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
return item;
|
||||
}
|
||||
|
||||
id createMenuItem(id title, const char *action, const char *key) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg(item, s("autorelease"));
|
||||
return item;
|
||||
}
|
||||
|
||||
id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled) {
|
||||
id item = createMenuItem(str(title), action, key);
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_id(menu, s("addItem:"), item);
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(menu, s("addItem:"), item);
|
||||
return item;
|
||||
}
|
||||
|
||||
id createMenu(id title) {
|
||||
id menu = ALLOC("NSMenu");
|
||||
msg_id(menu, s("initWithTitle:"), title);
|
||||
msg_bool(menu, s("setAutoenablesItems:"), NO);
|
||||
msg_reg(menu, s("autorelease"));
|
||||
msg(menu, s("initWithTitle:"), title);
|
||||
msg(menu, s("setAutoenablesItems:"), NO);
|
||||
// msg(menu, s("autorelease"));
|
||||
return menu;
|
||||
}
|
||||
|
||||
void createDefaultAppMenu(id parentMenu) {
|
||||
// App Menu
|
||||
id appName = msg_reg(msg_reg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
||||
id appName = msg(msg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
||||
id appMenuItem = createMenuItemNoAutorelease(appName, NULL, "");
|
||||
id appMenu = createMenu(appName);
|
||||
|
||||
msg_id(appMenuItem, s("setSubmenu:"), appMenu);
|
||||
msg_id(parentMenu, s("addItem:"), appMenuItem);
|
||||
msg(appMenuItem, s("setSubmenu:"), appMenu);
|
||||
msg(parentMenu, s("addItem:"), appMenuItem);
|
||||
|
||||
id title = msg_id(str("Hide "), s("stringByAppendingString:"), appName);
|
||||
id title = msg(str("Hide "), s("stringByAppendingString:"), appName);
|
||||
id item = createMenuItem(title, "hide:", "h");
|
||||
msg_id(appMenu, s("addItem:"), item);
|
||||
msg(appMenu, s("addItem:"), item);
|
||||
|
||||
id hideOthers = addMenuItem(appMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
|
||||
addMenuItem(appMenu, "Show All", "unhideAllApplications:", "", FALSE);
|
||||
|
||||
addSeparator(appMenu);
|
||||
|
||||
title = msg_id(str("Quit "), s("stringByAppendingString:"), appName);
|
||||
title = msg(str("Quit "), s("stringByAppendingString:"), appName);
|
||||
item = createMenuItem(title, "terminate:", "q");
|
||||
msg_id(appMenu, s("addItem:"), item);
|
||||
msg(appMenu, s("addItem:"), item);
|
||||
}
|
||||
|
||||
void createDefaultEditMenu(id parentMenu) {
|
||||
@@ -409,8 +407,8 @@ void createDefaultEditMenu(id parentMenu) {
|
||||
id editMenuItem = createMenuItemNoAutorelease(str("Edit"), NULL, "");
|
||||
id editMenu = createMenu(str("Edit"));
|
||||
|
||||
msg_id(editMenuItem, s("setSubmenu:"), editMenu);
|
||||
msg_id(parentMenu, s("addItem:"), editMenuItem);
|
||||
msg(editMenuItem, s("setSubmenu:"), editMenu);
|
||||
msg(parentMenu, s("addItem:"), editMenuItem);
|
||||
|
||||
addMenuItem(editMenu, "Undo", "undo:", "z", FALSE);
|
||||
addMenuItem(editMenu, "Redo", "redo:", "y", FALSE);
|
||||
@@ -438,7 +436,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
}
|
||||
if ( STREQ(roleName, "hideothers")) {
|
||||
id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
return;
|
||||
}
|
||||
if ( STREQ(roleName, "unhide")) {
|
||||
@@ -475,7 +473,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
}
|
||||
if( STREQ(roleName, "pasteandmatchstyle")) {
|
||||
id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE);
|
||||
msg_int(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
||||
msg(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
||||
}
|
||||
if ( STREQ(roleName, "selectall")) {
|
||||
addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE);
|
||||
@@ -510,21 +508,20 @@ unsigned long parseModifiers(const char **modifiers) {
|
||||
const char *thisModifier = modifiers[0];
|
||||
int count = 0;
|
||||
while( thisModifier != NULL ) {
|
||||
|
||||
// Determine flags
|
||||
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "optionoralt") ) {
|
||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
||||
result |= NSEventModifierFlagOption;
|
||||
}
|
||||
if( STREQ(thisModifier, "shift") ) {
|
||||
if( STREQ(thisModifier, "Shift") ) {
|
||||
result |= NSEventModifierFlagShift;
|
||||
}
|
||||
if( STREQ(thisModifier, "super") ) {
|
||||
if( STREQ(thisModifier, "Super") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "ctrl") ) {
|
||||
if( STREQ(thisModifier, "Control") ) {
|
||||
result |= NSEventModifierFlagControl;
|
||||
}
|
||||
count++;
|
||||
@@ -542,18 +539,18 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
||||
// Create a MenuItemCallbackData
|
||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Radio);
|
||||
|
||||
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
|
||||
id key = processAcceleratorKey(acceleratorkey);
|
||||
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
||||
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
|
||||
msg_id(parentmenu, s("addItem:"), item);
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
|
||||
}
|
||||
@@ -561,7 +558,6 @@ 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 item = ALLOC("NSMenuItem");
|
||||
msg_reg(item, s("autorelease"));
|
||||
|
||||
// Store the item in the menu item map
|
||||
hashmap_put(&menu->menuItemMap, (char*)menuid, strlen(menuid), item);
|
||||
@@ -569,135 +565,73 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
||||
// Create a MenuItemCallbackData
|
||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Checkbox);
|
||||
|
||||
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg_id(parentmenu, s("addItem:"), item);
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
}
|
||||
|
||||
// getColour returns the colour from a styledLabel based on the key
|
||||
const char* getColour(JsonNode *styledLabelEntry, const char* key) {
|
||||
JsonNode* colEntry = getJSONObject(styledLabelEntry, key);
|
||||
if( colEntry == NULL ) {
|
||||
return NULL;
|
||||
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 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));
|
||||
}
|
||||
return getJSONString(colEntry, "hex");
|
||||
}
|
||||
|
||||
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Create result
|
||||
id attributedString = ALLOC_INIT("NSMutableAttributedString");
|
||||
msg_reg(attributedString, s("autorelease"));
|
||||
|
||||
// Create new Dictionary
|
||||
// Process Menu Item attributes
|
||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||
msg_reg(dictionary, s("autorelease"));
|
||||
|
||||
// Use default font
|
||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||
|
||||
// Check user supplied font
|
||||
if( STR_HAS_CHARS(fontName) ) {
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
if( userFont != NULL ) {
|
||||
font = userFont;
|
||||
}
|
||||
}
|
||||
|
||||
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||
id NSBackgroundColorAttributeName = lookupStringConstant(str("NSBackgroundColorAttributeName"));
|
||||
|
||||
// Loop over styled text creating NSAttributedText and appending to result
|
||||
JsonNode *styledLabelEntry;
|
||||
json_foreach(styledLabelEntry, styledLabel) {
|
||||
|
||||
// Clear dictionary
|
||||
msg_reg(dictionary, s("removeAllObjects"));
|
||||
|
||||
// Add font to dictionary
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||
|
||||
// Get Text
|
||||
const char* thisLabel = mustJSONString(styledLabelEntry, "Label");
|
||||
|
||||
// Get foreground colour
|
||||
const char *hexColour = getColour(styledLabelEntry, "FgCol");
|
||||
if( hexColour != NULL) {
|
||||
unsigned short r, g, b, a;
|
||||
|
||||
// white by default
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
// Get background colour
|
||||
hexColour = getColour(styledLabelEntry, "BgCol");
|
||||
if( hexColour != NULL) {
|
||||
unsigned short r, g, b, a;
|
||||
|
||||
// white by default
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
// Create AttributedText
|
||||
id thisString = ALLOC("NSMutableAttributedString");
|
||||
msg_reg(thisString, s("autorelease"));
|
||||
msg_id_id(thisString, s("initWithString:attributes:"), str(thisLabel), dictionary);
|
||||
|
||||
// Append text to result
|
||||
msg_id(attributedString, s("appendAttributedString:"), thisString);
|
||||
}
|
||||
|
||||
return attributedString;
|
||||
|
||||
}
|
||||
|
||||
|
||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||
|
||||
// Create new Dictionary
|
||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||
// Process font
|
||||
id font;
|
||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||
|
||||
// Use default font
|
||||
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||
|
||||
// Check user supplied font
|
||||
if( STR_HAS_CHARS(fontName) ) {
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
if( userFont != NULL ) {
|
||||
font = userFont;
|
||||
// Check if valid
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id fontsOnSystem = msg(msg(c("NSFontManager"), s("sharedFontManager")), s("availableFonts"));
|
||||
bool valid = msg(fontsOnSystem, s("containsObject:"), fontNameAsNSString);
|
||||
if( valid ) {
|
||||
font = msg(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
} else {
|
||||
bool supportsMonospacedDigitSystemFont = (bool) msg(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
||||
if( supportsMonospacedDigitSystemFont ) {
|
||||
font = msg(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, NSFontWeightRegular);
|
||||
} else {
|
||||
font = msg(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
||||
}
|
||||
}
|
||||
|
||||
// Add font to dictionary
|
||||
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||
msg(dictionary, s("setObject:forKey:"), font, lookupStringConstant(str("NSFontAttributeName")));
|
||||
|
||||
// Add offset to dictionary
|
||||
id offset = msg(c("NSNumber"), s("numberWithFloat:"), 0.0);
|
||||
msg(dictionary, s("setObject:forKey:"), offset, lookupStringConstant(str("NSBaselineOffsetAttributeName")));
|
||||
|
||||
// RGBA
|
||||
if( RGBA != NULL && strlen(RGBA) > 0) {
|
||||
@@ -707,75 +641,32 @@ id createAttributedString(const char* title, const char* fontName, int fontSize,
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
id colour = msg(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(float)r / 255.0,
|
||||
(float)g / 255.0,
|
||||
(float)b / 255.0,
|
||||
(float)a / 255.0);
|
||||
msg(dictionary, s("setObject:forKey:"), colour, lookupStringConstant(str("NSForegroundColorAttributeName")));
|
||||
msg(colour, s("release"));
|
||||
}
|
||||
}
|
||||
|
||||
id attributedString = ALLOC("NSMutableAttributedString");
|
||||
msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||
msg_reg(attributedString, s("autorelease"));
|
||||
msg_reg(dictionary, s("autorelease"));
|
||||
return attributedString;
|
||||
}
|
||||
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||
msg(dictionary, s("release"));
|
||||
|
||||
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, JsonNode* styledLabel) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg(item, s("setAttributedTitle:"), attributedString);
|
||||
msg(attributedString, s("autorelease"));
|
||||
|
||||
// Create a MenuItemCallbackData
|
||||
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 nsimage = createImageFromBase64Data(image, templateImage);
|
||||
msg_id(item, s("setImage:"), nsimage);
|
||||
}
|
||||
|
||||
id attributedString = NULL;
|
||||
if( styledLabel != NULL) {
|
||||
attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize);
|
||||
} else {
|
||||
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(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
|
||||
// Process modifiers
|
||||
if( modifiers != NULL && !alternate) {
|
||||
if( modifiers != NULL ) {
|
||||
unsigned long modifierFlags = parseModifiers(modifiers);
|
||||
((id(*)(id, SEL, unsigned long))objc_msgSend)(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||
msg(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||
}
|
||||
|
||||
// alternate
|
||||
if( alternate ) {
|
||||
msg_bool(item, s("setAlternate:"), true);
|
||||
msg_int(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
||||
}
|
||||
msg_id(parentMenu, s("addItem:"), item);
|
||||
msg(parentMenu, s("addItem:"), item);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -796,6 +687,38 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a submenu
|
||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||
if( submenu != NULL ) {
|
||||
// Get the label
|
||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||
const char *name = "";
|
||||
if ( menuNameNode != NULL) {
|
||||
name = menuNameNode->string_;
|
||||
}
|
||||
|
||||
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
|
||||
id thisMenu = createMenu(str(name));
|
||||
|
||||
msg(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||
msg(parentMenu, s("addItem:"), thisMenuItem);
|
||||
|
||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||
// If we have no items, just return
|
||||
if ( submenuItems == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over submenu items
|
||||
JsonNode *item;
|
||||
json_foreach(item, submenuItems) {
|
||||
// Get item label
|
||||
processMenuItem(menu, thisMenu, item);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a user menu. Get the common data
|
||||
// Get the label
|
||||
const char *label = getJSONString(item, "Label");
|
||||
@@ -803,13 +726,6 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
label = "(empty)";
|
||||
}
|
||||
|
||||
// Check for a styled label
|
||||
JsonNode *styledLabel = getJSONObject(item, "StyledLabel");
|
||||
|
||||
// Is this an alternate menu item?
|
||||
bool alternate = false;
|
||||
getJSONBool(item, "MacAlternate", &alternate);
|
||||
|
||||
const char *menuid = getJSONString(item, "ID");
|
||||
if ( menuid == NULL) {
|
||||
menuid = "";
|
||||
@@ -830,7 +746,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
bool templateImage = false;
|
||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||
|
||||
int fontSize = 0;
|
||||
int fontSize = 12;
|
||||
getJSONInt(item, "FontSize", &fontSize);
|
||||
|
||||
// If we have an accelerator
|
||||
@@ -863,36 +779,9 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
// Get the Type
|
||||
JsonNode *type = json_find_member(item, "Type");
|
||||
if( type != NULL ) {
|
||||
if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) {
|
||||
id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate, styledLabel);
|
||||
|
||||
// Check if this node has a submenu
|
||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||
if( submenu != NULL ) {
|
||||
// Get the label
|
||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||
const char *name = "";
|
||||
if ( menuNameNode != NULL) {
|
||||
name = menuNameNode->string_;
|
||||
}
|
||||
|
||||
id thisMenu = createMenu(str(name));
|
||||
|
||||
msg_id(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||
|
||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||
// If we have no items, just return
|
||||
if ( submenuItems == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over submenu items
|
||||
JsonNode *item;
|
||||
json_foreach(item, submenuItems) {
|
||||
// Get item label
|
||||
processMenuItem(menu, thisMenu, item);
|
||||
}
|
||||
}
|
||||
if( STREQ(type->string_, "Text")) {
|
||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
||||
}
|
||||
else if ( STREQ(type->string_, "Separator")) {
|
||||
addSeparator(parentMenu);
|
||||
@@ -911,6 +800,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
|
||||
processRadioMenuItem(menu, parentMenu, label, menuid, disabled, checked, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( modifiers != NULL ) {
|
||||
|
||||
@@ -105,13 +105,10 @@ 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 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, JsonNode* styledLabel);
|
||||
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);
|
||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||
|
||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) ;
|
||||
id GetMenu(Menu *menu);
|
||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
||||
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize);
|
||||
|
||||
#endif //ASSETS_C_MENU_DARWIN_H
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,8 +6,6 @@
|
||||
#include "traymenu_darwin.h"
|
||||
#include "trayicons.h"
|
||||
|
||||
extern Class trayMenuDelegateClass;
|
||||
|
||||
// A cache for all our tray menu icons
|
||||
// Global because it's a singleton
|
||||
struct hashmap_s trayIconCache;
|
||||
@@ -31,25 +29,12 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
||||
|
||||
result->ID = mustJSONString(processedJSON, "ID");
|
||||
result->label = mustJSONString(processedJSON, "Label");
|
||||
result->icon = mustJSONString(processedJSON, "Image");
|
||||
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);
|
||||
|
||||
result->styledLabel = getJSONObject(processedJSON, "StyledLabel");
|
||||
result->icon = mustJSONString(processedJSON, "Icon");
|
||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||
|
||||
// Create the menu
|
||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||
result->menu = NewMenu(processedMenu);
|
||||
|
||||
result->delegate = NULL;
|
||||
|
||||
// Init tray status bar item
|
||||
result->statusbaritem = NULL;
|
||||
|
||||
@@ -65,28 +50,15 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
||||
}
|
||||
|
||||
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel) {
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
||||
|
||||
// Exit early if NULL
|
||||
if( trayMenu->label == NULL ) {
|
||||
return;
|
||||
}
|
||||
// Update button label
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
id attributedString = NULL;
|
||||
if( styledLabel != NULL) {
|
||||
attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize);
|
||||
} else {
|
||||
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);
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setTitle:"), str(label));
|
||||
}
|
||||
|
||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||
@@ -96,57 +68,44 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
|
||||
// Empty icon means remove it
|
||||
if( STREMPTY(trayMenu->icon) ) {
|
||||
// Remove image
|
||||
msg_id(statusBarButton, s("setImage:"), NULL);
|
||||
msg(statusBarButton, s("setImage:"), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
||||
|
||||
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
||||
if (trayImage == NULL) {
|
||||
trayImage = createImageFromBase64Data(trayMenu->icon, trayMenu->templateImage);
|
||||
}
|
||||
|
||||
msg_int(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
msg_id(statusBarButton, s("setImage:"), trayImage);
|
||||
|
||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
msg(statusBarButton, s("setImage:"), trayImage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ShowTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// Create a status bar item if we don't have one
|
||||
if( trayMenu->statusbaritem == NULL ) {
|
||||
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
trayMenu->statusbaritem = ((id(*)(id, SEL, CGFloat))objc_msgSend)(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||
msg_reg(trayMenu->statusbaritem, s("retain"));
|
||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||
msg(trayMenu->statusbaritem, s("retain"));
|
||||
|
||||
}
|
||||
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
msg_uint(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
|
||||
// Update the icon if needed
|
||||
UpdateTrayIcon(trayMenu);
|
||||
|
||||
// Update the label if needed
|
||||
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled, trayMenu->styledLabel);
|
||||
UpdateTrayLabel(trayMenu, trayMenu->label);
|
||||
|
||||
// Update the menu
|
||||
id menu = GetMenu(trayMenu->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);
|
||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||
}
|
||||
|
||||
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
||||
@@ -155,10 +114,6 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
|
||||
|
||||
// Delete the old menu
|
||||
DeleteMenu(currentMenu->menu);
|
||||
if( currentMenu->delegate != NULL ) {
|
||||
msg_reg(currentMenu->delegate, s("release"));
|
||||
currentMenu->delegate = NULL;
|
||||
}
|
||||
|
||||
// Set the new one
|
||||
currentMenu->menu = newMenu->menu;
|
||||
@@ -172,7 +127,6 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
|
||||
// Copy the other data
|
||||
currentMenu->ID = newMenu->ID;
|
||||
currentMenu->label = newMenu->label;
|
||||
currentMenu->styledLabel = newMenu->styledLabel;
|
||||
currentMenu->trayIconPosition = newMenu->trayIconPosition;
|
||||
currentMenu->icon = newMenu->icon;
|
||||
|
||||
@@ -180,12 +134,11 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
|
||||
|
||||
void DeleteTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// printf("Freeing TrayMenu:\n");
|
||||
// DumpTrayMenu(trayMenu);
|
||||
|
||||
// Delete the menu
|
||||
DeleteMenu(trayMenu->menu);
|
||||
if( trayMenu->delegate != NULL ) {
|
||||
msg_reg(trayMenu->delegate, s("release"));
|
||||
trayMenu->delegate = NULL;
|
||||
}
|
||||
|
||||
// Free JSON
|
||||
if (trayMenu->processedJSON != NULL ) {
|
||||
@@ -194,32 +147,15 @@ void DeleteTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// Free the status item
|
||||
if ( trayMenu->statusbaritem != NULL ) {
|
||||
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
msg_id(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
||||
msg_reg(trayMenu->statusbaritem, s("release"));
|
||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
msg(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
||||
msg(trayMenu->statusbaritem, s("release"));
|
||||
trayMenu->statusbaritem = NULL;
|
||||
}
|
||||
|
||||
// Free the tray menu memory
|
||||
MEMFREE(trayMenu);
|
||||
}
|
||||
void DeleteTrayMenuKeepStatusBarItem(TrayMenu* trayMenu) {
|
||||
|
||||
// Delete the menu
|
||||
DeleteMenu(trayMenu->menu);
|
||||
if( trayMenu->delegate != NULL ) {
|
||||
msg_reg(trayMenu->delegate, s("release"));
|
||||
trayMenu->delegate = NULL;
|
||||
}
|
||||
|
||||
// Free JSON
|
||||
if (trayMenu->processedJSON != NULL ) {
|
||||
json_delete(trayMenu->processedJSON);
|
||||
}
|
||||
|
||||
// Free the tray menu memory
|
||||
MEMFREE(trayMenu);
|
||||
}
|
||||
|
||||
void LoadTrayIcons() {
|
||||
|
||||
@@ -246,9 +182,9 @@ void LoadTrayIcons() {
|
||||
int length = atoi((const char *)lengthAsString);
|
||||
|
||||
// Create the icon and add to the hashmap
|
||||
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), (id)data, length);
|
||||
id imageData = msg(c("NSData"), s("dataWithBytes:length:"), data, length);
|
||||
id trayImage = ALLOC("NSImage");
|
||||
msg_id(trayImage, s("initWithData:"), imageData);
|
||||
msg(trayImage, s("initWithData:"), imageData);
|
||||
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,14 @@ typedef struct {
|
||||
const char *label;
|
||||
const char *icon;
|
||||
const char *ID;
|
||||
const char *tooltip;
|
||||
|
||||
bool templateImage;
|
||||
const char *fontName;
|
||||
int fontSize;
|
||||
const char *RGBA;
|
||||
|
||||
bool disabled;
|
||||
|
||||
Menu* menu;
|
||||
|
||||
id statusbaritem;
|
||||
unsigned int trayIconPosition;
|
||||
int trayIconPosition;
|
||||
|
||||
JsonNode* processedJSON;
|
||||
|
||||
JsonNode* styledLabel;
|
||||
|
||||
id delegate;
|
||||
|
||||
} TrayMenu;
|
||||
|
||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||
@@ -40,12 +28,11 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel);
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char*);
|
||||
|
||||
void LoadTrayIcons();
|
||||
void UnloadTrayIcons();
|
||||
|
||||
void DeleteTrayMenu(TrayMenu* trayMenu);
|
||||
void DeleteTrayMenuKeepStatusBarItem(TrayMenu* trayMenu);
|
||||
|
||||
#endif //TRAYMENU_DARWIN_H
|
||||
|
||||
@@ -16,11 +16,6 @@ TrayMenuStore* NewTrayMenuStore() {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -30,19 +25,15 @@ int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
}
|
||||
|
||||
void DumpTrayMenuStore(TrayMenuStore* store) {
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
}
|
||||
|
||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
|
||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
//TODO: check if there is already an entry for this menu
|
||||
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) {
|
||||
@@ -52,13 +43,12 @@ int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
}
|
||||
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
||||
pthread_mutex_lock(&store->lock);
|
||||
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
||||
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
||||
}
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
}
|
||||
|
||||
|
||||
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
DeleteTrayMenu(e->data);
|
||||
return -1;
|
||||
@@ -75,39 +65,22 @@ void DeleteTrayMenuStore(TrayMenuStore *store) {
|
||||
|
||||
// Destroy tray menu map
|
||||
hashmap_destroy(&store->trayMenuMap);
|
||||
|
||||
pthread_mutex_destroy(&store->lock);
|
||||
}
|
||||
|
||||
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||
// Get the current menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
return result;
|
||||
return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
}
|
||||
|
||||
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||
// Get the current menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
if (result == NULL ) {
|
||||
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
||||
}
|
||||
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) {
|
||||
// Parse the JSON
|
||||
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
||||
@@ -118,20 +91,8 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
||||
|
||||
// Check we have this menu
|
||||
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);
|
||||
|
||||
JsonNode *styledLabel = getJSONObject(parsedUpdate, "StyledLabel");
|
||||
|
||||
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled, styledLabel);
|
||||
|
||||
json_delete(parsedUpdate);
|
||||
}
|
||||
|
||||
void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
@@ -144,9 +105,7 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
// If we don't have a menu, we create one
|
||||
if ( currentMenu == NULL ) {
|
||||
// Store the new menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Show it
|
||||
ShowTrayMenu(newMenu);
|
||||
@@ -157,17 +116,18 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
// Save the status bar reference
|
||||
newMenu->statusbaritem = currentMenu->statusbaritem;
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Delete the current menu
|
||||
DeleteTrayMenuKeepStatusBarItem(currentMenu);
|
||||
DeleteMenu(currentMenu->menu);
|
||||
currentMenu->menu = NULL;
|
||||
|
||||
// Free the tray menu memory
|
||||
MEMFREE(currentMenu);
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Show the updated menu
|
||||
ShowTrayMenu(newMenu);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#ifndef TRAYMENUSTORE_DARWIN_H
|
||||
#define TRAYMENUSTORE_DARWIN_H
|
||||
|
||||
#include "traymenu_darwin.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
|
||||
int dummy;
|
||||
@@ -17,8 +13,6 @@ typedef struct {
|
||||
// It maps tray IDs to TrayMenu*
|
||||
struct hashmap_s trayMenuMap;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
|
||||
} TrayMenuStore;
|
||||
|
||||
TrayMenuStore* NewTrayMenuStore();
|
||||
@@ -28,9 +22,6 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON);
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||
|
||||
TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID);
|
||||
|
||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
||||
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
||||
|
||||
#endif //TRAYMENUSTORE_DARWIN_H
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// +build !windows
|
||||
|
||||
/**
|
||||
/**
|
||||
* Copyright (c) 2014 rxi
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user