mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 12:15:02 -07:00
Compare commits
90 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2432fccf71 | ||
|
|
70510fd180 | ||
|
|
17c6201469 | ||
|
|
0f209c8900 | ||
|
|
cbf043585c | ||
|
|
5ae621ceaa | ||
|
|
1231b59443 | ||
|
|
b18d4fbf41 | ||
|
|
9ec5605e63 | ||
|
|
98a4de8878 | ||
|
|
5fe709f558 | ||
|
|
5231a6893b | ||
|
|
74f3ce990f | ||
|
|
998a913853 | ||
|
|
964844835c | ||
|
|
4e152bb849 | ||
|
|
51133d098c | ||
|
|
d4191e7d1b | ||
|
|
9c273bc745 | ||
|
|
c6f6ad6beb | ||
|
|
4362a14459 | ||
|
|
0080e9e311 | ||
|
|
83d9297cac | ||
|
|
d8ad250608 | ||
|
|
eac8880f6d | ||
|
|
f47100e71c | ||
|
|
9a81a57d13 | ||
|
|
354429bc28 | ||
|
|
99d4d9490c | ||
|
|
61afe711bd | ||
|
|
6e3cfc157f | ||
|
|
30d762372f | ||
|
|
2dbaabb74c | ||
|
|
f8bae0430f | ||
|
|
21c07497d7 | ||
|
|
9b9bcd657f | ||
|
|
02038aa543 | ||
|
|
9910d1127a | ||
|
|
21a0245985 | ||
|
|
e860f3a00e | ||
|
|
2e480d2c52 | ||
|
|
2c0f93d647 | ||
|
|
41f973c7d5 | ||
|
|
2d1447d558 | ||
|
|
7c22cbcf38 | ||
|
|
e4b03f510b | ||
|
|
8dfd206aa9 | ||
|
|
6dabab1d2e | ||
|
|
c3bd8b1a85 | ||
|
|
e1b7332c47 | ||
|
|
5cd08e45f0 | ||
|
|
c2d03f0e6e | ||
|
|
d3501f4cb7 | ||
|
|
ee82cd25b7 | ||
|
|
bbb07e17d9 | ||
|
|
e6b40b55c4 | ||
|
|
7573f68df3 | ||
|
|
ceaacc7ba9 | ||
|
|
0e24f75753 | ||
|
|
82b9deeee4 | ||
|
|
cfa40b797f | ||
|
|
5aeb68acb7 | ||
|
|
b81101414f | ||
|
|
7ae89d04bb | ||
|
|
1c566f3802 | ||
|
|
c9c3c9ab90 | ||
|
|
56394ac50e | ||
|
|
f7c2f12ab2 | ||
|
|
a6bb6e0c93 | ||
|
|
4a5863e6ac | ||
|
|
913fe8d184 | ||
|
|
4ce8130cdf | ||
|
|
fe87463b78 | ||
|
|
fe0f0e29e8 | ||
|
|
83d6dac7cf | ||
|
|
02500e0930 | ||
|
|
5e1187f437 | ||
|
|
064ff3b65e | ||
|
|
b5c7019bf0 | ||
|
|
e9d16e77a3 | ||
|
|
2415d4c531 | ||
|
|
3f75213ce3 | ||
|
|
6120ceabf1 | ||
|
|
95a95d1750 | ||
|
|
d923e84456 | ||
|
|
343f573e78 | ||
|
|
c6d87da4f0 | ||
|
|
a9faebe51a | ||
|
|
d436f5d1be | ||
|
|
f40899821f |
@@ -3,12 +3,12 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"amd": true,
|
"amd": true,
|
||||||
"node": true,
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2016,
|
"ecmaVersion": 2016,
|
||||||
"sourceType": "module",
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function OpenURL(url) {
|
|||||||
* Opens the given filename using the system's default file handler
|
* Opens the given filename using the system's default file handler
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {sting} filename
|
* @param {string} filename
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function OpenFile(filename) {
|
export function OpenFile(filename) {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ if (window.crypto) {
|
|||||||
export function Call(bindingName, data, timeout) {
|
export function Call(bindingName, data, timeout) {
|
||||||
|
|
||||||
// Timeout infinite by default
|
// Timeout infinite by default
|
||||||
if (timeout == null || timeout == undefined) {
|
if (timeout == null) {
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function Invoke(message) {
|
|||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
* @param {string} payload
|
* @param {Object} payload
|
||||||
* @param {string=} callbackID
|
* @param {string=} callbackID
|
||||||
*/
|
*/
|
||||||
export function SendMessage(type, payload, callbackID) {
|
export function SendMessage(type, payload, callbackID) {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
bridge.js
|
index.js
|
||||||
@@ -5,33 +5,42 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/internal/process"
|
"github.com/wailsapp/wails/v2/internal/process"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func LogGreen(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.Green(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogRed(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.Red(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogDarkYellow(message string, args ...interface{}) {
|
||||||
|
text := fmt.Sprintf(message, args...)
|
||||||
|
println(colour.DarkYellow(text))
|
||||||
|
}
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
// AddSubcommand adds the `dev` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("dev", "Development mode")
|
command := app.NewSubCommand("dev", "Development mode")
|
||||||
|
|
||||||
outputType := "desktop"
|
|
||||||
|
|
||||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
|
||||||
|
|
||||||
// Setup target type flag
|
|
||||||
description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
|
|
||||||
command.StringFlag("t", description, &outputType)
|
|
||||||
|
|
||||||
// Passthrough ldflags
|
// Passthrough ldflags
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
@@ -42,15 +51,17 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// extensions to trigger rebuilds
|
// extensions to trigger rebuilds
|
||||||
extensions := "go"
|
extensions := "go"
|
||||||
command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||||
|
|
||||||
|
// extensions to trigger rebuilds
|
||||||
|
showWarnings := false
|
||||||
|
command.BoolFlag("w", "Show warnings", &showWarnings)
|
||||||
|
|
||||||
|
loglevel := ""
|
||||||
|
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Validate inputs
|
|
||||||
if !validTargetTypes.Contains(outputType) {
|
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := clilogger.New(w)
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
@@ -64,7 +75,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
var debugBinaryProcess *process.Process = nil
|
||||||
var buildFrontend bool = true
|
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
@@ -75,8 +85,13 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Do initial build
|
// Do initial build
|
||||||
logger.Println("Building application for development...")
|
logger.Println("Building application for development...")
|
||||||
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
newProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||||
|
if newProcess != nil {
|
||||||
|
debugBinaryProcess = newProcess
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||||
// logger.Println("event: %+v", event)
|
// logger.Println("event: %+v", event)
|
||||||
|
|
||||||
@@ -89,7 +104,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal("%s", err.Error())
|
logger.Fatal("%s", err.Error())
|
||||||
}
|
}
|
||||||
logger.Println("Watching directory: %s", event.Name)
|
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -98,38 +113,33 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
// Check for file writes
|
// Check for file writes
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
|
||||||
// logger.Println("modified file: %s", event.Name)
|
|
||||||
var rebuild bool = false
|
var rebuild bool = false
|
||||||
|
|
||||||
// Iterate all file patterns
|
// Iterate all file patterns
|
||||||
for _, pattern := range extensionsThatTriggerARebuild {
|
for _, pattern := range extensionsThatTriggerARebuild {
|
||||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
if strings.HasSuffix(event.Name, pattern) {
|
||||||
if err != nil {
|
rebuild = true
|
||||||
logger.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
if rebuild {
|
|
||||||
// Only build frontend when the file isn't a Go file
|
|
||||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rebuild {
|
if !rebuild {
|
||||||
logger.Println("Filename change: %s did not match extension list %s", event.Name, extensions)
|
if showWarnings {
|
||||||
|
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if buildFrontend {
|
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
||||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
|
||||||
} else {
|
|
||||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a rebuild
|
// Do a rebuild
|
||||||
|
|
||||||
// Try and build the app
|
// Try and build the app
|
||||||
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error during build: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
// If we have a new process, save it
|
// If we have a new process, save it
|
||||||
if newBinaryProcess != nil {
|
if newBinaryProcess != nil {
|
||||||
debugBinaryProcess = newBinaryProcess
|
debugBinaryProcess = newBinaryProcess
|
||||||
@@ -139,23 +149,28 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get project dir
|
// Get project dir
|
||||||
dir, err := os.Getwd()
|
projectDir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all subdirectories
|
// Get all subdirectories
|
||||||
dirs, err := fs.GetSubdirectories(dir)
|
dirs, err := fs.GetSubdirectories(projectDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogGreen("Watching (sub)/directory: %s", projectDir)
|
||||||
|
|
||||||
// Setup a watcher for non-node_modules directories
|
// Setup a watcher for non-node_modules directories
|
||||||
dirs.Each(func(dir string) {
|
dirs.Each(func(dir string) {
|
||||||
if strings.Contains(dir, "node_modules") {
|
if strings.Contains(dir, "node_modules") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Println("Watching directory: %s", dir)
|
// Ignore build directory
|
||||||
|
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = watcher.Add(dir)
|
err = watcher.Add(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
logger.Fatal(err.Error())
|
||||||
@@ -167,7 +182,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
for quit == false {
|
for quit == false {
|
||||||
select {
|
select {
|
||||||
case <-quitChannel:
|
case <-quitChannel:
|
||||||
println()
|
LogGreen("\nCaught quit")
|
||||||
// Notify debouncer to quit
|
// Notify debouncer to quit
|
||||||
debounceQuit <- true
|
debounceQuit <- true
|
||||||
quit = true
|
quit = true
|
||||||
@@ -182,7 +197,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Development mode exited")
|
LogGreen("Development mode exited")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -209,15 +224,15 @@ exit:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string) (*process.Process, error) {
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||||
println()
|
println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
LogRed("Build error - continuing to run current version")
|
||||||
return nil
|
LogDarkYellow(err.Error())
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
logger.Println("Build new binary: %s", appBinary)
|
|
||||||
|
|
||||||
// Kill existing binary if need be
|
// Kill existing binary if need be
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
@@ -233,7 +248,7 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
// TODO: Generate `backend.js`
|
// TODO: Generate `backend.js`
|
||||||
|
|
||||||
// Start up new binary
|
// Start up new binary
|
||||||
newProcess := process.NewProcess(logger, appBinary)
|
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
||||||
err = newProcess.Start()
|
err = newProcess.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove binary
|
// Remove binary
|
||||||
@@ -244,13 +259,13 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
logger.Fatal("Unable to start application: %s", err.Error())
|
logger.Fatal("Unable to start application: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProcess
|
return newProcess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
||||||
|
|
||||||
// Create random output file
|
// Create random output file
|
||||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
||||||
|
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
@@ -262,7 +277,7 @@ func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, co
|
|||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
OutputFile: outputFile,
|
OutputFile: outputFile,
|
||||||
IgnoreFrontend: !buildFrontend,
|
IgnoreFrontend: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Build(buildOptions)
|
return build.Build(buildOptions)
|
||||||
|
|||||||
164
v2/cmd/wails/internal/commands/update/update.go
Normal file
164
v2/cmd/wails/internal/commands/update/update.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package update
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/github"
|
||||||
|
|
||||||
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddSubcommand adds the `init` command for the Wails application
|
||||||
|
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.`)
|
||||||
|
|
||||||
|
// Setup flags
|
||||||
|
var prereleaseRequired bool
|
||||||
|
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
||||||
|
|
||||||
|
var specificVersion string
|
||||||
|
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
||||||
|
|
||||||
|
command.Action(func() error {
|
||||||
|
|
||||||
|
// Create logger
|
||||||
|
logger := clilogger.New(w)
|
||||||
|
|
||||||
|
// Print banner
|
||||||
|
app.PrintBanner()
|
||||||
|
logger.Println("Checking for updates...")
|
||||||
|
|
||||||
|
var desiredVersion *github.SemanticVersion
|
||||||
|
var err error
|
||||||
|
var valid bool
|
||||||
|
|
||||||
|
if len(specificVersion) > 0 {
|
||||||
|
// Check if this is a valid version
|
||||||
|
valid, err = github.IsValidTag(specificVersion)
|
||||||
|
if err == nil {
|
||||||
|
if !valid {
|
||||||
|
err = fmt.Errorf("version '%s' is invalid", specificVersion)
|
||||||
|
} else {
|
||||||
|
desiredVersion, err = github.NewSemanticVersion(specificVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if prereleaseRequired {
|
||||||
|
desiredVersion, err = github.GetLatestPreRelease()
|
||||||
|
} else {
|
||||||
|
desiredVersion, err = github.GetLatestStableRelease()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
fmt.Println(" Current Version : " + currentVersion)
|
||||||
|
|
||||||
|
if len(specificVersion) > 0 {
|
||||||
|
fmt.Printf(" Desired Version : v%s\n", desiredVersion)
|
||||||
|
} else {
|
||||||
|
if prereleaseRequired {
|
||||||
|
fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" Latest Release : v%s\n", desiredVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateToVersion(logger, desiredVersion, len(specificVersion) > 0, currentVersion)
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.SemanticVersion, force bool, currentVersion string) error {
|
||||||
|
|
||||||
|
var targetVersionString = "v" + targetVersion.String()
|
||||||
|
|
||||||
|
// Early exit
|
||||||
|
if targetVersionString == currentVersion {
|
||||||
|
logger.Println("Looks like you're up to date!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var desiredVersion string
|
||||||
|
|
||||||
|
if !force {
|
||||||
|
|
||||||
|
compareVersion := currentVersion
|
||||||
|
|
||||||
|
currentVersion, err := github.NewSemanticVersion(compareVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var success bool
|
||||||
|
|
||||||
|
// Release -> Pre-Release = Massage current version to prerelease format
|
||||||
|
if targetVersion.IsPreRelease() && currentVersion.IsRelease() {
|
||||||
|
testVersion, err := github.NewSemanticVersion(compareVersion + "-0")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success, _ = targetVersion.IsGreaterThan(testVersion)
|
||||||
|
}
|
||||||
|
// Pre-Release -> Release = Massage target version to prerelease format
|
||||||
|
if targetVersion.IsRelease() && currentVersion.IsPreRelease() {
|
||||||
|
// We are ok with greater than or equal
|
||||||
|
mainversion := currentVersion.MainVersion()
|
||||||
|
targetVersion, err = github.NewSemanticVersion(targetVersion.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
success, _ = targetVersion.IsGreaterThanOrEqual(mainversion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release -> Release = Standard check
|
||||||
|
if (targetVersion.IsRelease() && currentVersion.IsRelease()) ||
|
||||||
|
(targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) {
|
||||||
|
|
||||||
|
success, _ = targetVersion.IsGreaterThan(currentVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare
|
||||||
|
if !success {
|
||||||
|
logger.Println("Error: The requested version is lower than the current version.")
|
||||||
|
logger.Println("If this is what you really want to do, use `wails update -version %s`", targetVersionString)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
desiredVersion = "v" + targetVersion.String()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
desiredVersion = "v" + targetVersion.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
||||||
|
|
||||||
|
// Run command in non module directory
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Cannot find home directory! Please file a bug report!")
|
||||||
|
}
|
||||||
|
|
||||||
|
sout, serr, err := shell.RunCommand(homeDir, "go", "get", "github.com/wailsapp/wails/v2/cmd/wails@"+desiredVersion)
|
||||||
|
if err != nil {
|
||||||
|
logger.Println("Failed.")
|
||||||
|
logger.Println(sout + `\n` + serr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
logger.Println("Wails CLI updated to " + desiredVersion)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
"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/debug"
|
||||||
@@ -17,12 +22,18 @@ func fatal(message string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func banner(_ *clir.Cli) string {
|
||||||
|
return fmt.Sprintf("%s %s - Go/HTML Application Framework", colour.Yellow("Wails"), colour.DarkRed(version))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||||
|
|
||||||
|
app.SetBannerFunction(banner)
|
||||||
|
|
||||||
build.AddBuildSubcommand(app, os.Stdout)
|
build.AddBuildSubcommand(app, os.Stdout)
|
||||||
err = initialise.AddSubcommand(app, os.Stdout)
|
err = initialise.AddSubcommand(app, os.Stdout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,6 +59,11 @@ func main() {
|
|||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = update.AddSubcommand(app, os.Stdout, version)
|
||||||
|
if err != nil {
|
||||||
|
fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
err = app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("\n\nERROR: " + err.Error())
|
println("\n\nERROR: " + err.Error())
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.11"
|
var version = "v2.0.0-alpha.35"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
module github.com/wailsapp/wails/v2
|
module github.com/wailsapp/wails/v2
|
||||||
|
|
||||||
go 1.15
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Masterminds/semver v1.5.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
github.com/gorilla/websocket v1.4.1
|
||||||
github.com/imdario/mergo v0.3.11
|
github.com/imdario/mergo v0.3.11
|
||||||
github.com/jackmordaunt/icns v1.0.0
|
github.com/jackmordaunt/icns v1.0.0
|
||||||
github.com/leaanthony/clir v1.0.4
|
github.com/leaanthony/clir v1.0.4
|
||||||
@@ -13,16 +15,15 @@ require (
|
|||||||
github.com/leaanthony/slicer v1.5.0
|
github.com/leaanthony/slicer v1.5.0
|
||||||
github.com/matryer/is v1.4.0
|
github.com/matryer/is v1.4.0
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
github.com/tdewolff/minify v2.3.6+incompatible
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||||
github.com/tdewolff/test v1.0.6 // indirect
|
github.com/tdewolff/test v1.0.6 // indirect
|
||||||
|
github.com/wzshiming/ctc v1.2.3
|
||||||
github.com/xyproto/xpm v1.2.1
|
github.com/xyproto/xpm v1.2.1
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
|
||||||
nhooyr.io/websocket v1.8.6
|
nhooyr.io/websocket v1.8.6
|
||||||
)
|
)
|
||||||
|
|||||||
23
v2/go.sum
23
v2/go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||||
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -9,7 +11,6 @@ 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-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 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
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 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
@@ -39,9 +40,6 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn
|
|||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
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.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||||
@@ -62,17 +60,13 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
|
|||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
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 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
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 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
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/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||||
@@ -84,6 +78,10 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
|||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
|
||||||
|
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
|
||||||
|
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
|
||||||
|
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20=
|
||||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -100,33 +98,26 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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.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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
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/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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/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 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|||||||
@@ -2,11 +2,40 @@
|
|||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
// Init initialises the application for a debug environment
|
// Init initialises the application for a debug environment
|
||||||
func (a *App) Init() error {
|
func (a *App) Init() error {
|
||||||
// Indicate debug mode
|
// Indicate debug mode
|
||||||
a.debug = true
|
a.debug = true
|
||||||
// Enable dev tools
|
|
||||||
a.options.DevTools = true
|
if a.appType == "desktop" {
|
||||||
|
// Enable dev tools
|
||||||
|
a.options.DevTools = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set log levels
|
||||||
|
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||||
|
flag.Parse()
|
||||||
|
if len(*loglevel) > 0 {
|
||||||
|
switch strings.ToLower(*loglevel) {
|
||||||
|
case "trace":
|
||||||
|
a.logger.SetLogLevel(logger.TRACE)
|
||||||
|
case "info":
|
||||||
|
a.logger.SetLogLevel(logger.INFO)
|
||||||
|
case "warning":
|
||||||
|
a.logger.SetLogLevel(logger.WARNING)
|
||||||
|
case "error":
|
||||||
|
a.logger.SetLogLevel(logger.ERROR)
|
||||||
|
default:
|
||||||
|
a.logger.SetLogLevel(logger.DEBUG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !desktop,!hybrid,!server
|
// +build !desktop,!hybrid,!server,!dev
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
@@ -8,9 +8,10 @@ package app
|
|||||||
// will be unknown and the application will not work as expected.
|
// will be unknown and the application will not work as expected.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,7 +39,3 @@ func (a *App) Run() error {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the dummy interface
|
|
||||||
func (a *App) Bind(_ interface{}) {
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -17,6 +20,8 @@ import (
|
|||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
|
appType string
|
||||||
|
|
||||||
window *ffenestri.Application
|
window *ffenestri.Application
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
@@ -24,10 +29,10 @@ type App struct {
|
|||||||
options *options.App
|
options *options.App
|
||||||
|
|
||||||
// Subsystems
|
// Subsystems
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
binding *subsystem.Binding
|
//binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
menu *subsystem.Menu
|
menu *subsystem.Menu
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
@@ -79,11 +84,15 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
||||||
|
|
||||||
|
// Create binding exemptions - Ugly hack. There must be a better way
|
||||||
|
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
|
appType: "desktop",
|
||||||
window: window,
|
window: window,
|
||||||
servicebus: servicebus.New(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
bindings: binding.NewBindings(myLogger),
|
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
startupCallback: appoptions.Startup,
|
startupCallback: appoptions.Startup,
|
||||||
shutdownCallback: appoptions.Shutdown,
|
shutdownCallback: appoptions.Shutdown,
|
||||||
@@ -103,13 +112,10 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup a context
|
||||||
signalsubsystem, err := signal.NewManager(a.servicebus, a.logger)
|
var subsystemWaitGroup sync.WaitGroup
|
||||||
if err != nil {
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
return err
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
@@ -118,7 +124,7 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -132,17 +138,6 @@ func (a *App) Run() error {
|
|||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||||
|
|
||||||
// Start the binding subsystem
|
|
||||||
bindingsubsystem, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.binding = bindingsubsystem
|
|
||||||
err = a.binding.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the logging subsystem
|
// Start the logging subsystem
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -166,18 +161,18 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the eventing subsystem
|
// Start the eventing subsystem
|
||||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.event = event
|
a.event = eventsubsystem
|
||||||
err = a.event.Start()
|
err = a.event.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the menu subsystem
|
// Start the menu subsystem
|
||||||
menusubsystem, err := subsystem.NewMenu(a.servicebus, a.logger, a.menuManager)
|
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -188,11 +183,11 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.call = call
|
a.call = callSubsystem
|
||||||
err = a.call.Start()
|
err = a.call.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -204,23 +199,42 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := a.window.Run(dispatcher, bindingDump, a.debug)
|
// 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")
|
a.logger.Trace("Ffenestri.Run() exited")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close down all the subsystems
|
||||||
|
a.logger.Trace("Cancelling subsystems")
|
||||||
|
cancel()
|
||||||
|
subsystemWaitGroup.Wait()
|
||||||
|
|
||||||
|
a.logger.Trace("Cancelling dispatcher")
|
||||||
|
dispatcher.Close()
|
||||||
|
|
||||||
|
// Close log
|
||||||
|
a.logger.Trace("Stopping log")
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
a.logger.Trace("Stopping Service bus")
|
||||||
err = a.servicebus.Stop()
|
err = a.servicebus.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
// Shutdown callback
|
||||||
}
|
if a.shutdownCallback != nil {
|
||||||
|
a.shutdownCallback()
|
||||||
// Bind a struct to the application by passing in
|
|
||||||
// a pointer to it
|
|
||||||
func (a *App) Bind(structPtr interface{}) {
|
|
||||||
|
|
||||||
// Add the struct to the bindings
|
|
||||||
err := a.bindings.Add(structPtr)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
244
v2/internal/app/dev.go
Normal file
244
v2/internal/app/dev.go
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
// +build dev
|
||||||
|
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/bridge"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/signal"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// App defines a Wails application structure
|
||||||
|
type App struct {
|
||||||
|
appType string
|
||||||
|
|
||||||
|
servicebus *servicebus.ServiceBus
|
||||||
|
logger *logger.Logger
|
||||||
|
signal *signal.Manager
|
||||||
|
options *options.App
|
||||||
|
|
||||||
|
// Subsystems
|
||||||
|
log *subsystem.Log
|
||||||
|
runtime *subsystem.Runtime
|
||||||
|
event *subsystem.Event
|
||||||
|
//binding *subsystem.Binding
|
||||||
|
call *subsystem.Call
|
||||||
|
menu *subsystem.Menu
|
||||||
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
|
menuManager *menumanager.Manager
|
||||||
|
|
||||||
|
// Indicates if the app is in debug mode
|
||||||
|
debug bool
|
||||||
|
|
||||||
|
// This is our binding DB
|
||||||
|
bindings *binding.Bindings
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
loglevelStore *runtime.Store
|
||||||
|
appconfigStore *runtime.Store
|
||||||
|
|
||||||
|
// Startup/Shutdown
|
||||||
|
startupCallback func(*runtime.Runtime)
|
||||||
|
shutdownCallback func()
|
||||||
|
|
||||||
|
// Bridge
|
||||||
|
bridge *bridge.Bridge
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create App
|
||||||
|
func CreateApp(appoptions *options.App) (*App, error) {
|
||||||
|
|
||||||
|
// Merge default options
|
||||||
|
options.MergeDefaults(appoptions)
|
||||||
|
|
||||||
|
// Set up logger
|
||||||
|
myLogger := logger.New(appoptions.Logger)
|
||||||
|
|
||||||
|
// Create the menu manager
|
||||||
|
menuManager := menumanager.NewManager()
|
||||||
|
|
||||||
|
// Process the application menu
|
||||||
|
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
||||||
|
|
||||||
|
// Process context menus
|
||||||
|
contextMenus := options.GetContextMenus(appoptions)
|
||||||
|
for _, contextMenu := range contextMenus {
|
||||||
|
menuManager.AddContextMenu(contextMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process tray menus
|
||||||
|
trayMenus := options.GetTrayMenus(appoptions)
|
||||||
|
for _, trayMenu := range trayMenus {
|
||||||
|
menuManager.AddTrayMenu(trayMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create binding exemptions - Ugly hack. There must be a better way
|
||||||
|
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||||
|
|
||||||
|
result := &App{
|
||||||
|
appType: "dev",
|
||||||
|
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
||||||
|
logger: myLogger,
|
||||||
|
servicebus: servicebus.New(myLogger),
|
||||||
|
startupCallback: appoptions.Startup,
|
||||||
|
shutdownCallback: appoptions.Shutdown,
|
||||||
|
bridge: bridge.NewBridge(myLogger),
|
||||||
|
menuManager: menuManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
result.options = appoptions
|
||||||
|
|
||||||
|
// Initialise the app
|
||||||
|
err := result.Init()
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the application
|
||||||
|
func (a *App) Run() error {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Setup a context
|
||||||
|
var subsystemWaitGroup sync.WaitGroup
|
||||||
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
|
// Start the service bus
|
||||||
|
a.servicebus.Debug()
|
||||||
|
err = a.servicebus.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.runtime = runtimesubsystem
|
||||||
|
err = a.runtime.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||||
|
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||||
|
|
||||||
|
// Start the logging subsystem
|
||||||
|
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.log = log
|
||||||
|
err = a.log.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the dispatcher
|
||||||
|
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.dispatcher = dispatcher
|
||||||
|
err = dispatcher.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the eventing subsystem
|
||||||
|
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.event = eventsubsystem
|
||||||
|
err = a.event.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the menu subsystem
|
||||||
|
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.menu = menusubsystem
|
||||||
|
err = a.menu.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the call subsystem
|
||||||
|
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.call = callSubsystem
|
||||||
|
err = a.call.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump bindings as a debug
|
||||||
|
bindingDump, err := a.bindings.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close down all the subsystems
|
||||||
|
a.logger.Trace("Cancelling subsystems")
|
||||||
|
cancel()
|
||||||
|
subsystemWaitGroup.Wait()
|
||||||
|
|
||||||
|
a.logger.Trace("Cancelling dispatcher")
|
||||||
|
dispatcher.Close()
|
||||||
|
|
||||||
|
// Close log
|
||||||
|
a.logger.Trace("Stopping log")
|
||||||
|
log.Close()
|
||||||
|
|
||||||
|
a.logger.Trace("Stopping Service bus")
|
||||||
|
err = a.servicebus.Stop()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown callback
|
||||||
|
if a.shutdownCallback != nil {
|
||||||
|
a.shutdownCallback()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@ func CreateApp(options *Options) *App {
|
|||||||
webserver: webserver.NewWebServer(myLogger),
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
bindings: binding.NewBindings(myLogger),
|
bindings: binding.NewBindings(myLogger, options.Bind),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the app
|
// Initialise the app
|
||||||
@@ -192,14 +192,3 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind a struct to the application by passing in
|
|
||||||
// a pointer to it
|
|
||||||
func (a *App) Bind(structPtr interface{}) {
|
|
||||||
|
|
||||||
// Add the struct to the bindings
|
|
||||||
err := a.bindings.Add(structPtr)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||||
@@ -17,12 +20,16 @@ import (
|
|||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
|
appType string
|
||||||
|
|
||||||
binding *subsystem.Binding
|
binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
|
|
||||||
|
options *options.App
|
||||||
|
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
@@ -30,28 +37,40 @@ type App struct {
|
|||||||
webserver *webserver.WebServer
|
webserver *webserver.WebServer
|
||||||
|
|
||||||
debug bool
|
debug bool
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
loglevelStore *runtime.Store
|
||||||
|
appconfigStore *runtime.Store
|
||||||
|
|
||||||
|
// Startup/Shutdown
|
||||||
|
startupCallback func(*runtime.Runtime)
|
||||||
|
shutdownCallback func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
func CreateApp(options *Options) *App {
|
func CreateApp(appoptions *options.App) (*App, error) {
|
||||||
options.mergeDefaults()
|
|
||||||
// We ignore the inputs (for now)
|
|
||||||
|
|
||||||
// TODO: Allow logger output override on CLI
|
// Merge default options
|
||||||
myLogger := logger.New(os.Stdout)
|
options.MergeDefaults(appoptions)
|
||||||
myLogger.SetLogLevel(logger.TRACE)
|
|
||||||
|
// Set up logger
|
||||||
|
myLogger := logger.New(appoptions.Logger)
|
||||||
|
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
bindings: binding.NewBindings(myLogger),
|
appType: "server",
|
||||||
logger: myLogger,
|
bindings: binding.NewBindings(myLogger, options.Bind),
|
||||||
servicebus: servicebus.New(myLogger),
|
logger: myLogger,
|
||||||
webserver: webserver.NewWebServer(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
|
startupCallback: appoptions.Startup,
|
||||||
|
shutdownCallback: appoptions.Shutdown,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise app
|
// Initialise app
|
||||||
result.Init()
|
result.Init()
|
||||||
|
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
@@ -88,8 +107,21 @@ func (a *App) Run() error {
|
|||||||
if debugMode {
|
if debugMode {
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the runtime
|
||||||
|
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.runtime = runtime
|
||||||
|
a.runtime.Start()
|
||||||
|
|
||||||
|
// Application Stores
|
||||||
|
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
||||||
|
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
||||||
|
|
||||||
a.servicebus.Start()
|
a.servicebus.Start()
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -102,14 +134,6 @@ func (a *App) Run() error {
|
|||||||
a.dispatcher = dispatcher
|
a.dispatcher = dispatcher
|
||||||
a.dispatcher.Start()
|
a.dispatcher.Start()
|
||||||
|
|
||||||
// Start the runtime
|
|
||||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtime
|
|
||||||
a.runtime.Start()
|
|
||||||
|
|
||||||
// Start the binding subsystem
|
// Start the binding subsystem
|
||||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -127,7 +151,7 @@ func (a *App) Run() error {
|
|||||||
a.event.Start()
|
a.event.Start()
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -147,14 +171,3 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind a struct to the application by passing in
|
|
||||||
// a pointer to it
|
|
||||||
func (a *App) Bind(structPtr interface{}) {
|
|
||||||
|
|
||||||
// Add the struct to the bindings
|
|
||||||
err := a.bindings.Add(structPtr)
|
|
||||||
if err != nil {
|
|
||||||
a.logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
11
v2/internal/binding/assets/package.json
Normal file
11
v2/internal/binding/assets/package.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"name": "backend",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Package to wrap backend method calls",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
@@ -2,28 +2,53 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bindings struct {
|
type Bindings struct {
|
||||||
db *DB
|
db *DB
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
exemptions slicer.StringSlicer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
// NewBindings returns a new Bindings object
|
||||||
func NewBindings(logger *logger.Logger) *Bindings {
|
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
||||||
return &Bindings{
|
result := &Bindings{
|
||||||
db: newDB(),
|
db: newDB(),
|
||||||
logger: logger.CustomLogger("Bindings"),
|
logger: logger.CustomLogger("Bindings"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, exemption := range exemptions {
|
||||||
|
if exemptions == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
||||||
|
// Yuk yuk yuk! Is there a better way?
|
||||||
|
name = strings.TrimSuffix(name, "-fm")
|
||||||
|
result.exemptions.Add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the structs to bind
|
||||||
|
for _, ptr := range structPointersToBind {
|
||||||
|
err := result.Add(ptr)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the given struct methods to the Bindings
|
// Add the given struct methods to the Bindings
|
||||||
func (b *Bindings) Add(structPtr interface{}) error {
|
func (b *Bindings) Add(structPtr interface{}) error {
|
||||||
|
|
||||||
methods, err := getMethods(structPtr)
|
methods, err := b.getMethods(structPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
||||||
}
|
}
|
||||||
@@ -36,7 +61,6 @@ func (b *Bindings) Add(structPtr interface{}) error {
|
|||||||
|
|
||||||
// Add it as a regular method
|
// Add it as a regular method
|
||||||
b.db.AddMethod(packageName, structName, methodName, method)
|
b.db.AddMethod(packageName, structName, methodName, method)
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ func (b *BoundMethod) OutputCount() int {
|
|||||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
||||||
|
|
||||||
result := make([]interface{}, b.InputCount())
|
result := make([]interface{}, b.InputCount())
|
||||||
|
if len(args) != b.InputCount() {
|
||||||
|
return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Name, b.InputCount())
|
||||||
|
}
|
||||||
for index, arg := range args {
|
for index, arg := range args {
|
||||||
typ := b.Inputs[index].reflectType
|
typ := b.Inputs[index].reflectType
|
||||||
inputValue := reflect.New(typ).Interface()
|
inputValue := reflect.New(typ).Interface()
|
||||||
|
|||||||
174
v2/internal/binding/generate.go
Normal file
174
v2/internal/binding/generate.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
|
||||||
|
"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
|
||||||
|
|
||||||
|
func (b *Bindings) GenerateBackendJS() {
|
||||||
|
|
||||||
|
store := b.db.store
|
||||||
|
var output bytes.Buffer
|
||||||
|
|
||||||
|
output.WriteString(`// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
const backend = {`)
|
||||||
|
output.WriteString("\n")
|
||||||
|
|
||||||
|
var sortedPackageNames slicer.StringSlicer
|
||||||
|
for packageName := range store {
|
||||||
|
sortedPackageNames.Add(packageName)
|
||||||
|
}
|
||||||
|
sortedPackageNames.Sort()
|
||||||
|
sortedPackageNames.Each(func(packageName string) {
|
||||||
|
packages := store[packageName]
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||||
|
output.WriteString("\n")
|
||||||
|
var sortedStructNames slicer.StringSlicer
|
||||||
|
for structName := range packages {
|
||||||
|
sortedStructNames.Add(structName)
|
||||||
|
}
|
||||||
|
sortedStructNames.Sort()
|
||||||
|
|
||||||
|
sortedStructNames.Each(func(structName string) {
|
||||||
|
structs := packages[structName]
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||||
|
output.WriteString("\n")
|
||||||
|
|
||||||
|
var sortedMethodNames slicer.StringSlicer
|
||||||
|
for methodName := range structs {
|
||||||
|
sortedMethodNames.Add(methodName)
|
||||||
|
}
|
||||||
|
sortedMethodNames.Sort()
|
||||||
|
|
||||||
|
sortedMethodNames.Each(func(methodName string) {
|
||||||
|
methodDetails := structs[methodName]
|
||||||
|
output.WriteString(" /**\n")
|
||||||
|
output.WriteString(" * " + methodName + "\n")
|
||||||
|
var args slicer.StringSlicer
|
||||||
|
for count, input := range methodDetails.Inputs {
|
||||||
|
arg := fmt.Sprintf("arg%d", count+1)
|
||||||
|
args.Add(arg)
|
||||||
|
output.WriteString(fmt.Sprintf(" * @param {%s} %s - Go Type: %s\n", goTypeToJSDocType(input.TypeName), arg, input.TypeName))
|
||||||
|
}
|
||||||
|
returnType := "Promise"
|
||||||
|
returnTypeDetails := ""
|
||||||
|
if methodDetails.OutputCount() > 0 {
|
||||||
|
firstType := goTypeToJSDocType(methodDetails.Outputs[0].TypeName)
|
||||||
|
returnType += "<" + firstType
|
||||||
|
if methodDetails.OutputCount() == 2 {
|
||||||
|
secondType := goTypeToJSDocType(methodDetails.Outputs[1].TypeName)
|
||||||
|
returnType += "|" + secondType
|
||||||
|
}
|
||||||
|
returnType += ">"
|
||||||
|
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
||||||
|
}
|
||||||
|
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
||||||
|
output.WriteString(" */\n")
|
||||||
|
argsString := args.Join(", ")
|
||||||
|
output.WriteString(fmt.Sprintf(" \"%s\": (%s) => {", methodName, argsString))
|
||||||
|
output.WriteString("\n")
|
||||||
|
output.WriteString(fmt.Sprintf(" return window.backend.%s.%s.%s(%s);", packageName, structName, methodName, argsString))
|
||||||
|
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")
|
||||||
|
|
||||||
|
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fs.DirExists(dirname) {
|
||||||
|
err := fs.Mkdir(dirname)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packageJsonFile := filepath.Join(dirname, "package.json")
|
||||||
|
if !fs.FileExists(packageJsonFile) {
|
||||||
|
err := os.WriteFile(packageJsonFile, packageJSON, 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename := filepath.Join(dirname, "index.js")
|
||||||
|
err = os.WriteFile(filename, output.Bytes(), 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func goTypeToJSDocType(input string) string {
|
||||||
|
switch true {
|
||||||
|
case input == "string":
|
||||||
|
return "string"
|
||||||
|
case input == "error":
|
||||||
|
return "Error"
|
||||||
|
case
|
||||||
|
strings.HasPrefix(input, "int"),
|
||||||
|
strings.HasPrefix(input, "uint"),
|
||||||
|
strings.HasPrefix(input, "float"):
|
||||||
|
return "number"
|
||||||
|
case input == "bool":
|
||||||
|
return "boolean"
|
||||||
|
case strings.HasPrefix(input, "[]"):
|
||||||
|
arrayType := goTypeToJSDocType(input[2:])
|
||||||
|
return "Array.<" + arrayType + ">"
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,7 +23,7 @@ func isStruct(value interface{}) bool {
|
|||||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
return reflect.ValueOf(value).Kind() == reflect.Struct
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||||
|
|
||||||
// Create result placeholder
|
// Create result placeholder
|
||||||
var result []*BoundMethod
|
var result []*BoundMethod
|
||||||
@@ -56,6 +56,11 @@ func getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
fullMethodName := baseName + "." + methodName
|
fullMethodName := baseName + "." + methodName
|
||||||
method := structValue.MethodByName(methodName)
|
method := structValue.MethodByName(methodName)
|
||||||
|
|
||||||
|
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
||||||
|
if b.exemptions.Contains(methodReflectName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Create new method
|
// Create new method
|
||||||
boundMethod := &BoundMethod{
|
boundMethod := &BoundMethod{
|
||||||
Name: fullMethodName,
|
Name: fullMethodName,
|
||||||
|
|||||||
113
v2/internal/bridge/bridge.go
Normal file
113
v2/internal/bridge/bridge.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bridge struct {
|
||||||
|
upgrader websocket.Upgrader
|
||||||
|
server *http.Server
|
||||||
|
myLogger *logger.Logger
|
||||||
|
|
||||||
|
bindings string
|
||||||
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
sessions map[string]*session
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// Dialog client
|
||||||
|
dialog *messagedispatcher.DispatchClient
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
menumanager *menumanager.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridge(myLogger *logger.Logger) *Bridge {
|
||||||
|
result := &Bridge{
|
||||||
|
myLogger: myLogger,
|
||||||
|
upgrader: websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }},
|
||||||
|
sessions: make(map[string]*session),
|
||||||
|
}
|
||||||
|
|
||||||
|
myLogger.SetLogLevel(1)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
result.ctx = ctx
|
||||||
|
result.cancel = cancel
|
||||||
|
result.server = &http.Server{Addr: ":34115"}
|
||||||
|
http.HandleFunc("/bridge", result.wsBridgeHandler)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, menumanager *menumanager.Manager, bindings string, debug bool) error {
|
||||||
|
|
||||||
|
// Ensure we cancel the context when we shutdown
|
||||||
|
defer b.cancel()
|
||||||
|
|
||||||
|
b.bindings = bindings
|
||||||
|
b.dispatcher = dispatcher
|
||||||
|
b.menumanager = menumanager
|
||||||
|
|
||||||
|
// Setup dialog handler
|
||||||
|
dialogClient := NewDialogClient(b.myLogger)
|
||||||
|
b.dialog = dispatcher.RegisterClient(dialogClient)
|
||||||
|
dialogClient.dispatcher = b.dialog
|
||||||
|
|
||||||
|
b.myLogger.Info("Bridge mode started.")
|
||||||
|
|
||||||
|
err := b.server.ListenAndServe()
|
||||||
|
if err != nil && err != http.ErrServerClosed {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
c, err := b.upgrader.Upgrade(w, r, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("upgrade:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
b.myLogger.Info("Connection from frontend accepted [%s].", c.RemoteAddr().String())
|
||||||
|
b.startSession(c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bridge) startSession(conn *websocket.Conn) {
|
||||||
|
|
||||||
|
// Create a new session for this connection
|
||||||
|
s := newSession(conn, b.menumanager, b.bindings, b.dispatcher, b.myLogger, b.ctx)
|
||||||
|
|
||||||
|
// Setup the close handler
|
||||||
|
conn.SetCloseHandler(func(int, string) error {
|
||||||
|
b.myLogger.Info("Connection dropped [%s].", s.Identifier())
|
||||||
|
b.dispatcher.RemoveClient(s.client)
|
||||||
|
b.mu.Lock()
|
||||||
|
delete(b.sessions, s.Identifier())
|
||||||
|
b.mu.Unlock()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
b.mu.Lock()
|
||||||
|
go s.start(len(b.sessions) == 0)
|
||||||
|
b.sessions[s.Identifier()] = s
|
||||||
|
b.mu.Unlock()
|
||||||
|
}
|
||||||
130
v2/internal/bridge/client.go
Normal file
130
v2/internal/bridge/client.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BridgeClient struct {
|
||||||
|
session *session
|
||||||
|
|
||||||
|
// Tray menu cache to send to reconnecting clients
|
||||||
|
messageCache chan string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridgeClient() *BridgeClient {
|
||||||
|
return &BridgeClient{
|
||||||
|
messageCache: make(chan string, 100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) Quit() {
|
||||||
|
b.session.log.Info("Quit unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) NotifyEvent(message string) {
|
||||||
|
//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) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||||
|
// Handled by dialog_client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||||
|
// Handled by dialog_client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
||||||
|
// Handled by dialog_client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetTitle(title string) {
|
||||||
|
b.session.log.Info("WindowSetTitle unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowShow() {
|
||||||
|
b.session.log.Info("WindowShow unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowHide() {
|
||||||
|
b.session.log.Info("WindowHide unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowCenter() {
|
||||||
|
b.session.log.Info("WindowCenter unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowMaximise() {
|
||||||
|
b.session.log.Info("WindowMaximise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnmaximise() {
|
||||||
|
b.session.log.Info("WindowUnmaximise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowMinimise() {
|
||||||
|
b.session.log.Info("WindowMinimise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnminimise() {
|
||||||
|
b.session.log.Info("WindowUnminimise unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowPosition(x int, y int) {
|
||||||
|
b.session.log.Info("WindowPosition unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetMinSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSetMinSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetMaxSize(width int, height int) {
|
||||||
|
b.session.log.Info("WindowSetMaxSize unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowFullscreen() {
|
||||||
|
b.session.log.Info("WindowFullscreen unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowUnFullscreen() {
|
||||||
|
b.session.log.Info("WindowUnFullscreen unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) WindowSetColour(colour int) {
|
||||||
|
b.session.log.Info("WindowSetColour unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) DarkModeEnabled(callbackID string) {
|
||||||
|
b.session.log.Info("DarkModeEnabled unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SetApplicationMenu(menuJSON string) {
|
||||||
|
b.session.log.Info("SetApplicationMenu unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) SetTrayMenu(trayMenuJSON string) {
|
||||||
|
b.session.sendMessage("TS" + trayMenuJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
||||||
|
b.session.sendMessage("TU" + trayMenuJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) UpdateContextMenu(contextMenuJSON string) {
|
||||||
|
b.session.log.Info("UpdateContextMenu unsupported in Bridge mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBridgeClient(session *session) *BridgeClient {
|
||||||
|
return &BridgeClient{
|
||||||
|
session: session,
|
||||||
|
}
|
||||||
|
}
|
||||||
1
v2/internal/bridge/darwin.js
Normal file
1
v2/internal/bridge/darwin.js
Normal file
File diff suppressed because one or more lines are too long
141
v2/internal/bridge/dialog_client.go
Normal file
141
v2/internal/bridge/dialog_client.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DialogClient struct {
|
||||||
|
dispatcher *messagedispatcher.DispatchClient
|
||||||
|
log *logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDialogClient(log *logger.Logger) *DialogClient {
|
||||||
|
return &DialogClient{
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) Quit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) NotifyEvent(message string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) CallResult(message string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
||||||
|
|
||||||
|
osa, err := exec.LookPath("osascript")
|
||||||
|
if err != nil {
|
||||||
|
d.log.Info("MessageDialog unavailable (osascript not found)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var btns slicer.StringSlicer
|
||||||
|
defaultButton := ""
|
||||||
|
cancelButton := ""
|
||||||
|
for index, btn := range dialogOptions.Buttons {
|
||||||
|
btns.Add(strconv.Quote(btn))
|
||||||
|
if btn == dialogOptions.DefaultButton {
|
||||||
|
defaultButton = fmt.Sprintf("default button %d", index+1)
|
||||||
|
}
|
||||||
|
if btn == dialogOptions.CancelButton {
|
||||||
|
cancelButton = fmt.Sprintf("cancel button %d", index+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttons := "{" + btns.Join(",") + "}"
|
||||||
|
script := fmt.Sprintf("display dialog \"%s\" buttons %s %s %s with title \"%s\"", dialogOptions.Message, buttons, defaultButton, cancelButton, dialogOptions.Title)
|
||||||
|
go func() {
|
||||||
|
out, err := exec.Command(osa, "-e", script).Output()
|
||||||
|
if err != nil {
|
||||||
|
// Assume user has pressed cancel button
|
||||||
|
if dialogOptions.CancelButton != "" {
|
||||||
|
d.dispatcher.DispatchMessage("DM" + callbackID + "|" + dialogOptions.CancelButton)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.log.Error("Dialog had bad exit code. If this was a Cancel button, add 'CancelButton' to the dialog.MessageDialog struct. Error: %s", err.Error())
|
||||||
|
d.dispatcher.DispatchMessage("DM" + callbackID + "|error - check logs")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonPressed := strings.TrimSpace(strings.TrimPrefix(string(out), "button returned:"))
|
||||||
|
d.dispatcher.DispatchMessage("DM" + callbackID + "|" + buttonPressed)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowSetTitle(title string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowShow() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowHide() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowCenter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowMaximise() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowUnmaximise() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowMinimise() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowUnminimise() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowPosition(x int, y int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowSize(width int, height int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowSetMinSize(width int, height int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowSetMaxSize(width int, height int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowFullscreen() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowUnFullscreen() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) WindowSetColour(colour int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) DarkModeEnabled(callbackID string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) SetApplicationMenu(menuJSON string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) SetTrayMenu(trayMenuJSON string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) UpdateContextMenu(contextMenuJSON string) {
|
||||||
|
}
|
||||||
162
v2/internal/bridge/session.go
Normal file
162
v2/internal/bridge/session.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
|
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed darwin.js
|
||||||
|
var darwinRuntime string
|
||||||
|
|
||||||
|
// session represents a single websocket session
|
||||||
|
type session struct {
|
||||||
|
bindings string
|
||||||
|
conn *websocket.Conn
|
||||||
|
//eventManager interfaces.EventManager
|
||||||
|
log *logger.Logger
|
||||||
|
//ipc interfaces.IPCManager
|
||||||
|
|
||||||
|
// Mutex for writing to the socket
|
||||||
|
shutdown chan bool
|
||||||
|
writeChan chan []byte
|
||||||
|
|
||||||
|
done bool
|
||||||
|
|
||||||
|
// context
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// client
|
||||||
|
client *messagedispatcher.DispatchClient
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
menumanager *menumanager.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSession(conn *websocket.Conn, menumanager *menumanager.Manager, bindings string, dispatcher *messagedispatcher.Dispatcher, logger *logger.Logger, ctx context.Context) *session {
|
||||||
|
result := &session{
|
||||||
|
conn: conn,
|
||||||
|
bindings: bindings,
|
||||||
|
log: logger,
|
||||||
|
shutdown: make(chan bool),
|
||||||
|
writeChan: make(chan []byte, 100),
|
||||||
|
ctx: ctx,
|
||||||
|
menumanager: menumanager,
|
||||||
|
}
|
||||||
|
|
||||||
|
result.client = dispatcher.RegisterClient(newBridgeClient(result))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier returns a string identifier for the remote connection.
|
||||||
|
// Taking the form of the client's <ip address>:<port>.
|
||||||
|
func (s *session) Identifier() string {
|
||||||
|
if s.conn != nil {
|
||||||
|
return s.conn.RemoteAddr().String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) sendMessage(msg string) error {
|
||||||
|
if !s.done {
|
||||||
|
s.writeChan <- []byte(msg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *session) start(firstSession bool) {
|
||||||
|
s.log.SetLogLevel(1)
|
||||||
|
s.log.Info("Connected to frontend.")
|
||||||
|
go s.writePump()
|
||||||
|
|
||||||
|
var wailsRuntime string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
wailsRuntime = darwinRuntime
|
||||||
|
default:
|
||||||
|
log.Fatal("platform not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingsMessage := "window.wailsbindings = `" + s.bindings + "`;"
|
||||||
|
s.log.Info(bindingsMessage)
|
||||||
|
bootstrapMessage := bindingsMessage + wailsRuntime
|
||||||
|
|
||||||
|
s.sendMessage("b" + bootstrapMessage)
|
||||||
|
|
||||||
|
// Send menus
|
||||||
|
traymenus, err := s.menumanager.GetTrayMenus()
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, trayMenu := range traymenus {
|
||||||
|
s.sendMessage("TS" + trayMenu)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
messageType, buffer, err := s.conn.ReadMessage()
|
||||||
|
if messageType == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.log.Error("Error reading message: %v", err)
|
||||||
|
err = s.conn.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
message := string(buffer)
|
||||||
|
|
||||||
|
s.log.Debug("Got message: %#v\n", message)
|
||||||
|
|
||||||
|
// Dispatch message as normal
|
||||||
|
s.client.DispatchMessage(message)
|
||||||
|
|
||||||
|
if s.done {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown
|
||||||
|
func (s *session) Shutdown() {
|
||||||
|
s.conn.Close()
|
||||||
|
s.done = true
|
||||||
|
s.log.Info("session %v exit", s.Identifier())
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePump pulls messages from the writeChan and sends them to the client
|
||||||
|
// since it uses a channel to read the messages the socket is protected without locks
|
||||||
|
func (s *session) writePump() {
|
||||||
|
s.log.Debug("Session %v - writePump start", s.Identifier())
|
||||||
|
defer s.log.Debug("Session %v - writePump shutdown", s.Identifier())
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
s.Shutdown()
|
||||||
|
return
|
||||||
|
case msg, ok := <-s.writeChan:
|
||||||
|
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
||||||
|
if !ok {
|
||||||
|
s.log.Debug("writeChan was closed!")
|
||||||
|
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
|
||||||
|
s.log.Debug(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
v2/internal/colour/colour.go
Normal file
89
v2/internal/colour/colour.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package colour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wzshiming/ctc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Col(col ctc.Color, text string) string {
|
||||||
|
return fmt.Sprintf("%s%s%s", col, text, ctc.Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Yellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Red(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Blue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Green(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Cyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Magenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func White(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Black(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkYellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkRed(text string) string {
|
||||||
|
return Col(ctc.ForegroundRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkGreen(text string) string {
|
||||||
|
return Col(ctc.ForegroundGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkCyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkMagenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkWhite(text string) string {
|
||||||
|
return Col(ctc.ForegroundWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlack(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rainbowCols = []func(string) string{Red, Yellow, Green, Cyan, Blue, Magenta}
|
||||||
|
|
||||||
|
func Rainbow(text string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
fn := rainbowCols[i%len(rainbowCols)]
|
||||||
|
builder.WriteString(fn(text[i : i+1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
21
v2/internal/deepcopy/LICENSE
Normal file
21
v2/internal/deepcopy/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Joel
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
125
v2/internal/deepcopy/deepcopy.go
Normal file
125
v2/internal/deepcopy/deepcopy.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// deepcopy makes deep copies of things. A standard copy will copy the
|
||||||
|
// pointers: deep copy copies the values pointed to. Unexported field
|
||||||
|
// values are not copied.
|
||||||
|
//
|
||||||
|
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
|
||||||
|
// License: MIT, for more details check the included LICENSE file.
|
||||||
|
package deepcopy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for delegating copy process to type
|
||||||
|
type Interface interface {
|
||||||
|
DeepCopy() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
|
||||||
|
func Iface(iface interface{}) interface{} {
|
||||||
|
return Copy(iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy creates a deep copy of whatever is passed to it and returns the copy
|
||||||
|
// in an interface{}. The returned value will need to be asserted to the
|
||||||
|
// correct type.
|
||||||
|
func Copy(src interface{}) interface{} {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the interface a reflect.Value
|
||||||
|
original := reflect.ValueOf(src)
|
||||||
|
|
||||||
|
// Make a copy of the same type as the original.
|
||||||
|
cpy := reflect.New(original.Type()).Elem()
|
||||||
|
|
||||||
|
// Recursively copy the original.
|
||||||
|
copyRecursive(original, cpy)
|
||||||
|
|
||||||
|
// Return the copy as an interface.
|
||||||
|
return cpy.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyRecursive does the actual copying of the interface. It currently has
|
||||||
|
// limited support for what it can handle. Add as needed.
|
||||||
|
func copyRecursive(original, cpy reflect.Value) {
|
||||||
|
// check for implement deepcopy.Interface
|
||||||
|
if original.CanInterface() {
|
||||||
|
if copier, ok := original.Interface().(Interface); ok {
|
||||||
|
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle according to original's Kind
|
||||||
|
switch original.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
// Get the actual value being pointed to.
|
||||||
|
originalValue := original.Elem()
|
||||||
|
|
||||||
|
// if it isn't valid, return.
|
||||||
|
if !originalValue.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cpy.Set(reflect.New(originalValue.Type()))
|
||||||
|
copyRecursive(originalValue, cpy.Elem())
|
||||||
|
|
||||||
|
case reflect.Interface:
|
||||||
|
// If this is a nil, don't do anything
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Get the value for the interface, not the pointer.
|
||||||
|
originalValue := original.Elem()
|
||||||
|
|
||||||
|
// Get the value by calling Elem().
|
||||||
|
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||||
|
copyRecursive(originalValue, copyValue)
|
||||||
|
cpy.Set(copyValue)
|
||||||
|
|
||||||
|
case reflect.Struct:
|
||||||
|
t, ok := original.Interface().(time.Time)
|
||||||
|
if ok {
|
||||||
|
cpy.Set(reflect.ValueOf(t))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Go through each field of the struct and copy it.
|
||||||
|
for i := 0; i < original.NumField(); i++ {
|
||||||
|
// The Type's StructField for a given field is checked to see if StructField.PkgPath
|
||||||
|
// is set to determine if the field is exported or not because CanSet() returns false
|
||||||
|
// for settable fields. I'm not sure why. -mohae
|
||||||
|
if original.Type().Field(i).PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
copyRecursive(original.Field(i), cpy.Field(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Make a new slice and copy each element.
|
||||||
|
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
|
||||||
|
for i := 0; i < original.Len(); i++ {
|
||||||
|
copyRecursive(original.Index(i), cpy.Index(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
case reflect.Map:
|
||||||
|
if original.IsNil() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cpy.Set(reflect.MakeMap(original.Type()))
|
||||||
|
for _, key := range original.MapKeys() {
|
||||||
|
originalValue := original.MapIndex(key)
|
||||||
|
copyValue := reflect.New(originalValue.Type()).Elem()
|
||||||
|
copyRecursive(originalValue, copyValue)
|
||||||
|
copyKey := Copy(key.Interface())
|
||||||
|
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
cpy.Set(original)
|
||||||
|
}
|
||||||
|
}
|
||||||
1110
v2/internal/deepcopy/deepcopy_test.go
Normal file
1110
v2/internal/deepcopy/deepcopy_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
|||||||
package ffenestri
|
package ffenestri
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
@@ -118,7 +119,8 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
startHidden := a.bool2Cint(a.config.StartHidden)
|
||||||
logLevel := C.int(a.config.LogLevel)
|
logLevel := C.int(a.config.LogLevel)
|
||||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel)
|
hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose)
|
||||||
|
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose)
|
||||||
|
|
||||||
// Save app reference
|
// Save app reference
|
||||||
a.app = (*C.struct_Application)(app)
|
a.app = (*C.struct_Application)(app)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
struct Application;
|
struct Application;
|
||||||
|
|
||||||
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel);
|
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose);
|
||||||
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
||||||
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
||||||
extern void Run(struct Application*, int argc, char **argv);
|
extern void Run(struct Application*, int argc, char **argv);
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ package ffenestri
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -113,6 +114,14 @@ func (c *Client) WindowSize(width int, height int) {
|
|||||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
C.SetSize(c.app.app, C.int(width), C.int(height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) WindowSetMinSize(width int, height int) {
|
||||||
|
C.SetMinWindowSize(c.app.app, C.int(width), C.int(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) WindowSetMaxSize(width int, height int) {
|
||||||
|
C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height))
|
||||||
|
}
|
||||||
|
|
||||||
// WindowSetColour sets the window colour
|
// WindowSetColour sets the window colour
|
||||||
func (c *Client) WindowSetColour(colour int) {
|
func (c *Client) WindowSetColour(colour int) {
|
||||||
r, g, b, a := intToColour(colour)
|
r, g, b, a := intToColour(colour)
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ BOOL yes(id self, SEL cmd)
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no command simply returns NO!
|
||||||
|
BOOL no(id self, SEL cmd)
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
// Prints a hashmap entry
|
// Prints a hashmap entry
|
||||||
int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
||||||
printf("%s: %p ", (char*)e->key, e->data);
|
printf("%s: %p ", (char*)e->key, e->data);
|
||||||
@@ -65,6 +71,7 @@ struct Application {
|
|||||||
// Cocoa data
|
// Cocoa data
|
||||||
id application;
|
id application;
|
||||||
id delegate;
|
id delegate;
|
||||||
|
id windowDelegate;
|
||||||
id mainWindow;
|
id mainWindow;
|
||||||
id wkwebview;
|
id wkwebview;
|
||||||
id manager;
|
id manager;
|
||||||
@@ -92,6 +99,7 @@ struct Application {
|
|||||||
const char *appearance;
|
const char *appearance;
|
||||||
int decorations;
|
int decorations;
|
||||||
int logLevel;
|
int logLevel;
|
||||||
|
int hideWindowOnClose;
|
||||||
|
|
||||||
// Features
|
// Features
|
||||||
int frame;
|
int frame;
|
||||||
@@ -120,6 +128,9 @@ struct Application {
|
|||||||
// Bindings
|
// Bindings
|
||||||
const char *bindings;
|
const char *bindings;
|
||||||
|
|
||||||
|
// shutting down flag
|
||||||
|
bool shuttingDown;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug works like sprintf but mutes if the global debug flag is true
|
// Debug works like sprintf but mutes if the global debug flag is true
|
||||||
@@ -140,6 +151,16 @@ void Debug(struct Application *app, const char *message, ... ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Error(struct Application *app, const char *message, ... ) {
|
||||||
|
const char *temp = concat("LEFfenestri (C) | ", message);
|
||||||
|
va_list args;
|
||||||
|
va_start(args, message);
|
||||||
|
vsnprintf(logbuffer, MAXMESSAGE, temp, args);
|
||||||
|
app->sendMessageToBackend(&logbuffer[0]);
|
||||||
|
MEMFREE(temp);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
void Fatal(struct Application *app, const char *message, ... ) {
|
void Fatal(struct Application *app, const char *message, ... ) {
|
||||||
const char *temp = concat("LFFfenestri (C) | ", message);
|
const char *temp = concat("LFFfenestri (C) | ", message);
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -150,6 +171,12 @@ void Fatal(struct Application *app, const char *message, ... ) {
|
|||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Requires NSString input EG lookupStringConstant(str("NSFontAttributeName"))
|
||||||
|
void* lookupStringConstant(id constantName) {
|
||||||
|
void ** dataPtr = CFBundleGetDataPointerForName(CFBundleGetBundleWithIdentifier((CFStringRef)str("com.apple.AppKit")), (CFStringRef) constantName);
|
||||||
|
return (dataPtr ? *dataPtr : nil);
|
||||||
|
}
|
||||||
|
|
||||||
bool isRetina(struct Application *app) {
|
bool isRetina(struct Application *app) {
|
||||||
CGFloat scale = GET_BACKINGSCALEFACTOR(app->mainWindow);
|
CGFloat scale = GET_BACKINGSCALEFACTOR(app->mainWindow);
|
||||||
if( (int)scale == 1 ) {
|
if( (int)scale == 1 ) {
|
||||||
@@ -206,6 +233,9 @@ void applyWindowColour(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetColour(struct Application *app, int red, int green, int blue, int alpha) {
|
void SetColour(struct Application *app, int red, int green, int blue, int alpha) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->red = red;
|
app->red = red;
|
||||||
app->green = green;
|
app->green = green;
|
||||||
app->blue = blue;
|
app->blue = blue;
|
||||||
@@ -219,12 +249,18 @@ void FullSizeContent(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Hide(struct Application *app) {
|
void Hide(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->application, s("hide:"))
|
msg(app->application, s("hide:"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Show(struct Application *app) {
|
void Show(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->mainWindow, s("makeKeyAndOrderFront:"), NULL);
|
msg(app->mainWindow, s("makeKeyAndOrderFront:"), NULL);
|
||||||
msg(app->application, s("activateIgnoringOtherApps:"), YES);
|
msg(app->application, s("activateIgnoringOtherApps:"), YES);
|
||||||
@@ -240,7 +276,10 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
|||||||
struct Application *app = (struct Application *)objc_getAssociatedObject(
|
struct Application *app = (struct Application *)objc_getAssociatedObject(
|
||||||
self, "application");
|
self, "application");
|
||||||
const char *name = (const char *)msg(msg(message, s("name")), s("UTF8String"));
|
const char *name = (const char *)msg(msg(message, s("name")), s("UTF8String"));
|
||||||
if( strcmp(name, "completed") == 0) {
|
if( strcmp(name, "error") == 0 ) {
|
||||||
|
printf("There was a Javascript error. Please open the devtools for more information.\n");
|
||||||
|
Show(app);
|
||||||
|
} else if( strcmp(name, "completed") == 0) {
|
||||||
// Delete handler
|
// Delete handler
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
||||||
|
|
||||||
@@ -403,6 +442,7 @@ void freeDialogIconCache(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DestroyApplication(struct Application *app) {
|
void DestroyApplication(struct Application *app) {
|
||||||
|
app->shuttingDown = true;
|
||||||
Debug(app, "Destroying Application");
|
Debug(app, "Destroying Application");
|
||||||
|
|
||||||
// Free the bindings
|
// Free the bindings
|
||||||
@@ -444,24 +484,25 @@ void DestroyApplication(struct Application *app) {
|
|||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu"));
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
||||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("external"));
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("external"));
|
||||||
|
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("error"));
|
||||||
|
|
||||||
// Close main window
|
// Close main window
|
||||||
msg(app->mainWindow, s("close"));
|
if( app->windowDelegate != NULL ) {
|
||||||
|
msg(app->windowDelegate, s("release"));
|
||||||
|
msg(app->mainWindow, s("setDelegate:"), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// msg(app->mainWindow, s("close"));
|
||||||
|
|
||||||
|
|
||||||
// Terminate app
|
|
||||||
msg(c("NSApp"), s("terminate:"), NULL);
|
|
||||||
Debug(app, "Finished Destroying Application");
|
Debug(app, "Finished Destroying Application");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit will stop the cocoa application and free up all the memory
|
|
||||||
// used by the application
|
|
||||||
void Quit(struct Application *app) {
|
|
||||||
Debug(app, "Quit Called");
|
|
||||||
DestroyApplication(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTitle sets the main window title to the given string
|
// SetTitle sets the main window title to the given string
|
||||||
void SetTitle(struct Application *app, const char *title) {
|
void SetTitle(struct Application *app, const char *title) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "SetTitle Called");
|
Debug(app, "SetTitle Called");
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->mainWindow, s("setTitle:"), str(title));
|
msg(app->mainWindow, s("setTitle:"), str(title));
|
||||||
@@ -483,6 +524,9 @@ bool isFullScreen(struct Application *app) {
|
|||||||
|
|
||||||
// Fullscreen sets the main window to be fullscreen
|
// Fullscreen sets the main window to be fullscreen
|
||||||
void Fullscreen(struct Application *app) {
|
void Fullscreen(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "Fullscreen Called");
|
Debug(app, "Fullscreen Called");
|
||||||
if( ! isFullScreen(app) ) {
|
if( ! isFullScreen(app) ) {
|
||||||
ToggleFullscreen(app);
|
ToggleFullscreen(app);
|
||||||
@@ -491,6 +535,9 @@ void Fullscreen(struct Application *app) {
|
|||||||
|
|
||||||
// UnFullscreen resets the main window after a fullscreen
|
// UnFullscreen resets the main window after a fullscreen
|
||||||
void UnFullscreen(struct Application *app) {
|
void UnFullscreen(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "UnFullscreen Called");
|
Debug(app, "UnFullscreen Called");
|
||||||
if( isFullScreen(app) ) {
|
if( isFullScreen(app) ) {
|
||||||
ToggleFullscreen(app);
|
ToggleFullscreen(app);
|
||||||
@@ -498,6 +545,9 @@ void UnFullscreen(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Center(struct Application *app) {
|
void Center(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "Center Called");
|
Debug(app, "Center Called");
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("center");
|
MAIN_WINDOW_CALL("center");
|
||||||
@@ -512,23 +562,35 @@ void ToggleMaximise(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Maximise(struct Application *app) {
|
void Maximise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if( app->maximised == 0) {
|
if( app->maximised == 0) {
|
||||||
ToggleMaximise(app);
|
ToggleMaximise(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unmaximise(struct Application *app) {
|
void Unmaximise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if( app->maximised == 1) {
|
if( app->maximised == 1) {
|
||||||
ToggleMaximise(app);
|
ToggleMaximise(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Minimise(struct Application *app) {
|
void Minimise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("miniaturize:");
|
MAIN_WINDOW_CALL("miniaturize:");
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
void Unminimise(struct Application *app) {
|
void Unminimise(struct Application *app) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
MAIN_WINDOW_CALL("deminiaturize:");
|
MAIN_WINDOW_CALL("deminiaturize:");
|
||||||
);
|
);
|
||||||
@@ -552,6 +614,9 @@ void dumpFrame(struct Application *app, const char *message, CGRect frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetSize(struct Application *app, int width, int height) {
|
void SetSize(struct Application *app, int width, int height) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id screen = getCurrentScreen(app);
|
id screen = getCurrentScreen(app);
|
||||||
|
|
||||||
@@ -568,6 +633,9 @@ void SetSize(struct Application *app, int width, int height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetPosition(struct Application *app, int x, int y) {
|
void SetPosition(struct Application *app, int x, int y) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id screen = getCurrentScreen(app);
|
id screen = getCurrentScreen(app);
|
||||||
CGRect screenFrame = GET_FRAME(screen);
|
CGRect screenFrame = GET_FRAME(screen);
|
||||||
@@ -593,6 +661,9 @@ void processDialogButton(id alert, char *buttonTitle, char *cancelButton, char *
|
|||||||
}
|
}
|
||||||
|
|
||||||
extern 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) {
|
extern 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) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
id alert = ALLOC_INIT("NSAlert");
|
id alert = ALLOC_INIT("NSAlert");
|
||||||
char *dialogType = type;
|
char *dialogType = type;
|
||||||
@@ -689,7 +760,7 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
|||||||
buttonPressed = button4;
|
buttonPressed = button4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct callback message. Format "DS<callbackID>|<selected button index>"
|
// Construct callback message. Format "DM<callbackID>|<selected button index>"
|
||||||
const char *callback = concat("DM", callbackID);
|
const char *callback = concat("DM", callbackID);
|
||||||
const char *header = concat(callback, "|");
|
const char *header = concat(callback, "|");
|
||||||
const char *responseMessage = concat(header, buttonPressed);
|
const char *responseMessage = concat(header, buttonPressed);
|
||||||
@@ -706,6 +777,9 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
|||||||
|
|
||||||
// OpenDialog opens a dialog to select files/directories
|
// OpenDialog opens a dialog to select files/directories
|
||||||
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 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) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "OpenDialog Called with callback id: %s", callbackID);
|
Debug(app, "OpenDialog Called with callback id: %s", callbackID);
|
||||||
|
|
||||||
// Create an open panel
|
// Create an open panel
|
||||||
@@ -794,6 +868,9 @@ void OpenDialog(struct Application *app, char *callbackID, char *title, char *fi
|
|||||||
|
|
||||||
// SaveDialog opens a dialog to select files/directories
|
// SaveDialog opens a dialog to select files/directories
|
||||||
void SaveDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
void SaveDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
Debug(app, "SaveDialog Called with callback id: %s", callbackID);
|
Debug(app, "SaveDialog Called with callback id: %s", callbackID);
|
||||||
|
|
||||||
// Create an open panel
|
// Create an open panel
|
||||||
@@ -871,6 +948,9 @@ void DisableFrame(struct Application *app)
|
|||||||
|
|
||||||
void setMinMaxSize(struct Application *app)
|
void setMinMaxSize(struct Application *app)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if (app->maxHeight > 0 && app->maxWidth > 0)
|
if (app->maxHeight > 0 && app->maxWidth > 0)
|
||||||
{
|
{
|
||||||
msg(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight));
|
msg(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight));
|
||||||
@@ -879,10 +959,27 @@ void setMinMaxSize(struct Application *app)
|
|||||||
{
|
{
|
||||||
msg(app->mainWindow, s("setMinSize:"), CGSizeMake(app->minWidth, app->minHeight));
|
msg(app->mainWindow, s("setMinSize:"), CGSizeMake(app->minWidth, app->minHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate if window needs resizing
|
||||||
|
int newWidth = app->width;
|
||||||
|
int newHeight = app->height;
|
||||||
|
|
||||||
|
if (app->maxWidth > 0 && app->width > app->maxWidth) newWidth = app->maxWidth;
|
||||||
|
if (app->minWidth > 0 && app->width < app->minWidth) newWidth = app->minWidth;
|
||||||
|
if (app->maxHeight > 0 && app->height > app->maxHeight ) newHeight = app->maxHeight;
|
||||||
|
if (app->minHeight > 0 && app->height < app->minHeight ) newHeight = app->minHeight;
|
||||||
|
|
||||||
|
// If we have any change, resize window
|
||||||
|
if ( newWidth != app->width || newHeight != app->height ) {
|
||||||
|
SetSize(app, newWidth, newHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->minWidth = minWidth;
|
app->minWidth = minWidth;
|
||||||
app->minHeight = minHeight;
|
app->minHeight = minHeight;
|
||||||
|
|
||||||
@@ -896,6 +993,9 @@ void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
|||||||
|
|
||||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||||
{
|
{
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
app->maxWidth = maxWidth;
|
app->maxWidth = maxWidth;
|
||||||
app->maxHeight = maxHeight;
|
app->maxHeight = maxHeight;
|
||||||
|
|
||||||
@@ -916,24 +1016,40 @@ void SetDebug(void *applicationPointer, int flag) {
|
|||||||
|
|
||||||
// AddContextMenu sets the context menu map for this application
|
// AddContextMenu sets the context menu map for this application
|
||||||
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
||||||
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
|
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||||
|
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
||||||
);
|
);
|
||||||
@@ -1000,6 +1116,9 @@ void createApplication(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
const char *result = isDarkMode(app) ? "T" : "F";
|
const char *result = isDarkMode(app) ? "T" : "F";
|
||||||
|
|
||||||
@@ -1020,9 +1139,9 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
|||||||
void createDelegate(struct Application *app) {
|
void createDelegate(struct Application *app) {
|
||||||
// Define delegate
|
// Define delegate
|
||||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
||||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) yes, "c@:@");
|
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||||
class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
// class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
||||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||||
|
|
||||||
// All Menu Items use a common callback
|
// All Menu Items use a common callback
|
||||||
@@ -1048,6 +1167,11 @@ void createDelegate(struct Application *app) {
|
|||||||
msg(app->application, s("setDelegate:"), delegate);
|
msg(app->application, s("setDelegate:"), delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool windowShouldClose(id self, SEL cmd, id sender) {
|
||||||
|
msg(sender, s("orderBack:"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void createMainWindow(struct Application *app) {
|
void createMainWindow(struct Application *app) {
|
||||||
// Create main window
|
// Create main window
|
||||||
id mainWindow = ALLOC("NSWindow");
|
id mainWindow = ALLOC("NSWindow");
|
||||||
@@ -1066,6 +1190,15 @@ void createMainWindow(struct Application *app) {
|
|||||||
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
||||||
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
||||||
|
|
||||||
|
if( app->hideWindowOnClose ) {
|
||||||
|
// Create window delegate to override windowShouldClose
|
||||||
|
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "WindowDelegate", 0);
|
||||||
|
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSWindowDelegate"));
|
||||||
|
class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldClose, "v@:@");
|
||||||
|
app->windowDelegate = msg((id)delegateClass, s("new"));
|
||||||
|
msg(mainWindow, s("setDelegate:"), app->windowDelegate);
|
||||||
|
}
|
||||||
|
|
||||||
app->mainWindow = mainWindow;
|
app->mainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1152,7 +1285,7 @@ void parseMenuRole(struct Application *app, id parentMenu, JsonNode *item) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "quit")) {
|
if ( STREQ(roleName, "quit")) {
|
||||||
addMenuItem(parentMenu, "Quit (More work TBD)", "terminate:", "q", FALSE);
|
addMenuItem(parentMenu, "Quit", "terminate:", "q", FALSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( STREQ(roleName, "togglefullscreen")) {
|
if ( STREQ(roleName, "togglefullscreen")) {
|
||||||
@@ -1430,6 +1563,9 @@ void updateMenu(struct Application *app, const char *menuAsJSON) {
|
|||||||
|
|
||||||
// SetApplicationMenu sets the initial menu for the application
|
// SetApplicationMenu sets the initial menu for the application
|
||||||
void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
||||||
|
// Guard against calling during shutdown
|
||||||
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
if ( app->applicationMenu == NULL ) {
|
if ( app->applicationMenu == NULL ) {
|
||||||
app->applicationMenu = NewApplicationMenu(menuAsJSON);
|
app->applicationMenu = NewApplicationMenu(menuAsJSON);
|
||||||
return;
|
return;
|
||||||
@@ -1528,6 +1664,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
id manager = msg(config, s("userContentController"));
|
id manager = msg(config, s("userContentController"));
|
||||||
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("external"));
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("external"));
|
||||||
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("completed"));
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("completed"));
|
||||||
|
msg(manager, s("addScriptMessageHandler:name:"), app->delegate, str("error"));
|
||||||
app->manager = manager;
|
app->manager = manager;
|
||||||
|
|
||||||
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
||||||
@@ -1670,11 +1807,22 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
Debug(app, "Run called");
|
Debug(app, "Run called");
|
||||||
msg(app->application, s("run"));
|
msg(app->application, s("run"));
|
||||||
|
|
||||||
|
DestroyApplication(app);
|
||||||
|
|
||||||
MEMFREE(internalCode);
|
MEMFREE(internalCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quit will stop the cocoa application and free up all the memory
|
||||||
|
// used by the application
|
||||||
|
void Quit(struct Application *app) {
|
||||||
|
Debug(app, "Quit Called");
|
||||||
|
msg(app->application, s("stop:"), NULL);
|
||||||
|
SetSize(app, 0, 0);
|
||||||
|
Show(app);
|
||||||
|
Hide(app);
|
||||||
|
}
|
||||||
|
|
||||||
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) {
|
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||||
|
|
||||||
// Load the tray icons
|
// Load the tray icons
|
||||||
LoadTrayIcons();
|
LoadTrayIcons();
|
||||||
@@ -1695,6 +1843,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->startHidden = startHidden;
|
result->startHidden = startHidden;
|
||||||
result->decorations = 0;
|
result->decorations = 0;
|
||||||
result->logLevel = logLevel;
|
result->logLevel = logLevel;
|
||||||
|
result->hideWindowOnClose = hideWindowOnClose;
|
||||||
|
|
||||||
result->mainWindow = NULL;
|
result->mainWindow = NULL;
|
||||||
result->mouseEvent = NULL;
|
result->mouseEvent = NULL;
|
||||||
@@ -1723,12 +1872,17 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
// Context Menus
|
// Context Menus
|
||||||
result->contextMenuStore = NewContextMenuStore();
|
result->contextMenuStore = NewContextMenuStore();
|
||||||
|
|
||||||
|
// Window delegate
|
||||||
|
result->windowDelegate = NULL;
|
||||||
|
|
||||||
// Window Appearance
|
// Window Appearance
|
||||||
result->titlebarAppearsTransparent = 0;
|
result->titlebarAppearsTransparent = 0;
|
||||||
result->webviewIsTranparent = 0;
|
result->webviewIsTranparent = 0;
|
||||||
|
|
||||||
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
||||||
|
|
||||||
|
result->shuttingDown = false;
|
||||||
|
|
||||||
return (void*) result;
|
return (void*) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package ffenestri
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
#cgo darwin LDFLAGS: -framework WebKit -framework CoreFoundation -lobjc
|
||||||
|
|
||||||
#include "ffenestri.h"
|
#include "ffenestri.h"
|
||||||
#include "ffenestri_darwin.h"
|
#include "ffenestri_darwin.h"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
||||||
#include <objc/objc-runtime.h>
|
#include <objc/objc-runtime.h>
|
||||||
#include <CoreGraphics/CoreGraphics.h>
|
#include <CoreGraphics/CoreGraphics.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "stdlib.h"
|
#include "stdlib.h"
|
||||||
@@ -20,7 +21,6 @@
|
|||||||
#define strunicode(input) msg(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
#define strunicode(input) msg(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
||||||
#define cstr(input) (const char *)msg(input, s("UTF8String"))
|
#define cstr(input) (const char *)msg(input, s("UTF8String"))
|
||||||
#define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input))
|
#define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||||
|
|
||||||
#define ALLOC(classname) msg(c(classname), s("alloc"))
|
#define ALLOC(classname) msg(c(classname), s("alloc"))
|
||||||
#define ALLOC_INIT(classname) msg(msg(c(classname), s("alloc")), s("init"))
|
#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_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
|
||||||
@@ -110,4 +110,6 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
|||||||
//void SetContextMenus(struct Application* app, const char *);
|
//void SetContextMenus(struct Application* app, const char *);
|
||||||
void AddTrayMenu(struct Application* app, const char *);
|
void AddTrayMenu(struct Application* app, const char *);
|
||||||
|
|
||||||
|
void* lookupStringConstant(id constantName);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "ffenestri_darwin.h"
|
#include "ffenestri_darwin.h"
|
||||||
#include "menu_darwin.h"
|
#include "menu_darwin.h"
|
||||||
#include "contextmenus_darwin.h"
|
#include "contextmenus_darwin.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
// NewMenu creates a new Menu struct, saving the given menu structure as JSON
|
// NewMenu creates a new Menu struct, saving the given menu structure as JSON
|
||||||
Menu* NewMenu(JsonNode *menuData) {
|
Menu* NewMenu(JsonNode *menuData) {
|
||||||
@@ -63,8 +64,6 @@ MenuItemCallbackData* CreateMenuItemCallbackData(Menu *menu, id menuItem, const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void DeleteMenu(Menu *menu) {
|
void DeleteMenu(Menu *menu) {
|
||||||
|
|
||||||
// Free menu item hashmap
|
// Free menu item hashmap
|
||||||
@@ -181,151 +180,151 @@ id processAcceleratorKey(const char *key) {
|
|||||||
return str("");
|
return str("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if( STREQ(key, "Backspace") ) {
|
if( STREQ(key, "backspace") ) {
|
||||||
return strunicode(0x0008);
|
return strunicode(0x0008);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Tab") ) {
|
if( STREQ(key, "tab") ) {
|
||||||
return strunicode(0x0009);
|
return strunicode(0x0009);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Return") ) {
|
if( STREQ(key, "return") ) {
|
||||||
return strunicode(0x000d);
|
return strunicode(0x000d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Escape") ) {
|
if( STREQ(key, "escape") ) {
|
||||||
return strunicode(0x001b);
|
return strunicode(0x001b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Left") ) {
|
if( STREQ(key, "left") ) {
|
||||||
return strunicode(0x001c);
|
return strunicode(0x001c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Right") ) {
|
if( STREQ(key, "right") ) {
|
||||||
return strunicode(0x001d);
|
return strunicode(0x001d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Up") ) {
|
if( STREQ(key, "up") ) {
|
||||||
return strunicode(0x001e);
|
return strunicode(0x001e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Down") ) {
|
if( STREQ(key, "down") ) {
|
||||||
return strunicode(0x001f);
|
return strunicode(0x001f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Space") ) {
|
if( STREQ(key, "space") ) {
|
||||||
return strunicode(0x0020);
|
return strunicode(0x0020);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Delete") ) {
|
if( STREQ(key, "delete") ) {
|
||||||
return strunicode(0x007f);
|
return strunicode(0x007f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Home") ) {
|
if( STREQ(key, "home") ) {
|
||||||
return strunicode(0x2196);
|
return strunicode(0x2196);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "End") ) {
|
if( STREQ(key, "end") ) {
|
||||||
return strunicode(0x2198);
|
return strunicode(0x2198);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Up") ) {
|
if( STREQ(key, "page up") ) {
|
||||||
return strunicode(0x21de);
|
return strunicode(0x21de);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Down") ) {
|
if( STREQ(key, "page down") ) {
|
||||||
return strunicode(0x21df);
|
return strunicode(0x21df);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F1") ) {
|
if( STREQ(key, "f1") ) {
|
||||||
return strunicode(0xf704);
|
return strunicode(0xf704);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F2") ) {
|
if( STREQ(key, "f2") ) {
|
||||||
return strunicode(0xf705);
|
return strunicode(0xf705);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F3") ) {
|
if( STREQ(key, "f3") ) {
|
||||||
return strunicode(0xf706);
|
return strunicode(0xf706);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F4") ) {
|
if( STREQ(key, "f4") ) {
|
||||||
return strunicode(0xf707);
|
return strunicode(0xf707);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F5") ) {
|
if( STREQ(key, "f5") ) {
|
||||||
return strunicode(0xf708);
|
return strunicode(0xf708);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F6") ) {
|
if( STREQ(key, "f6") ) {
|
||||||
return strunicode(0xf709);
|
return strunicode(0xf709);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F7") ) {
|
if( STREQ(key, "f7") ) {
|
||||||
return strunicode(0xf70a);
|
return strunicode(0xf70a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F8") ) {
|
if( STREQ(key, "f8") ) {
|
||||||
return strunicode(0xf70b);
|
return strunicode(0xf70b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F9") ) {
|
if( STREQ(key, "f9") ) {
|
||||||
return strunicode(0xf70c);
|
return strunicode(0xf70c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F10") ) {
|
if( STREQ(key, "f10") ) {
|
||||||
return strunicode(0xf70d);
|
return strunicode(0xf70d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F11") ) {
|
if( STREQ(key, "f11") ) {
|
||||||
return strunicode(0xf70e);
|
return strunicode(0xf70e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F12") ) {
|
if( STREQ(key, "f12") ) {
|
||||||
return strunicode(0xf70f);
|
return strunicode(0xf70f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F13") ) {
|
if( STREQ(key, "f13") ) {
|
||||||
return strunicode(0xf710);
|
return strunicode(0xf710);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F14") ) {
|
if( STREQ(key, "f14") ) {
|
||||||
return strunicode(0xf711);
|
return strunicode(0xf711);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F15") ) {
|
if( STREQ(key, "f15") ) {
|
||||||
return strunicode(0xf712);
|
return strunicode(0xf712);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F16") ) {
|
if( STREQ(key, "f16") ) {
|
||||||
return strunicode(0xf713);
|
return strunicode(0xf713);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F17") ) {
|
if( STREQ(key, "f17") ) {
|
||||||
return strunicode(0xf714);
|
return strunicode(0xf714);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F18") ) {
|
if( STREQ(key, "f18") ) {
|
||||||
return strunicode(0xf715);
|
return strunicode(0xf715);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F19") ) {
|
if( STREQ(key, "f19") ) {
|
||||||
return strunicode(0xf716);
|
return strunicode(0xf716);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F20") ) {
|
if( STREQ(key, "f20") ) {
|
||||||
return strunicode(0xf717);
|
return strunicode(0xf717);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F21") ) {
|
if( STREQ(key, "f21") ) {
|
||||||
return strunicode(0xf718);
|
return strunicode(0xf718);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F22") ) {
|
if( STREQ(key, "f22") ) {
|
||||||
return strunicode(0xf719);
|
return strunicode(0xf719);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F23") ) {
|
if( STREQ(key, "f23") ) {
|
||||||
return strunicode(0xf71a);
|
return strunicode(0xf71a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F24") ) {
|
if( STREQ(key, "f24") ) {
|
||||||
return strunicode(0xf71b);
|
return strunicode(0xf71b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F25") ) {
|
if( STREQ(key, "f25") ) {
|
||||||
return strunicode(0xf71c);
|
return strunicode(0xf71c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F26") ) {
|
if( STREQ(key, "f26") ) {
|
||||||
return strunicode(0xf71d);
|
return strunicode(0xf71d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F27") ) {
|
if( STREQ(key, "f27") ) {
|
||||||
return strunicode(0xf71e);
|
return strunicode(0xf71e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F28") ) {
|
if( STREQ(key, "f28") ) {
|
||||||
return strunicode(0xf71f);
|
return strunicode(0xf71f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F29") ) {
|
if( STREQ(key, "f29") ) {
|
||||||
return strunicode(0xf720);
|
return strunicode(0xf720);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F30") ) {
|
if( STREQ(key, "f30") ) {
|
||||||
return strunicode(0xf721);
|
return strunicode(0xf721);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F31") ) {
|
if( STREQ(key, "f31") ) {
|
||||||
return strunicode(0xf722);
|
return strunicode(0xf722);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F32") ) {
|
if( STREQ(key, "f32") ) {
|
||||||
return strunicode(0xf723);
|
return strunicode(0xf723);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F33") ) {
|
if( STREQ(key, "f33") ) {
|
||||||
return strunicode(0xf724);
|
return strunicode(0xf724);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F34") ) {
|
if( STREQ(key, "f34") ) {
|
||||||
return strunicode(0xf725);
|
return strunicode(0xf725);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F35") ) {
|
if( STREQ(key, "f35") ) {
|
||||||
return strunicode(0xf726);
|
return strunicode(0xf726);
|
||||||
}
|
}
|
||||||
// if( STREQ(key, "Insert") ) {
|
// if( STREQ(key, "Insert") ) {
|
||||||
@@ -337,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
|||||||
// if( STREQ(key, "ScrollLock") ) {
|
// if( STREQ(key, "ScrollLock") ) {
|
||||||
// return strunicode(0xf72f);
|
// return strunicode(0xf72f);
|
||||||
// }
|
// }
|
||||||
if( STREQ(key, "NumLock") ) {
|
if( STREQ(key, "numLock") ) {
|
||||||
return strunicode(0xf739);
|
return strunicode(0xf739);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,19 +509,19 @@ unsigned long parseModifiers(const char **modifiers) {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
while( thisModifier != NULL ) {
|
while( thisModifier != NULL ) {
|
||||||
// Determine flags
|
// Determine flags
|
||||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
if( STREQ(thisModifier, "optionoralt") ) {
|
||||||
result |= NSEventModifierFlagOption;
|
result |= NSEventModifierFlagOption;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Shift") ) {
|
if( STREQ(thisModifier, "shift") ) {
|
||||||
result |= NSEventModifierFlagShift;
|
result |= NSEventModifierFlagShift;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Super") ) {
|
if( STREQ(thisModifier, "super") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Control") ) {
|
if( STREQ(thisModifier, "control") ) {
|
||||||
result |= NSEventModifierFlagControl;
|
result |= NSEventModifierFlagControl;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
@@ -576,7 +575,7 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers) {
|
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");
|
id item = ALLOC("NSMenuItem");
|
||||||
|
|
||||||
// Create a MenuItemCallbackData
|
// Create a MenuItemCallbackData
|
||||||
@@ -589,6 +588,76 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||||
s("menuItemCallback:"), key);
|
s("menuItemCallback:"), key);
|
||||||
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg(item, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process image
|
||||||
|
if( image != NULL && strlen(image) > 0) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
||||||
|
id nsimage = ALLOC("NSImage");
|
||||||
|
msg(nsimage, s("initWithData:"), imageData);
|
||||||
|
if( templateImage ) {
|
||||||
|
msg(nsimage, s("template"), YES);
|
||||||
|
}
|
||||||
|
msg(item, s("setImage:"), nsimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process Menu Item attributes
|
||||||
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
|
|
||||||
|
// Process font
|
||||||
|
id font;
|
||||||
|
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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) {
|
||||||
|
unsigned short r, g, b, a;
|
||||||
|
|
||||||
|
// white by default
|
||||||
|
r = g = b = a = 255;
|
||||||
|
int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||||
|
if (count > 0) {
|
||||||
|
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(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||||
|
msg(dictionary, s("release"));
|
||||||
|
|
||||||
|
msg(item, s("setAttributedTitle:"), attributedString);
|
||||||
|
msg(attributedString, s("autorelease"));
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg(item, s("setEnabled:"), !disabled);
|
||||||
msg(item, s("autorelease"));
|
msg(item, s("autorelease"));
|
||||||
|
|
||||||
@@ -670,6 +739,16 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
const char *acceleratorkey = NULL;
|
const char *acceleratorkey = NULL;
|
||||||
const char **modifiers = NULL;
|
const char **modifiers = NULL;
|
||||||
|
|
||||||
|
const char *tooltip = getJSONString(item, "Tooltip");
|
||||||
|
const char *image = getJSONString(item, "Image");
|
||||||
|
const char *fontName = getJSONString(item, "FontName");
|
||||||
|
const char *RGBA = getJSONString(item, "RGBA");
|
||||||
|
bool templateImage = false;
|
||||||
|
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||||
|
|
||||||
|
int fontSize = 12;
|
||||||
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
if( accelerator != NULL ) {
|
if( accelerator != NULL ) {
|
||||||
// Get the key
|
// Get the key
|
||||||
@@ -702,7 +781,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
if( type != NULL ) {
|
if( type != NULL ) {
|
||||||
|
|
||||||
if( STREQ(type->string_, "Text")) {
|
if( STREQ(type->string_, "Text")) {
|
||||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers);
|
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
||||||
}
|
}
|
||||||
else if ( STREQ(type->string_, "Separator")) {
|
else if ( STREQ(type->string_, "Separator")) {
|
||||||
addSeparator(parentMenu);
|
addSeparator(parentMenu);
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ static const char *MenuTypeAsString[] = {
|
|||||||
"ApplicationMenu", "ContextMenu", "TrayMenu",
|
"ApplicationMenu", "ContextMenu", "TrayMenu",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct _NSRange {
|
||||||
|
unsigned long location;
|
||||||
|
unsigned long length;
|
||||||
|
} NSRange;
|
||||||
|
|
||||||
|
#define NSFontWeightUltraLight -0.8
|
||||||
|
#define NSFontWeightThin -0.6
|
||||||
|
#define NSFontWeightLight -0.4
|
||||||
|
#define NSFontWeightRegular 0.0
|
||||||
|
#define NSFontWeightMedium 0.23
|
||||||
|
#define NSFontWeightSemibold 0.3
|
||||||
|
#define NSFontWeightBold 0.4
|
||||||
|
#define NSFontWeightHeavy 0.56
|
||||||
|
#define NSFontWeightBlack 0.62
|
||||||
|
|
||||||
extern void messageFromWindowCallback(const char *);
|
extern void messageFromWindowCallback(const char *);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -90,8 +105,7 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
|||||||
|
|
||||||
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers);
|
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 processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -97,14 +97,13 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
|
|
||||||
void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||||
DumpTrayMenu(newMenu);
|
// DumpTrayMenu(newMenu);
|
||||||
|
|
||||||
// Get the current menu
|
// Get the current menu
|
||||||
TrayMenu *currentMenu = GetTrayMenuFromStore(store, newMenu->ID);
|
TrayMenu *currentMenu = GetTrayMenuFromStore(store, newMenu->ID);
|
||||||
|
|
||||||
// If we don't have a menu, we create one
|
// If we don't have a menu, we create one
|
||||||
if ( currentMenu == NULL ) {
|
if ( currentMenu == NULL ) {
|
||||||
printf(" currentMenu = NULL\n");
|
|
||||||
// Store the new menu
|
// Store the new menu
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
|
||||||
@@ -112,7 +111,7 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
ShowTrayMenu(newMenu);
|
ShowTrayMenu(newMenu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DumpTrayMenu(currentMenu);
|
// DumpTrayMenu(currentMenu);
|
||||||
|
|
||||||
// Save the status bar reference
|
// Save the status bar reference
|
||||||
newMenu->statusbaritem = currentMenu->statusbaritem;
|
newMenu->statusbaritem = currentMenu->statusbaritem;
|
||||||
|
|||||||
103
v2/internal/github/github.go
Normal file
103
v2/internal/github/github.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetVersionTags gets the list of tags on the Wails repo
|
||||||
|
// It returns a list of sorted tags in descending order
|
||||||
|
func GetVersionTags() ([]*SemanticVersion, error) {
|
||||||
|
|
||||||
|
result := []*SemanticVersion{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/tags")
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(body, &data)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert tag data to Version structs
|
||||||
|
for _, tag := range data {
|
||||||
|
version := tag["name"].(string)
|
||||||
|
if !strings.HasPrefix(version, "v2") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
semver, err := NewSemanticVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result = append(result, semver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse Sort
|
||||||
|
sort.Sort(sort.Reverse(SemverCollection(result)))
|
||||||
|
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestStableRelease gets the latest stable release on GitHub
|
||||||
|
func GetLatestStableRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no release tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatestPreRelease gets the latest prerelease on GitHub
|
||||||
|
func GetLatestPreRelease() (result *SemanticVersion, err error) {
|
||||||
|
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.IsPreRelease() {
|
||||||
|
return tag, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("no prerelease tag found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValidTag returns true if the given string is a valid tag
|
||||||
|
func IsValidTag(tagVersion string) (bool, error) {
|
||||||
|
if tagVersion[0] == 'v' {
|
||||||
|
tagVersion = tagVersion[1:]
|
||||||
|
}
|
||||||
|
tags, err := GetVersionTags()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tag := range tags {
|
||||||
|
if tag.String() == tagVersion {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
106
v2/internal/github/semver.go
Normal file
106
v2/internal/github/semver.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SemanticVersion is a struct containing a semantic version
|
||||||
|
type SemanticVersion struct {
|
||||||
|
Version *semver.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSemanticVersion creates a new SemanticVersion object with the given version string
|
||||||
|
func NewSemanticVersion(version string) (*SemanticVersion, error) {
|
||||||
|
semverVersion, err := semver.NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &SemanticVersion{
|
||||||
|
Version: semverVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRelease returns true if it's a release version
|
||||||
|
func (s *SemanticVersion) IsRelease() bool {
|
||||||
|
// Limit to v2
|
||||||
|
if s.Version.Major() != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPreRelease returns true if it's a prerelease version
|
||||||
|
func (s *SemanticVersion) IsPreRelease() bool {
|
||||||
|
// Limit to v1
|
||||||
|
if s.Version.Major() != 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s.Version.Prerelease()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SemanticVersion) String() string {
|
||||||
|
return s.Version.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThan returns true if this version is greater than the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint("> " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version
|
||||||
|
func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) {
|
||||||
|
// Set up new constraint
|
||||||
|
constraint, err := semver.NewConstraint(">= " + version.Version.String())
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the desired one is greater than the requested on
|
||||||
|
success, msgs := constraint.Validate(s.Version)
|
||||||
|
if !success {
|
||||||
|
return false, msgs[0]
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MainVersion returns the main version of any version+prerelease+metadata
|
||||||
|
// EG: MainVersion("1.2.3-pre") => "1.2.3"
|
||||||
|
func (s *SemanticVersion) MainVersion() *SemanticVersion {
|
||||||
|
mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch())
|
||||||
|
result, _ := NewSemanticVersion(mainVersion)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemverCollection is a collection of SemanticVersion objects
|
||||||
|
type SemverCollection []*SemanticVersion
|
||||||
|
|
||||||
|
// Len returns the length of a collection. The number of Version instances
|
||||||
|
// on the slice.
|
||||||
|
func (c SemverCollection) Len() int {
|
||||||
|
return len(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less is needed for the sort interface to compare two Version objects on the
|
||||||
|
// slice. If checks if one is less than the other.
|
||||||
|
func (c SemverCollection) Less(i, j int) bool {
|
||||||
|
return c[i].Version.LessThan(c[j].Version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap is needed for the sort interface to replace the Version objects
|
||||||
|
// at two different positions in the slice.
|
||||||
|
func (c SemverCollection) Swap(i, j int) {
|
||||||
|
c[i], c[j] = c[j], c[i]
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package menumanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||||
)
|
)
|
||||||
@@ -9,44 +10,57 @@ import (
|
|||||||
type ProcessedMenuItem struct {
|
type ProcessedMenuItem struct {
|
||||||
ID string
|
ID string
|
||||||
// Label is what appears as the menu text
|
// Label is what appears as the menu text
|
||||||
Label string
|
Label string `json:",omitempty"`
|
||||||
// Role is a predefined menu type
|
// Role is a predefined menu type
|
||||||
Role menu.Role `json:"Role,omitempty"`
|
Role menu.Role `json:",omitempty"`
|
||||||
// Accelerator holds a representation of a key binding
|
// Accelerator holds a representation of a key binding
|
||||||
Accelerator *keys.Accelerator `json:"Accelerator,omitempty"`
|
Accelerator *keys.Accelerator `json:",omitempty"`
|
||||||
// Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu
|
// Type of MenuItem, EG: Checkbox, Text, Separator, Radio, Submenu
|
||||||
Type menu.Type
|
Type menu.Type
|
||||||
// Disabled makes the item unselectable
|
// Disabled makes the item unselectable
|
||||||
Disabled bool
|
Disabled bool `json:",omitempty"`
|
||||||
// Hidden ensures that the item is not shown in the menu
|
// Hidden ensures that the item is not shown in the menu
|
||||||
Hidden bool
|
Hidden bool `json:",omitempty"`
|
||||||
// Checked indicates if the item is selected (used by Checkbox and Radio types only)
|
// Checked indicates if the item is selected (used by Checkbox and Radio types only)
|
||||||
Checked bool
|
Checked bool `json:",omitempty"`
|
||||||
// Submenu contains a list of menu items that will be shown as a submenu
|
// Submenu contains a list of menu items that will be shown as a submenu
|
||||||
//SubMenu []*MenuItem `json:"SubMenu,omitempty"`
|
//SubMenu []*MenuItem `json:"SubMenu,omitempty"`
|
||||||
SubMenu *ProcessedMenu `json:"SubMenu,omitempty"`
|
SubMenu *ProcessedMenu `json:",omitempty"`
|
||||||
|
|
||||||
// Foreground colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red
|
// Colour
|
||||||
Foreground int
|
RGBA string `json:",omitempty"`
|
||||||
|
|
||||||
// Background colour
|
// Font
|
||||||
Background int
|
FontSize int `json:",omitempty"`
|
||||||
|
FontName string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Image - base64 image data
|
||||||
|
Image string `json:",omitempty"`
|
||||||
|
MacTemplateImage bool `json:", omitempty"`
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
Tooltip string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
|
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
|
||||||
|
|
||||||
ID := menuItemMap.menuItemToIDMap[menuItem]
|
ID := menuItemMap.menuItemToIDMap[menuItem]
|
||||||
result := &ProcessedMenuItem{
|
result := &ProcessedMenuItem{
|
||||||
ID: ID,
|
ID: ID,
|
||||||
Label: menuItem.Label,
|
Label: menuItem.Label,
|
||||||
Role: menuItem.Role,
|
Role: menuItem.Role,
|
||||||
Accelerator: menuItem.Accelerator,
|
Accelerator: menuItem.Accelerator,
|
||||||
Type: menuItem.Type,
|
Type: menuItem.Type,
|
||||||
Disabled: menuItem.Disabled,
|
Disabled: menuItem.Disabled,
|
||||||
Hidden: menuItem.Hidden,
|
Hidden: menuItem.Hidden,
|
||||||
Checked: menuItem.Checked,
|
Checked: menuItem.Checked,
|
||||||
Foreground: menuItem.Foreground,
|
SubMenu: nil,
|
||||||
Background: menuItem.Background,
|
RGBA: menuItem.RGBA,
|
||||||
|
FontSize: menuItem.FontSize,
|
||||||
|
FontName: menuItem.FontName,
|
||||||
|
Image: menuItem.Image,
|
||||||
|
MacTemplateImage: menuItem.MacTemplateImage,
|
||||||
|
Tooltip: menuItem.Tooltip,
|
||||||
}
|
}
|
||||||
|
|
||||||
if menuItem.SubMenu != nil {
|
if menuItem.SubMenu != nil {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package messagedispatcher
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
@@ -26,6 +27,8 @@ type Client interface {
|
|||||||
WindowUnminimise()
|
WindowUnminimise()
|
||||||
WindowPosition(x int, y int)
|
WindowPosition(x int, y int)
|
||||||
WindowSize(width int, height int)
|
WindowSize(width int, height int)
|
||||||
|
WindowSetMinSize(width int, height int)
|
||||||
|
WindowSetMaxSize(width int, height int)
|
||||||
WindowFullscreen()
|
WindowFullscreen()
|
||||||
WindowUnFullscreen()
|
WindowUnFullscreen()
|
||||||
WindowSetColour(colour int)
|
WindowSetColour(colour int)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func dialogMessageParser(message string) (*parsedMessage, error) {
|
|||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
return nil, fmt.Errorf("Invalid dialog response message format: %+v", message)
|
return nil, fmt.Errorf("Invalid dialog response message format: %+v", message)
|
||||||
}
|
}
|
||||||
callbackID := message[:idx+1]
|
callbackID := message[:idx]
|
||||||
payloadData := message[idx+1:]
|
payloadData := message[idx+1:]
|
||||||
|
|
||||||
switch dialogType {
|
switch dialogType {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package messagedispatcher
|
package messagedispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
@@ -23,7 +25,6 @@ type Dispatcher struct {
|
|||||||
dialogChannel <-chan *servicebus.Message
|
dialogChannel <-chan *servicebus.Message
|
||||||
systemChannel <-chan *servicebus.Message
|
systemChannel <-chan *servicebus.Message
|
||||||
menuChannel <-chan *servicebus.Message
|
menuChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -31,6 +32,13 @@ type Dispatcher struct {
|
|||||||
// Clients
|
// Clients
|
||||||
clients map[string]*DispatchClient
|
clients map[string]*DispatchClient
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
|
// Context for cancellation
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// internal wait group
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// New dispatcher. Needs a service bus to send to.
|
// New dispatcher. Needs a service bus to send to.
|
||||||
@@ -75,6 +83,9 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create context
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
result := &Dispatcher{
|
result := &Dispatcher{
|
||||||
servicebus: servicebus,
|
servicebus: servicebus,
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
@@ -86,6 +97,8 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
dialogChannel: dialogChannel,
|
dialogChannel: dialogChannel,
|
||||||
systemChannel: systemChannel,
|
systemChannel: systemChannel,
|
||||||
menuChannel: menuChannel,
|
menuChannel: menuChannel,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -96,15 +109,18 @@ func (d *Dispatcher) Start() error {
|
|||||||
|
|
||||||
d.logger.Trace("Starting")
|
d.logger.Trace("Starting")
|
||||||
|
|
||||||
d.running = true
|
d.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for d.running {
|
defer d.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-d.ctx.Done():
|
||||||
|
d.wg.Done()
|
||||||
|
return
|
||||||
case <-d.quitChannel:
|
case <-d.quitChannel:
|
||||||
d.processQuit()
|
d.processQuit()
|
||||||
d.running = false
|
|
||||||
case resultMessage := <-d.resultChannel:
|
case resultMessage := <-d.resultChannel:
|
||||||
d.processCallResult(resultMessage)
|
d.processCallResult(resultMessage)
|
||||||
case eventMessage := <-d.eventChannel:
|
case eventMessage := <-d.eventChannel:
|
||||||
@@ -119,9 +135,6 @@ func (d *Dispatcher) Start() error {
|
|||||||
d.processMenuMessage(menuMessage)
|
d.processMenuMessage(menuMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
d.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -135,10 +148,6 @@ func (d *Dispatcher) processQuit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) shutdown() {
|
|
||||||
d.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClient will register the given callback with the dispatcher
|
// RegisterClient will register the given callback with the dispatcher
|
||||||
// and return a DispatchClient that the caller can use to send messages
|
// and return a DispatchClient that the caller can use to send messages
|
||||||
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
||||||
@@ -349,6 +358,38 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.WindowSize(w, h)
|
client.frontend.WindowSize(w, h)
|
||||||
}
|
}
|
||||||
|
case "minsize":
|
||||||
|
// We need 2 arguments
|
||||||
|
if len(splitTopic) != 4 {
|
||||||
|
d.logger.Error("Invalid number of parameters for 'window:minsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err1 := strconv.Atoi(splitTopic[2])
|
||||||
|
h, err2 := strconv.Atoi(splitTopic[3])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
d.logger.Error("Invalid integer parameters for 'window:minsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Notifh clients
|
||||||
|
for _, client := range d.clients {
|
||||||
|
client.frontend.WindowSetMinSize(w, h)
|
||||||
|
}
|
||||||
|
case "maxsize":
|
||||||
|
// We need 2 arguments
|
||||||
|
if len(splitTopic) != 4 {
|
||||||
|
d.logger.Error("Invalid number of parameters for 'window:maxsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w, err1 := strconv.Atoi(splitTopic[2])
|
||||||
|
h, err2 := strconv.Atoi(splitTopic[3])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
d.logger.Error("Invalid integer parameters for 'window:maxsize' : %#v", result.Data())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Notifh clients
|
||||||
|
for _, client := range d.clients {
|
||||||
|
client.frontend.WindowSetMaxSize(w, h)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown window command: %s", command)
|
d.logger.Error("Unknown window command: %s", command)
|
||||||
}
|
}
|
||||||
@@ -491,3 +532,8 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|||||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
d.logger.Error("Unknown menufrontend command: %s", command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) Close() {
|
||||||
|
d.cancel()
|
||||||
|
d.wg.Wait()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
@@ -16,11 +17,14 @@ type Process struct {
|
|||||||
|
|
||||||
// NewProcess creates a new process struct
|
// NewProcess creates a new process struct
|
||||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||||
return &Process{
|
result := &Process{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
cmd: exec.Command(cmd, args...),
|
cmd: exec.Command(cmd, args...),
|
||||||
exitChannel: make(chan bool, 1),
|
exitChannel: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
|
result.cmd.Stdout = os.Stdout
|
||||||
|
result.cmd.Stderr = os.Stderr
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the process
|
// Start the process
|
||||||
@@ -35,7 +39,12 @@ func (p *Process) Start() error {
|
|||||||
|
|
||||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
||||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
||||||
cmd.Wait()
|
err := cmd.Wait()
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != "signal: killed" {
|
||||||
|
logger.Fatal("Fatal error from app: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||||
*running = false
|
*running = false
|
||||||
exitChannel <- true
|
exitChannel <- true
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,7 +65,7 @@ export function SetBindings(bindingsMap) {
|
|||||||
window.backend[packageName][structName][methodName] = function () {
|
window.backend[packageName][structName][methodName] = function () {
|
||||||
|
|
||||||
// No timeout by default
|
// No timeout by default
|
||||||
var timeout = 0;
|
let timeout = 0;
|
||||||
|
|
||||||
// Actual function
|
// Actual function
|
||||||
function dynamic() {
|
function dynamic() {
|
||||||
@@ -89,19 +89,3 @@ export function SetBindings(bindingsMap) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// /**
|
|
||||||
// * Determines if the given identifier is valid Javascript
|
|
||||||
// *
|
|
||||||
// * @param {boolean} name
|
|
||||||
// * @returns
|
|
||||||
// */
|
|
||||||
// function isValidIdentifier(name) {
|
|
||||||
// // Don't xss yourself :-)
|
|
||||||
// try {
|
|
||||||
// new Function('var ' + name);
|
|
||||||
// return true;
|
|
||||||
// } catch (e) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import { SetBindings } from './bindings';
|
import { SetBindings } from './bindings';
|
||||||
import { Init } from './main';
|
import { Init } from './main';
|
||||||
|
import {RaiseError} from '../desktop/darwin';
|
||||||
|
|
||||||
// Setup global error handler
|
// Setup global error handler
|
||||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||||
@@ -21,7 +22,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
|||||||
error: JSON.stringify(error),
|
error: JSON.stringify(error),
|
||||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
stack: function() { return JSON.stringify(new Error().stack); }(),
|
||||||
};
|
};
|
||||||
window.wails.Log.Error(JSON.stringify(errorMessage));
|
RaiseError(errorMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialise the Runtime
|
// Initialise the Runtime
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Error } from './log';
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
// Defines a single listener with a maximum number of times to callback
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Listener class defines a listener! :-)
|
* The Listener class defines a listener! :-)
|
||||||
*
|
*
|
||||||
@@ -43,7 +44,7 @@ class Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventListeners = {};
|
let eventListeners = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
@@ -96,7 +97,7 @@ export function Once(eventName, callback) {
|
|||||||
function notifyListeners(eventData) {
|
function notifyListeners(eventData) {
|
||||||
|
|
||||||
// Get the event name
|
// Get the event name
|
||||||
var eventName = eventData.name;
|
let eventName = eventData.name;
|
||||||
|
|
||||||
// Check if we have any listeners for this event
|
// Check if we have any listeners for this event
|
||||||
if (eventListeners[eventName]) {
|
if (eventListeners[eventName]) {
|
||||||
@@ -110,7 +111,7 @@ function notifyListeners(eventData) {
|
|||||||
// Get next listener
|
// Get next listener
|
||||||
const listener = eventListeners[eventName][count];
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
var data = eventData.data;
|
let data = eventData.data;
|
||||||
|
|
||||||
// Do the callback
|
// Do the callback
|
||||||
const destroy = listener.Callback(data);
|
const destroy = listener.Callback(data);
|
||||||
@@ -120,7 +121,7 @@ function notifyListeners(eventData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update callbacks with new list of listners
|
// Update callbacks with new list of listeners
|
||||||
eventListeners[eventName] = newEventListenerList;
|
eventListeners[eventName] = newEventListenerList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,4 @@ export function Init() {
|
|||||||
|
|
||||||
// Do platform specific Init
|
// Do platform specific Init
|
||||||
Platform.Init();
|
Platform.Init();
|
||||||
|
|
||||||
window.wailsloader.runtime = true;
|
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,10 @@ export function SendMessage(message) {
|
|||||||
window.webkit.messageHandlers.external.postMessage(message);
|
window.webkit.messageHandlers.external.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RaiseError(message) {
|
||||||
|
window.webkit.messageHandlers.error.postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
export function Init() {
|
export function Init() {
|
||||||
|
|
||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function Init() {
|
|||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||||
window.addEventListener('mousedown', function (e) {
|
window.addEventListener('mousedown', function (e) {
|
||||||
var currentElement = e.target;
|
let currentElement = e.target;
|
||||||
while (currentElement != null) {
|
while (currentElement != null) {
|
||||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
bridge.js
|
src
|
||||||
1721
v2/internal/runtime/js/runtime/bridge.js
Normal file
1721
v2/internal/runtime/js/runtime/bridge.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,25 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import bridge from './bridge';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the Wails runtime
|
* ready will execute the callback when Wails has loaded
|
||||||
|
* and initialised.
|
||||||
*
|
*
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function Init(callback) {
|
function ready(callback) {
|
||||||
window.wails._.Init(callback);
|
|
||||||
|
// If window.wails exists, we are ready
|
||||||
|
if( window.wails ) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not we need to setup the bridge
|
||||||
|
bridge.InitBridge(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Init;
|
module.exports = {
|
||||||
|
ready: ready,
|
||||||
|
};
|
||||||
@@ -23,7 +23,7 @@ module.exports = {
|
|||||||
Browser: Browser,
|
Browser: Browser,
|
||||||
Dialog: Dialog,
|
Dialog: Dialog,
|
||||||
Events: Events,
|
Events: Events,
|
||||||
Init: Init,
|
ready: Init.ready,
|
||||||
Log: Log,
|
Log: Log,
|
||||||
System: System,
|
System: System,
|
||||||
Store: Store,
|
Store: Store,
|
||||||
|
|||||||
2
v2/internal/runtime/js/runtime/package-lock.json
generated
2
v2/internal/runtime/js/runtime/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wails/runtime",
|
||||||
"version": "1.2.13",
|
"version": "1.2.24",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wails/runtime",
|
||||||
"version": "1.2.22",
|
"version": "1.3.10",
|
||||||
"description": "Wails V2 Javascript runtime library",
|
"description": "Wails V2 Javascript runtime library",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"types": "runtime.d.ts",
|
"types": "runtime.d.ts",
|
||||||
|
|||||||
56
v2/internal/runtime/js/runtime/src/Menu.svelte
Normal file
56
v2/internal/runtime/js/runtime/src/Menu.svelte
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export let menu;
|
||||||
|
|
||||||
|
export let hidden = true;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !hidden}
|
||||||
|
<div class="menu">
|
||||||
|
{#if menu.Menu }
|
||||||
|
{#each menu.Menu.Items as menuItem}
|
||||||
|
<div class="menuitem">
|
||||||
|
{#if menuItem.Image }
|
||||||
|
<div><img alt="" src="data:image/png;base64,{menuItem.Image}"/></div>
|
||||||
|
{/if}
|
||||||
|
<div class="menulabel">{menuItem.Label}</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #0008;
|
||||||
|
color: #EEF;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 5px;
|
||||||
|
position: absolute;
|
||||||
|
backdrop-filter: blur(3px) saturate(160%) contrast(45%) brightness(140%);
|
||||||
|
border: 1px solid rgb(88,88,88);
|
||||||
|
box-shadow: 0 0 1px rgb(146,146,148) inset;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuitem {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuitem:hover {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgb(57,131,223);
|
||||||
|
padding: 1px 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menuitem img {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
72
v2/internal/runtime/js/runtime/src/Menubar.svelte
Normal file
72
v2/internal/runtime/js/runtime/src/Menubar.svelte
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
import {menuVisible} from './store'
|
||||||
|
import {fade} from 'svelte/transition';
|
||||||
|
|
||||||
|
import {trays} from './store'
|
||||||
|
import TrayMenu from "./TrayMenu.svelte";
|
||||||
|
import {onMount} from "svelte";
|
||||||
|
|
||||||
|
let time = new Date();
|
||||||
|
$: day = time.toLocaleString("default", { weekday: "short" })
|
||||||
|
$: dom = time.getDate()
|
||||||
|
$: mon = time.toLocaleString("default", { month: "short" })
|
||||||
|
$: currentTime = time.toLocaleString('en-US', { hour: 'numeric', minute: 'numeric', hour12: true }).toLowerCase()
|
||||||
|
$: dateTimeString = `${day} ${dom} ${mon} ${currentTime}`
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
time = new Date();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
// Backtick toggle
|
||||||
|
if( e.keyCode == 192 ) {
|
||||||
|
menuVisible.update( (current) => {
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $menuVisible }
|
||||||
|
<div class="wails-menubar" transition:fade>
|
||||||
|
<span class="tray-menus">
|
||||||
|
{#each $trays as tray}
|
||||||
|
<TrayMenu {tray}/>
|
||||||
|
{/each}
|
||||||
|
<span class="time">{dateTimeString}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window on:keydown={handleKeydown}/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.tray-menus {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.wails-menubar { position: relative;
|
||||||
|
display: block;
|
||||||
|
top: 0;
|
||||||
|
height: 2rem;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px solid #b3b3b3;
|
||||||
|
box-shadow: 0 0 10px 0 #33333360;
|
||||||
|
}
|
||||||
|
.time {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
overflow: visible;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
v2/internal/runtime/js/runtime/src/Overlay.svelte
Normal file
55
v2/internal/runtime/js/runtime/src/Overlay.svelte
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
import { overlayVisible } from './store'
|
||||||
|
import { fade, } from 'svelte/transition';
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if $overlayVisible }
|
||||||
|
<div class="wails-reconnect-overlay" transition:fade="{{ duration: 200 }}">
|
||||||
|
<div class="wails-reconnect-overlay-content">
|
||||||
|
<div class="wails-reconnect-overlay-loadingspinner"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wails-reconnect-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
backdrop-filter: blur(20px) saturate(160%) contrast(45%) brightness(140%);
|
||||||
|
z-index: 999999
|
||||||
|
}
|
||||||
|
|
||||||
|
.wails-reconnect-overlay-content {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
margin: 0;
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAflBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAAAAABAQEEBAQAAAAAAAAEBAQAAAADAwMAAAABAQEAAAAAAAAAAAAAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWCC3waAAAAKXRSTlMALgUMIBk0+xEqJs70Xhb3lu3EjX2EZTlv5eHXvbarQj3cdmpXSqOeUDwaqNAAAAKCSURBVEjHjZTntqsgEIUPVVCwtxg1vfD+L3hHRe8K6snZf+KKn8OewvzsSSeXLruLnz+KHs0gr6DkT3xsRkU6VVn4Ha/UxLe1Z4y64i847sykPBh/AvQ7ry3eFN70oKrfcBJYvm/tQ1qxP4T3emXPeXAkvodPUvtdjbhk+Ft4c0hslTiXVOzxOJ15NWUblQhRsdu3E1AfCjj3Gdm18zSOsiH8Lk4TB480ksy62fiqNo4OpyU8O21l6+hyRtS6z8r1pHlmle5sR1/WXS6Mq2Nl+YeKt3vr+vdH/q4O68tzXuwkiZmngYb4R8Co1jh0+Ww2UTyWxBvtyxLO7QVjO3YOD/lWZpbXDGellFG2Mws58mMnjVZSn7p+XvZ6IF4nn02OJZV0aTO22arp/DgLPtrgpVoi6TPbZm4XQBjY159w02uO0BDdYsfrOEi0M2ulRXlCIPAOuN1NOVhi+riBR3dgwQplYsZRZJLXq23Mlo5njkbY0rZFu3oiNIYG2kqsbVz67OlNuZZIOlfxHDl0UpyRX86z/OYC/3qf1A1xTrMp/PWWM4ePzf8DDp1nesQRpcFk7BlwdzN08ZIALJpCaciQXO0f6k4dnuT/Ewg4l7qSTNzm2SykdHn6GJ12mWc6aCNj/g1cTXpB8YFfr0uVc96aFkkqiIiX4nO+salKwGtIkvfB+Ja8DxMeD3hIXP5mTOYPB4eVT0+32I5ykvPZjesnkGgIREgYnmLrPb0PdV3hoLup2TjcGBPM4mgsfF5BrawZR4/GpzYQzQfrUZCf0TCWYo2DqhdhTJBQ6j4xqmmLN5LjdRIY8LWExiFUsSrza/nmFBqw3I9tEZB9h0lIQSO9if8DkISDAj8CDawAAAAASUVORK5CYII=);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center
|
||||||
|
}
|
||||||
|
|
||||||
|
.wails-reconnect-overlay-loadingspinner {
|
||||||
|
pointer-events: none;
|
||||||
|
width: 2.5em;
|
||||||
|
height: 2.5em;
|
||||||
|
border: .4em solid transparent;
|
||||||
|
border-color: #f00 #eee0 #f00 #eee0;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: loadingspin 1s linear infinite;
|
||||||
|
margin: auto;
|
||||||
|
padding: 2.5em
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loadingspin {
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
65
v2/internal/runtime/js/runtime/src/TrayMenu.svelte
Normal file
65
v2/internal/runtime/js/runtime/src/TrayMenu.svelte
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<script>
|
||||||
|
import Menu from "./Menu.svelte";
|
||||||
|
import { selectedMenu } from "./store";
|
||||||
|
|
||||||
|
export let tray = null;
|
||||||
|
|
||||||
|
$: hidden = $selectedMenu !== tray;
|
||||||
|
|
||||||
|
function closeMenu() {
|
||||||
|
selectedMenu.set(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trayClicked() {
|
||||||
|
if ( $selectedMenu !== tray ) {
|
||||||
|
selectedMenu.set(tray);
|
||||||
|
} else {
|
||||||
|
selectedMenu.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Source: https://svelte.dev/repl/0ace7a508bd843b798ae599940a91783?version=3.16.7
|
||||||
|
/** Dispatch event on click outside of node */
|
||||||
|
function clickOutside(node) {
|
||||||
|
|
||||||
|
const handleClick = event => {
|
||||||
|
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
||||||
|
node.dispatchEvent(
|
||||||
|
new CustomEvent('click_outside', node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', handleClick, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
document.removeEventListener('click', handleClick, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class="tray-menu" use:clickOutside on:click_outside={closeMenu}>
|
||||||
|
<!--{#if tray.Image && tray.Image.length > 0}-->
|
||||||
|
<!-- <img alt="" src="data:image/png;base64,{tray.Image}"/>-->
|
||||||
|
<!--{/if}-->
|
||||||
|
<span class="label" on:click={trayClicked}>{tray.Label}</span>
|
||||||
|
{#if tray.ProcessedMenu }
|
||||||
|
<Menu menu="{tray.ProcessedMenu}" {hidden}/>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.tray-menu {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
overflow: visible;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
9
v2/internal/runtime/js/runtime/src/log.js
Normal file
9
v2/internal/runtime/js/runtime/src/log.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
export function log(message) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log(
|
||||||
|
'%c wails bridge %c ' + message + ' ',
|
||||||
|
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
|
||||||
|
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
|
||||||
|
);
|
||||||
|
}
|
||||||
45
v2/internal/runtime/js/runtime/src/main.js
Normal file
45
v2/internal/runtime/js/runtime/src/main.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import Overlay from './Overlay.svelte';
|
||||||
|
import MenuBar from './Menubar.svelte';
|
||||||
|
import {showOverlay} from "./store";
|
||||||
|
import {StartWebsocket} from "./websocket";
|
||||||
|
|
||||||
|
let components = {};
|
||||||
|
|
||||||
|
function setupMenuBar() {
|
||||||
|
components.menubar = new MenuBar({
|
||||||
|
target: document.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up the overlay
|
||||||
|
function setupOverlay() {
|
||||||
|
components.overlay = new Overlay({
|
||||||
|
target: document.body,
|
||||||
|
anchor: document.querySelector('#wails-bridge'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InitBridge(callback) {
|
||||||
|
|
||||||
|
setupMenuBar()
|
||||||
|
|
||||||
|
// Setup the overlay
|
||||||
|
setupOverlay();
|
||||||
|
|
||||||
|
// Start by showing the overlay...
|
||||||
|
showOverlay();
|
||||||
|
|
||||||
|
// ...and attempt to connect
|
||||||
|
StartWebsocket(callback);
|
||||||
|
}
|
||||||
525
v2/internal/runtime/js/runtime/src/package-lock.json
generated
Normal file
525
v2/internal/runtime/js/runtime/src/package-lock.json
generated
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
{
|
||||||
|
"name": "bridge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/code-frame": {
|
||||||
|
"version": "7.12.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz",
|
||||||
|
"integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/highlight": "^7.12.13"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.12.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
|
||||||
|
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@babel/highlight": {
|
||||||
|
"version": "7.12.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.12.13.tgz",
|
||||||
|
"integrity": "sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-validator-identifier": "^7.12.11",
|
||||||
|
"chalk": "^2.0.0",
|
||||||
|
"js-tokens": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@rollup/plugin-commonjs": {
|
||||||
|
"version": "17.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz",
|
||||||
|
"integrity": "sha512-PoMdXCw0ZyvjpCMT5aV4nkL0QywxP29sODQsSGeDpr/oI49Qq9tRtAsb/LbYbDzFlOydVEqHmmZWFtXJEAX9ew==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@rollup/pluginutils": "^3.1.0",
|
||||||
|
"commondir": "^1.0.1",
|
||||||
|
"estree-walker": "^2.0.1",
|
||||||
|
"glob": "^7.1.6",
|
||||||
|
"is-reference": "^1.2.1",
|
||||||
|
"magic-string": "^0.25.7",
|
||||||
|
"resolve": "^1.17.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@rollup/plugin-node-resolve": {
|
||||||
|
"version": "11.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.1.tgz",
|
||||||
|
"integrity": "sha512-zlBXR4eRS+2m79TsUZWhsd0slrHUYdRx4JF+aVQm+MI0wsKdlpC2vlDVjmlGvtZY1vsefOT9w3JxvmWSBei+Lg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@rollup/pluginutils": "^3.1.0",
|
||||||
|
"@types/resolve": "1.17.1",
|
||||||
|
"builtin-modules": "^3.1.0",
|
||||||
|
"deepmerge": "^4.2.2",
|
||||||
|
"is-module": "^1.0.0",
|
||||||
|
"resolve": "^1.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@rollup/pluginutils": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/estree": "0.0.39",
|
||||||
|
"estree-walker": "^1.0.1",
|
||||||
|
"picomatch": "^2.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/estree": {
|
||||||
|
"version": "0.0.39",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
|
||||||
|
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "14.14.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz",
|
||||||
|
"integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/resolve": {
|
||||||
|
"version": "1.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||||
|
"integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ansi-styles": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"balanced-match": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"brace-expansion": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"balanced-match": "^1.0.0",
|
||||||
|
"concat-map": "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"buffer-from": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"builtin-modules": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "2.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||||
|
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^3.2.1",
|
||||||
|
"escape-string-regexp": "^1.0.5",
|
||||||
|
"supports-color": "^5.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"commondir": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"concat-map": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"deepmerge": {
|
||||||
|
"version": "4.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||||
|
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||||
|
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"estree-walker": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"fs.realpath": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"fsevents": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"function-bind": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "7.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fs.realpath": "^1.0.0",
|
||||||
|
"inflight": "^1.0.4",
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "^3.0.4",
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"path-is-absolute": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"function-bind": "^1.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"inflight": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
|
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.3.0",
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-core-module": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has": "^1.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-module": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"is-reference": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/estree": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jest-worker": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"supports-color": "^7.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"js-tokens": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"magic-string": {
|
||||||
|
"version": "0.25.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
|
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"sourcemap-codec": "^1.4.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"merge-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"brace-expansion": "^1.1.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-is-absolute": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"path-parse": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"picomatch": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"randombytes": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"require-relative": {
|
||||||
|
"version": "0.8.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
|
||||||
|
"integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"resolve": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"is-core-module": "^2.1.0",
|
||||||
|
"path-parse": "^1.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup": {
|
||||||
|
"version": "2.38.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz",
|
||||||
|
"integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fsevents": "~2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup-plugin-svelte": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-svelte/-/rollup-plugin-svelte-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-vopCUq3G+25sKjwF5VilIbiY6KCuMNHP1PFvx2Vr3REBNMDllKHFZN2B9jwwC+MqNc3UPKkjXnceLPEjTjXGXg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"require-relative": "^0.8.7",
|
||||||
|
"rollup-pluginutils": "^2.8.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup-plugin-terser": {
|
||||||
|
"version": "7.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz",
|
||||||
|
"integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.10.4",
|
||||||
|
"jest-worker": "^26.2.1",
|
||||||
|
"serialize-javascript": "^4.0.0",
|
||||||
|
"terser": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rollup-pluginutils": {
|
||||||
|
"version": "2.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
|
||||||
|
"integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"estree-walker": "^0.6.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"estree-walker": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"serialize-javascript": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"source-map-support": {
|
||||||
|
"version": "0.5.19",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||||
|
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sourcemap-codec": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
|
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"svelte": {
|
||||||
|
"version": "3.32.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.32.2.tgz",
|
||||||
|
"integrity": "sha512-Zxh1MQQl/+vnToKbU1Per+PoMN8Jb2MeKJcGxiOsCGR677hXw7jkMfbnNXq33+dxIzV/HfA4xtoSPJrqeB0VUg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"version": "5.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz",
|
||||||
|
"integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map": "~0.7.2",
|
||||||
|
"source-map-support": "~0.5.19"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
v2/internal/runtime/js/runtime/src/package.json
Normal file
21
v2/internal/runtime/js/runtime/src/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "bridge",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Wails bridge",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "npx rollup -c",
|
||||||
|
"watch": "npx rollup -c -w"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^17.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||||
|
"rollup": "^2.38.5",
|
||||||
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"svelte": "^3.32.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
37
v2/internal/runtime/js/runtime/src/rollup.config.js
Normal file
37
v2/internal/runtime/js/runtime/src/rollup.config.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import resolve from '@rollup/plugin-node-resolve';
|
||||||
|
// import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import svelte from 'rollup-plugin-svelte';
|
||||||
|
import { terser } from "rollup-plugin-terser";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// browser-friendly UMD build
|
||||||
|
{
|
||||||
|
input: 'main.js',
|
||||||
|
output: {
|
||||||
|
name: 'bridge',
|
||||||
|
file: '../bridge.js',
|
||||||
|
format: 'umd',
|
||||||
|
exports: "named"
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
svelte({
|
||||||
|
// Optionally, preprocess components with svelte.preprocess:
|
||||||
|
// https://svelte.dev/docs#svelte_preprocess
|
||||||
|
// preprocess: {
|
||||||
|
// style: ({content}) => {
|
||||||
|
// return transformStyles(content);
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
|
||||||
|
// Emit CSS as "files" for other plugins to process. default is true
|
||||||
|
emitCss: false,
|
||||||
|
|
||||||
|
}),
|
||||||
|
resolve({browser: true}), // so Rollup can find `ms`
|
||||||
|
// commonjs() // so Rollup can convert `ms` to an ES module
|
||||||
|
// terser(),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
];
|
||||||
52
v2/internal/runtime/js/runtime/src/store.js
Normal file
52
v2/internal/runtime/js/runtime/src/store.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import {log} from "./log";
|
||||||
|
|
||||||
|
/** Overlay */
|
||||||
|
export const overlayVisible = writable(false);
|
||||||
|
|
||||||
|
export function showOverlay() {
|
||||||
|
overlayVisible.set(true);
|
||||||
|
}
|
||||||
|
export function hideOverlay() {
|
||||||
|
overlayVisible.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Menubar **/
|
||||||
|
export const menuVisible = writable(false);
|
||||||
|
|
||||||
|
export function showMenuBar() {
|
||||||
|
menuVisible.set(true);
|
||||||
|
}
|
||||||
|
export function hideMenuBar() {
|
||||||
|
menuVisible.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Trays **/
|
||||||
|
|
||||||
|
export const trays = writable([]);
|
||||||
|
export function setTray(tray) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === tray.ID);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
current.push(tray);
|
||||||
|
} else {
|
||||||
|
current[index] = tray;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function updateTrayLabel(tray) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === tray.ID);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
return log("ERROR: Attempted to update tray index ", tray.ID, "but it doesn't exist")
|
||||||
|
}
|
||||||
|
current[index].Label = tray.Label;
|
||||||
|
return current;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export let selectedMenu = writable(null);
|
||||||
165
v2/internal/runtime/js/runtime/src/websocket.js
Normal file
165
v2/internal/runtime/js/runtime/src/websocket.js
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
|
import {setTray, hideOverlay, showOverlay, updateTrayLabel} from "./store";
|
||||||
|
import {log} from "./log";
|
||||||
|
|
||||||
|
let websocket = null;
|
||||||
|
let callback = null;
|
||||||
|
let connectTimer;
|
||||||
|
|
||||||
|
export function StartWebsocket(userCallback) {
|
||||||
|
|
||||||
|
callback = userCallback;
|
||||||
|
|
||||||
|
window.onbeforeunload = function() {
|
||||||
|
if( websocket ) {
|
||||||
|
websocket.onclose = function () { };
|
||||||
|
websocket.close();
|
||||||
|
websocket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and attempt to connect
|
||||||
|
connect();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupIPCBridge() {
|
||||||
|
// darwin
|
||||||
|
window.webkit = {
|
||||||
|
messageHandlers: {
|
||||||
|
external: {
|
||||||
|
postMessage: (message) => {
|
||||||
|
websocket.send(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
windowDrag: {
|
||||||
|
postMessage: () => {
|
||||||
|
// Ignore window drag events
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles incoming websocket connections
|
||||||
|
function handleConnect() {
|
||||||
|
log('Connected to backend');
|
||||||
|
setupIPCBridge();
|
||||||
|
hideOverlay();
|
||||||
|
clearInterval(connectTimer);
|
||||||
|
websocket.onclose = handleDisconnect;
|
||||||
|
websocket.onmessage = handleMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles websocket disconnects
|
||||||
|
function handleDisconnect() {
|
||||||
|
log('Disconnected from backend');
|
||||||
|
websocket = null;
|
||||||
|
showOverlay();
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to the backend every 1s (default value).
|
||||||
|
function connect() {
|
||||||
|
connectTimer = setInterval(function () {
|
||||||
|
if (websocket == null) {
|
||||||
|
websocket = new WebSocket('ws://' + window.location.hostname + ':34115/bridge');
|
||||||
|
websocket.onopen = handleConnect;
|
||||||
|
websocket.onerror = function (e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
websocket = null;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a script to the Dom.
|
||||||
|
// Removes it if second parameter is true.
|
||||||
|
function addScript(script, remove) {
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.setAttribute('type', 'text/javascript');
|
||||||
|
s.textContent = script;
|
||||||
|
document.head.appendChild(s);
|
||||||
|
|
||||||
|
// Remove internal messages from the DOM
|
||||||
|
if (remove) {
|
||||||
|
s.parentNode.removeChild(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMessage(message) {
|
||||||
|
// As a bridge we ignore js and css injections
|
||||||
|
switch (message.data[0]) {
|
||||||
|
// Wails library - inject!
|
||||||
|
case 'b':
|
||||||
|
message = message.data.slice(1)
|
||||||
|
addScript(message);
|
||||||
|
log('Loaded Wails Runtime');
|
||||||
|
|
||||||
|
// We need to now send a message to the backend telling it
|
||||||
|
// we have loaded (System Start)
|
||||||
|
window.webkit.messageHandlers.external.postMessage("SS");
|
||||||
|
|
||||||
|
// Now wails runtime is loaded, wails for the ready event
|
||||||
|
// and callback to the main app
|
||||||
|
// window.wails.Events.On('wails:loaded', function () {
|
||||||
|
if (callback) {
|
||||||
|
log('Notifying application');
|
||||||
|
callback(window.wails);
|
||||||
|
}
|
||||||
|
// });
|
||||||
|
break;
|
||||||
|
// // Notifications
|
||||||
|
// case 'n':
|
||||||
|
// addScript(message.data.slice(1), true);
|
||||||
|
// break;
|
||||||
|
// // Binding
|
||||||
|
// case 'b':
|
||||||
|
// const binding = message.data.slice(1);
|
||||||
|
// //log("Binding: " + binding)
|
||||||
|
// window.wails._.NewBinding(binding);
|
||||||
|
// break;
|
||||||
|
// // Call back
|
||||||
|
case 'c':
|
||||||
|
const callbackData = message.data.slice(1);
|
||||||
|
window.wails._.Callback(callbackData);
|
||||||
|
break;
|
||||||
|
// Tray
|
||||||
|
case 'T':
|
||||||
|
const trayMessage = message.data.slice(1);
|
||||||
|
switch (trayMessage[0]) {
|
||||||
|
case 'S':
|
||||||
|
// Set tray
|
||||||
|
const trayJSON = trayMessage.slice(1);
|
||||||
|
let tray = JSON.parse(trayJSON)
|
||||||
|
setTray(tray)
|
||||||
|
break
|
||||||
|
case 'U':
|
||||||
|
// Update label
|
||||||
|
const updateTrayLabelJSON = trayMessage.slice(1);
|
||||||
|
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
||||||
|
updateTrayLabel(trayLabelData)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
log('Unknown tray message: ' + message.data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log('Unknown message: ' + message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ const Events = require('./events');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked when the user changes the
|
* Registers an event listener that will be invoked when the user changes the
|
||||||
* desktop theme (light mode / dark mode). The callback receives a boolean which
|
* desktop theme (light mode / dark mode). The callback receives a booleanean which
|
||||||
* indicates if dark mode is enabled.
|
* indicates if dark mode is enabled.
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
@@ -43,12 +43,12 @@ function DarkModeEnabled() {
|
|||||||
* Mac Title Bar Config
|
* Mac Title Bar Config
|
||||||
* Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings
|
* Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings
|
||||||
* @typedef {Object} MacTitleBar
|
* @typedef {Object} MacTitleBar
|
||||||
* @param {bool} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
* @param {boolean} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
||||||
* @param {bool} HideTitle - NSWindow.hideTitle
|
* @param {boolean} HideTitle - NSWindow.hideTitle
|
||||||
* @param {bool} HideTitleBar - NSWindow.hideTitleBar
|
* @param {boolean} HideTitleBar - NSWindow.hideTitleBar
|
||||||
* @param {bool} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar
|
* @param {boolean} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar
|
||||||
* @param {bool} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger)
|
* @param {boolean} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger)
|
||||||
* @param {bool} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area
|
* @param {boolean} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ function DarkModeEnabled() {
|
|||||||
* @param {number} MinHeight - Window Minimum Height
|
* @param {number} MinHeight - Window Minimum Height
|
||||||
* @param {number} MaxWidth - Window Maximum Width
|
* @param {number} MaxWidth - Window Maximum Width
|
||||||
* @param {number} MaxHeight - Window Maximum Height
|
* @param {number} MaxHeight - Window Maximum Height
|
||||||
* @param {bool} StartHidden - Start with window hidden
|
* @param {boolean} StartHidden - Start with window hidden
|
||||||
* @param {bool} DevTools - Enables the window devtools
|
* @param {boolean} DevTools - Enables the window devtools
|
||||||
* @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
* @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
||||||
* @param {MacAppConfig} [Mac] - Configuration when running on Mac
|
* @param {MacAppConfig} [Mac] - Configuration when running on Mac
|
||||||
* @param {LinuxAppConfig} [Linux] - Configuration when running on Linux
|
* @param {LinuxAppConfig} [Linux] - Configuration when running on Linux
|
||||||
@@ -88,11 +88,23 @@ function AppConfig() {
|
|||||||
return window.wails.System.AppConfig.get();
|
return window.wails.System.AppConfig.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function LogLevel() {
|
||||||
|
return window.wails.System.LogLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
function Platform() {
|
||||||
|
return window.wails.System.Platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppType() {
|
||||||
|
return window.wails.System.AppType();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
OnThemeChange: OnThemeChange,
|
OnThemeChange: OnThemeChange,
|
||||||
DarkModeEnabled: DarkModeEnabled,
|
DarkModeEnabled: DarkModeEnabled,
|
||||||
LogLevel: window.wails.System.LogLevel,
|
LogLevel: LogLevel,
|
||||||
Platform: window.wails.System.Platform,
|
Platform: Platform,
|
||||||
AppType: window.wails.System.AppType,
|
AppType: AppType,
|
||||||
AppConfig: AppConfig,
|
AppConfig: AppConfig,
|
||||||
};
|
};
|
||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
mode: 'production',
|
mode: 'production',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, '..', 'assets'),
|
path: path.resolve(__dirname, '..', 'assets'),
|
||||||
filename: 'desktop.js',
|
filename: 'desktop_'+platform+'.js',
|
||||||
library: 'Wails'
|
library: 'Wails'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
@@ -35,5 +35,6 @@ func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
|||||||
|
|
||||||
// Quit the application
|
// Quit the application
|
||||||
func (r *Runtime) Quit() {
|
func (r *Runtime) Quit() {
|
||||||
|
// Start shutdown of Wails
|
||||||
r.bus.Publish("quit", "runtime.Quit()")
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -49,12 +50,20 @@ func main() {
|
|||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js")
|
wailsJS := fs.RelativePath("../assets/desktop_" + platform + ".js")
|
||||||
runtimeData, err := ioutil.ReadFile(wailsJS)
|
runtimeData, err := ioutil.ReadFile(wailsJS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy this file to bridge directory for embedding
|
||||||
|
bridgeDir := fs.RelativePath("../../bridge/" + platform + ".js")
|
||||||
|
println("Copying", wailsJS, "to", bridgeDir)
|
||||||
|
err = fs.CopyFile(wailsJS, bridgeDir)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to C structure
|
// Convert to C structure
|
||||||
runtimeC := `
|
runtimeC := `
|
||||||
// runtime.c (c) 2019-Present Lea Anthony.
|
// runtime.c (c) 2019-Present Lea Anthony.
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
golog "log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/deepcopy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Options defines the optional data that may be used
|
// Options defines the optional data that may be used
|
||||||
@@ -64,21 +67,31 @@ func fatal(err error) {
|
|||||||
// New creates a new store
|
// New creates a new store
|
||||||
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
||||||
|
|
||||||
dataType := reflect.TypeOf(defaultValue)
|
if defaultValue == nil {
|
||||||
|
golog.Fatal("Cannot initialise a store with nil")
|
||||||
|
}
|
||||||
|
|
||||||
result := Store{
|
result := Store{
|
||||||
name: name,
|
name: name,
|
||||||
runtime: p.runtime,
|
runtime: p.runtime,
|
||||||
data: reflect.ValueOf(defaultValue),
|
|
||||||
dataType: dataType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the sync listener
|
// Setup the sync listener
|
||||||
result.setupListener()
|
result.setupListener()
|
||||||
|
|
||||||
|
result.Set(defaultValue)
|
||||||
|
|
||||||
return &result
|
return &result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Store) lock() {
|
||||||
|
s.mux.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) unlock() {
|
||||||
|
s.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// OnError takes a function that will be called
|
// OnError takes a function that will be called
|
||||||
// whenever an error occurs
|
// whenever an error occurs
|
||||||
func (s *Store) OnError(callback func(error)) {
|
func (s *Store) OnError(callback func(error)) {
|
||||||
@@ -105,7 +118,7 @@ func (s *Store) processUpdatedData(data string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lock mutex for writing
|
// Lock mutex for writing
|
||||||
s.mux.Lock()
|
s.lock()
|
||||||
|
|
||||||
// Handle nulls
|
// Handle nulls
|
||||||
if newData == nil {
|
if newData == nil {
|
||||||
@@ -116,7 +129,7 @@ func (s *Store) processUpdatedData(data string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unlock mutex
|
// Unlock mutex
|
||||||
s.mux.Unlock()
|
s.unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -146,20 +159,34 @@ func (s *Store) setupListener() {
|
|||||||
// Resetting the curent data will resync
|
// Resetting the curent data will resync
|
||||||
s.resync()
|
s.resync()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Do initial resync
|
||||||
|
s.resync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Store) resync() {
|
func (s *Store) resync() {
|
||||||
// Stringify data
|
|
||||||
newdata, err := json.Marshal(s.data.Interface())
|
// Lock
|
||||||
if err != nil {
|
s.lock()
|
||||||
if s.errorHandler != nil {
|
defer s.unlock()
|
||||||
s.errorHandler(err)
|
|
||||||
return
|
var result string
|
||||||
|
|
||||||
|
if s.data.IsValid() {
|
||||||
|
rawdata, err := json.Marshal(s.data.Interface())
|
||||||
|
if err != nil {
|
||||||
|
if s.errorHandler != nil {
|
||||||
|
s.errorHandler(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
result = string(rawdata)
|
||||||
|
} else {
|
||||||
|
result = "{}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit event to front end
|
// Emit event to front end
|
||||||
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, string(newdata))
|
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, result)
|
||||||
|
|
||||||
// Notify subscribers
|
// Notify subscribers
|
||||||
s.notify()
|
s.notify()
|
||||||
@@ -172,7 +199,9 @@ func (s *Store) notify() {
|
|||||||
for _, callback := range s.callbacks {
|
for _, callback := range s.callbacks {
|
||||||
|
|
||||||
// Build args
|
// Build args
|
||||||
|
s.lock()
|
||||||
args := []reflect.Value{s.data}
|
args := []reflect.Value{s.data}
|
||||||
|
s.unlock()
|
||||||
|
|
||||||
if s.notifySynchronously {
|
if s.notifySynchronously {
|
||||||
callback.Call(args)
|
callback.Call(args)
|
||||||
@@ -187,16 +216,31 @@ func (s *Store) notify() {
|
|||||||
// and notify listeners of the change
|
// and notify listeners of the change
|
||||||
func (s *Store) Set(data interface{}) error {
|
func (s *Store) Set(data interface{}) error {
|
||||||
|
|
||||||
inType := reflect.TypeOf(data)
|
if data == nil {
|
||||||
|
return fmt.Errorf("cannot set store to nil")
|
||||||
|
}
|
||||||
|
|
||||||
if inType != s.dataType {
|
s.lock()
|
||||||
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
|
||||||
|
dataCopy := deepcopy.Copy(data)
|
||||||
|
|
||||||
|
if dataCopy != nil {
|
||||||
|
inType := reflect.TypeOf(dataCopy)
|
||||||
|
|
||||||
|
if inType != s.dataType && s.data.IsValid() {
|
||||||
|
s.unlock()
|
||||||
|
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.dataType == nil {
|
||||||
|
s.dataType = reflect.TypeOf(dataCopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save data
|
// Save data
|
||||||
s.mux.Lock()
|
s.data = reflect.ValueOf(dataCopy)
|
||||||
s.data = reflect.ValueOf(data)
|
|
||||||
s.mux.Unlock()
|
s.unlock()
|
||||||
|
|
||||||
// Resync with subscribers
|
// Resync with subscribers
|
||||||
s.resync()
|
s.resync()
|
||||||
@@ -247,7 +291,9 @@ func (s *Store) Subscribe(callback interface{}) {
|
|||||||
|
|
||||||
callbackFunc := reflect.ValueOf(callback)
|
callbackFunc := reflect.ValueOf(callback)
|
||||||
|
|
||||||
|
s.lock()
|
||||||
s.callbacks = append(s.callbacks, callbackFunc)
|
s.callbacks = append(s.callbacks, callbackFunc)
|
||||||
|
s.unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// updaterCheck ensures the given function to Update() is
|
// updaterCheck ensures the given function to Update() is
|
||||||
@@ -297,7 +343,9 @@ func (s *Store) Update(updater interface{}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build args
|
// Build args
|
||||||
|
s.lock()
|
||||||
args := []reflect.Value{s.data}
|
args := []reflect.Value{s.data}
|
||||||
|
s.unlock()
|
||||||
|
|
||||||
// Make call
|
// Make call
|
||||||
results := reflect.ValueOf(updater).Call(args)
|
results := reflect.ValueOf(updater).Call(args)
|
||||||
@@ -308,5 +356,12 @@ func (s *Store) Update(updater interface{}) {
|
|||||||
|
|
||||||
// Get returns the value of the data that's kept in the current state / Store
|
// Get returns the value of the data that's kept in the current state / Store
|
||||||
func (s *Store) Get() interface{} {
|
func (s *Store) Get() interface{} {
|
||||||
|
s.lock()
|
||||||
|
defer s.unlock()
|
||||||
|
|
||||||
|
if !s.data.IsValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return s.data.Interface()
|
return s.data.Interface()
|
||||||
}
|
}
|
||||||
|
|||||||
165
v2/internal/runtime/store_test.go
Normal file
165
v2/internal/runtime/store_test.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/rand"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
internallogger "github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
|
||||||
|
is2 "github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithNilDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", 0)
|
||||||
|
|
||||||
|
// You should be able to write a new value into a
|
||||||
|
// store initialised with nil
|
||||||
|
err = testStore.Set(100)
|
||||||
|
is.NoErr(err)
|
||||||
|
|
||||||
|
// You shouldn't be able to write different types to the
|
||||||
|
// store
|
||||||
|
err = testStore.Set(false)
|
||||||
|
is.True(err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithScalarDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
testStore := storeProvider.New("test", 100)
|
||||||
|
value := testStore.Get()
|
||||||
|
is.Equal(value, 100)
|
||||||
|
testStore.resync()
|
||||||
|
value = testStore.Get()
|
||||||
|
is.Equal(value, 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_NewWithStructDefault(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
type TestValue struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
testValue := &TestValue{
|
||||||
|
Name: "hi",
|
||||||
|
}
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", testValue)
|
||||||
|
|
||||||
|
err = testStore.Set(testValue)
|
||||||
|
is.NoErr(err)
|
||||||
|
testStore.resync()
|
||||||
|
value := testStore.Get()
|
||||||
|
is.Equal(value, testValue)
|
||||||
|
is.Equal(value.(*TestValue).Name, "hi")
|
||||||
|
|
||||||
|
testValue = &TestValue{
|
||||||
|
Name: "there",
|
||||||
|
}
|
||||||
|
err = testStore.Set(testValue)
|
||||||
|
is.NoErr(err)
|
||||||
|
testStore.resync()
|
||||||
|
value = testStore.Get()
|
||||||
|
is.Equal(value, testValue)
|
||||||
|
is.Equal(value.(*TestValue).Name, "there")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStoreProvider_RapidReadWrite(t *testing.T) {
|
||||||
|
is := is2.New(t)
|
||||||
|
|
||||||
|
defaultLogger := logger.NewDefaultLogger()
|
||||||
|
testLogger := internallogger.New(defaultLogger)
|
||||||
|
//testLogger.SetLogLevel(logger.TRACE)
|
||||||
|
serviceBus := servicebus.New(testLogger)
|
||||||
|
err := serviceBus.Start()
|
||||||
|
is.NoErr(err)
|
||||||
|
defer serviceBus.Stop()
|
||||||
|
|
||||||
|
testRuntime := New(serviceBus)
|
||||||
|
storeProvider := newStore(testRuntime)
|
||||||
|
|
||||||
|
testStore := storeProvider.New("test", 1)
|
||||||
|
|
||||||
|
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
readers := 100
|
||||||
|
writers := 100
|
||||||
|
wg.Add(readers + writers)
|
||||||
|
// Setup readers
|
||||||
|
go func(testStore *Store, ctx context.Context) {
|
||||||
|
for readerCount := 0; readerCount < readers; readerCount++ {
|
||||||
|
go func(store *Store, ctx context.Context, id int) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
store.Get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(testStore, ctx, readerCount)
|
||||||
|
}
|
||||||
|
}(testStore, ctx)
|
||||||
|
|
||||||
|
// Setup writers
|
||||||
|
go func(testStore *Store, ctx context.Context) {
|
||||||
|
for writerCount := 0; writerCount < writers; writerCount++ {
|
||||||
|
go func(store *Store, ctx context.Context, id int) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
err := store.Set(rand.Int())
|
||||||
|
is.NoErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(testStore, ctx, writerCount)
|
||||||
|
}
|
||||||
|
}(testStore, ctx)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ type Window interface {
|
|||||||
Unminimise()
|
Unminimise()
|
||||||
SetTitle(title string)
|
SetTitle(title string)
|
||||||
SetSize(width int, height int)
|
SetSize(width int, height int)
|
||||||
|
SetMinSize(width int, height int)
|
||||||
|
SetMaxSize(width int, height int)
|
||||||
SetPosition(x int, y int)
|
SetPosition(x int, y int)
|
||||||
Fullscreen()
|
Fullscreen()
|
||||||
UnFullscreen()
|
UnFullscreen()
|
||||||
@@ -85,6 +87,18 @@ func (w *window) SetSize(width int, height int) {
|
|||||||
w.bus.Publish(message, "")
|
w.bus.Publish(message, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size of the window
|
||||||
|
func (w *window) SetMinSize(width int, height int) {
|
||||||
|
message := fmt.Sprintf("window:minsize:%d:%d", width, height)
|
||||||
|
w.bus.Publish(message, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size of the window
|
||||||
|
func (w *window) SetMaxSize(width int, height int) {
|
||||||
|
message := fmt.Sprintf("window:maxsize:%d:%d", width, height)
|
||||||
|
w.bus.Publish(message, "")
|
||||||
|
}
|
||||||
|
|
||||||
// SetPosition sets the position of the window
|
// SetPosition sets the position of the window
|
||||||
func (w *window) SetPosition(x int, y int) {
|
func (w *window) SetPosition(x int, y int) {
|
||||||
message := fmt.Sprintf("window:position:%d:%d", x, y)
|
message := fmt.Sprintf("window:position:%d:%d", x, y)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package servicebus
|
package servicebus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -12,23 +13,26 @@ import (
|
|||||||
type ServiceBus struct {
|
type ServiceBus struct {
|
||||||
listeners map[string][]chan *Message
|
listeners map[string][]chan *Message
|
||||||
messageQueue chan *Message
|
messageQueue chan *Message
|
||||||
quitChannel chan struct{}
|
|
||||||
wg sync.WaitGroup
|
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
closed bool
|
closed bool
|
||||||
debug bool
|
debug bool
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ServiceBus
|
// New creates a new ServiceBus
|
||||||
// The internal message queue is set to 100 messages
|
// The internal message queue is set to 100 messages
|
||||||
// Listener queues are set to 10
|
// Listener queues are set to 10
|
||||||
func New(logger *logger.Logger) *ServiceBus {
|
func New(logger *logger.Logger) *ServiceBus {
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &ServiceBus{
|
return &ServiceBus{
|
||||||
listeners: make(map[string][]chan *Message),
|
listeners: make(map[string][]chan *Message),
|
||||||
messageQueue: make(chan *Message, 100),
|
messageQueue: make(chan *Message, 100),
|
||||||
quitChannel: make(chan struct{}, 1),
|
|
||||||
logger: logger.CustomLogger("Service Bus"),
|
logger: logger.CustomLogger("Service Bus"),
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,23 +67,22 @@ func (s *ServiceBus) Debug() {
|
|||||||
// Start the service bus
|
// Start the service bus
|
||||||
func (s *ServiceBus) Start() error {
|
func (s *ServiceBus) Start() error {
|
||||||
|
|
||||||
s.logger.Trace("Starting")
|
|
||||||
|
|
||||||
// Prevent starting when closed
|
// Prevent starting when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return fmt.Errorf("cannot call start on closed servicebus")
|
return fmt.Errorf("cannot call start on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We run in a different thread
|
s.logger.Trace("Starting")
|
||||||
go func() {
|
|
||||||
|
|
||||||
quit := false
|
go func() {
|
||||||
s.wg.Add(1)
|
defer s.logger.Trace("Stopped")
|
||||||
|
|
||||||
// Loop until we get a quit message
|
// Loop until we get a quit message
|
||||||
for !quit {
|
for {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
return
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
case message := <-s.messageQueue:
|
case message := <-s.messageQueue:
|
||||||
@@ -90,16 +93,9 @@ func (s *ServiceBus) Start() error {
|
|||||||
}
|
}
|
||||||
// Dispatch message
|
// Dispatch message
|
||||||
s.dispatchMessage(message)
|
s.dispatchMessage(message)
|
||||||
|
|
||||||
// Listen for quit messages
|
|
||||||
case <-s.quitChannel:
|
|
||||||
quit = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicate we have shut down
|
|
||||||
s.wg.Done()
|
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -116,10 +112,7 @@ func (s *ServiceBus) Stop() error {
|
|||||||
s.closed = true
|
s.closed = true
|
||||||
|
|
||||||
// Send quit message
|
// Send quit message
|
||||||
s.quitChannel <- struct{}{}
|
s.cancel()
|
||||||
|
|
||||||
// Wait for dispatcher to stop
|
|
||||||
s.wg.Wait()
|
|
||||||
|
|
||||||
// Close down subscriber channels
|
// Close down subscriber channels
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
@@ -134,7 +127,6 @@ func (s *ServiceBus) Stop() error {
|
|||||||
// Close message queue
|
// Close message queue
|
||||||
close(s.messageQueue)
|
close(s.messageQueue)
|
||||||
|
|
||||||
s.logger.Trace("Stopped")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +163,6 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
|||||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +174,6 @@ func (s *ServiceBus) Publish(topic string, data interface{}) {
|
|||||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message := NewMessageForTarget(topic, data, target)
|
message := NewMessageForTarget(topic, data, target)
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package signal
|
package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"os"
|
"os"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -20,24 +22,24 @@ type Manager struct {
|
|||||||
// signalChannel
|
// signalChannel
|
||||||
signalchannel chan os.Signal
|
signalchannel chan os.Signal
|
||||||
|
|
||||||
// Quit channel
|
// ctx
|
||||||
quitChannel <-chan *servicebus.Message
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// Parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new signal manager
|
// NewManager creates a new signal manager
|
||||||
func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Manager{
|
result := &Manager{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
logger: logger.CustomLogger("Event Manager"),
|
logger: logger.CustomLogger("Event Manager"),
|
||||||
signalchannel: make(chan os.Signal, 2),
|
signalchannel: make(chan os.Signal, 2),
|
||||||
quitChannel: quitChannel,
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -49,20 +51,23 @@ func (m *Manager) Start() {
|
|||||||
// Hook into interrupts
|
// Hook into interrupts
|
||||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
// Spin off signal listener
|
m.wg.Add(1)
|
||||||
|
|
||||||
|
// Spin off signal listener and wait for either a cancellation
|
||||||
|
// or signal
|
||||||
go func() {
|
go func() {
|
||||||
running := true
|
select {
|
||||||
for running {
|
case <-m.signalchannel:
|
||||||
select {
|
println()
|
||||||
case <-m.signalchannel:
|
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||||
println()
|
m.bus.Publish("quit", "ctrl-c pressed")
|
||||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
|
||||||
m.bus.Publish("quit", "ctrl-c pressed")
|
// Start shutdown of Wails
|
||||||
case <-m.quitChannel:
|
m.cancel()
|
||||||
running = false
|
|
||||||
break
|
case <-m.ctx.Done():
|
||||||
}
|
|
||||||
}
|
}
|
||||||
m.logger.Trace("Shutdown")
|
m.logger.Trace("Shutdown")
|
||||||
|
m.wg.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
// Binding is the Binding subsystem. It manages all service bus messages
|
// Binding is the Binding subsystem. It manages all service bus messages
|
||||||
// starting with "binding".
|
// starting with "binding".
|
||||||
type Binding struct {
|
type Binding struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
bindingChannel <-chan *servicebus.Message
|
bindingChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
running bool
|
||||||
|
|
||||||
// Binding db
|
// Binding db
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
@@ -27,12 +27,6 @@ type Binding struct {
|
|||||||
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
||||||
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
bindingChannel, err := bus.Subscribe("binding")
|
bindingChannel, err := bus.Subscribe("binding")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -40,7 +34,6 @@ func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Binding{
|
result := &Binding{
|
||||||
quitChannel: quitChannel,
|
|
||||||
bindingChannel: bindingChannel,
|
bindingChannel: bindingChannel,
|
||||||
logger: logger.CustomLogger("Binding Subsystem"),
|
logger: logger.CustomLogger("Binding Subsystem"),
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
@@ -61,20 +54,16 @@ func (b *Binding) Start() error {
|
|||||||
go func() {
|
go func() {
|
||||||
for b.running {
|
for b.running {
|
||||||
select {
|
select {
|
||||||
case <-b.quitChannel:
|
|
||||||
b.running = false
|
|
||||||
case bindingMessage := <-b.bindingChannel:
|
case bindingMessage := <-b.bindingChannel:
|
||||||
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
b.logger.Trace("Shutdown")
|
||||||
// Call shutdown
|
|
||||||
b.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binding) shutdown() {
|
func (b *Binding) Close() {
|
||||||
b.logger.Trace("Shutdown")
|
b.running = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -16,9 +19,10 @@ import (
|
|||||||
// Call is the Call subsystem. It manages all service bus messages
|
// Call is the Call subsystem. It manages all service bus messages
|
||||||
// starting with "call".
|
// starting with "call".
|
||||||
type Call struct {
|
type Call struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
callChannel <-chan *servicebus.Message
|
callChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// bindings DB
|
// bindings DB
|
||||||
DB *binding.DB
|
DB *binding.DB
|
||||||
@@ -31,16 +35,16 @@ type Call struct {
|
|||||||
|
|
||||||
// runtime
|
// runtime
|
||||||
runtime *runtime.Runtime
|
runtime *runtime.Runtime
|
||||||
|
|
||||||
|
// context
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCall creates a new call subsystem
|
// NewCall creates a new call subsystem
|
||||||
func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
callChannel, err := bus.Subscribe("call:invoke")
|
callChannel, err := bus.Subscribe("call:invoke")
|
||||||
@@ -49,12 +53,13 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Call{
|
result := &Call{
|
||||||
quitChannel: quitChannel,
|
|
||||||
callChannel: callChannel,
|
callChannel: callChannel,
|
||||||
logger: logger.CustomLogger("Call Subsystem"),
|
logger: logger.CustomLogger("Call Subsystem"),
|
||||||
DB: DB,
|
DB: DB,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -63,22 +68,21 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (c *Call) Start() error {
|
func (c *Call) Start() error {
|
||||||
|
|
||||||
c.running = true
|
c.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for c.running {
|
defer c.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.quitChannel:
|
case <-c.ctx.Done():
|
||||||
c.running = false
|
c.wg.Done()
|
||||||
|
return
|
||||||
case callMessage := <-c.callChannel:
|
case callMessage := <-c.callChannel:
|
||||||
// TODO: Check if this works ok in a goroutine
|
|
||||||
c.processCall(callMessage)
|
c.processCall(callMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
c.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -190,10 +194,6 @@ func (c *Call) sendError(err error, payload *message.CallMessage, clientID strin
|
|||||||
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Call) shutdown() {
|
|
||||||
c.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallbackMessage defines a message that contains the result of a call
|
// CallbackMessage defines a message that contains the result of a call
|
||||||
type CallbackMessage struct {
|
type CallbackMessage struct {
|
||||||
Result interface{} `json:"result"`
|
Result interface{} `json:"result"`
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -22,9 +23,7 @@ type eventListener struct {
|
|||||||
// Event is the Eventing subsystem. It manages all service bus messages
|
// Event is the Eventing subsystem. It manages all service bus messages
|
||||||
// starting with "event".
|
// starting with "event".
|
||||||
type Event struct {
|
type Event struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
eventChannel <-chan *servicebus.Message
|
eventChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
|
||||||
// Event listeners
|
// Event listeners
|
||||||
listeners map[string][]*eventListener
|
listeners map[string][]*eventListener
|
||||||
@@ -32,16 +31,16 @@ type Event struct {
|
|||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
|
// ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent creates a new log subsystem
|
// NewEvent creates a new log subsystem
|
||||||
func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
eventChannel, err := bus.Subscribe("event")
|
eventChannel, err := bus.Subscribe("event")
|
||||||
@@ -50,10 +49,11 @@ func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Event{
|
result := &Event{
|
||||||
quitChannel: quitChannel,
|
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
logger: logger.CustomLogger("Event Subsystem"),
|
logger: logger.CustomLogger("Event Subsystem"),
|
||||||
listeners: make(map[string][]*eventListener),
|
listeners: make(map[string][]*eventListener),
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -80,15 +80,16 @@ func (e *Event) Start() error {
|
|||||||
|
|
||||||
e.logger.Trace("Starting")
|
e.logger.Trace("Starting")
|
||||||
|
|
||||||
e.running = true
|
e.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for e.running {
|
defer e.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-e.quitChannel:
|
case <-e.ctx.Done():
|
||||||
e.running = false
|
e.wg.Done()
|
||||||
break
|
return
|
||||||
case eventMessage := <-e.eventChannel:
|
case eventMessage := <-e.eventChannel:
|
||||||
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
||||||
eventType := splitTopic[1]
|
eventType := splitTopic[1]
|
||||||
@@ -128,8 +129,6 @@ func (e *Event) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
e.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -190,7 +189,3 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
|||||||
// Unlock
|
// Unlock
|
||||||
e.notifyLock.Unlock()
|
e.notifyLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Event) shutdown() {
|
|
||||||
e.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
@@ -12,15 +14,23 @@ import (
|
|||||||
// Log is the Logging subsystem. It handles messages with topics starting
|
// Log is the Logging subsystem. It handles messages with topics starting
|
||||||
// with "log:"
|
// with "log:"
|
||||||
type Log struct {
|
type Log struct {
|
||||||
logChannel <-chan *servicebus.Message
|
logChannel <-chan *servicebus.Message
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
running bool
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// Logger!
|
// Logger!
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
|
|
||||||
// Loglevel store
|
// Loglevel store
|
||||||
logLevelStore *runtime.Store
|
logLevelStore *runtime.Store
|
||||||
|
|
||||||
|
// Context for shutdown
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
// internal waitgroup
|
||||||
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLog creates a new log subsystem
|
// NewLog creates a new log subsystem
|
||||||
@@ -32,17 +42,14 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to quit messages
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &Log{
|
result := &Log{
|
||||||
logChannel: logChannel,
|
logChannel: logChannel,
|
||||||
quitChannel: quitChannel,
|
|
||||||
logger: logger,
|
logger: logger,
|
||||||
logLevelStore: logLevelStore,
|
logLevelStore: logLevelStore,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -51,15 +58,17 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (l *Log) Start() error {
|
func (l *Log) Start() error {
|
||||||
|
|
||||||
l.running = true
|
l.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for l.running {
|
defer l.logger.Trace("Logger Shutdown")
|
||||||
|
|
||||||
|
for l.shouldQuit == false {
|
||||||
select {
|
select {
|
||||||
case <-l.quitChannel:
|
case <-l.ctx.Done():
|
||||||
l.running = false
|
l.wg.Done()
|
||||||
break
|
return
|
||||||
case logMessage := <-l.logChannel:
|
case logMessage := <-l.logChannel:
|
||||||
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
||||||
switch logType {
|
switch logType {
|
||||||
@@ -98,8 +107,12 @@ func (l *Log) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
l.logger.Trace("Logger Shutdown")
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Log) Close() {
|
||||||
|
l.cancel()
|
||||||
|
l.wg.Wait()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
@@ -13,9 +16,10 @@ import (
|
|||||||
// Menu is the subsystem that handles the operation of menus. It manages all service bus messages
|
// Menu is the subsystem that handles the operation of menus. It manages all service bus messages
|
||||||
// starting with "menu".
|
// starting with "menu".
|
||||||
type Menu struct {
|
type Menu struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
menuChannel <-chan *servicebus.Message
|
menuChannel <-chan *servicebus.Message
|
||||||
running bool
|
|
||||||
|
// shutdown flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -25,16 +29,16 @@ type Menu struct {
|
|||||||
|
|
||||||
// Menu Manager
|
// Menu Manager
|
||||||
menuManager *menumanager.Manager
|
menuManager *menumanager.Manager
|
||||||
|
|
||||||
|
// ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// parent waitgroup
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMenu creates a new menu subsystem
|
// NewMenu creates a new menu subsystem
|
||||||
func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
|
func NewMenu(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to menu messages
|
// Subscribe to menu messages
|
||||||
menuChannel, err := bus.Subscribe("menu:")
|
menuChannel, err := bus.Subscribe("menu:")
|
||||||
@@ -43,11 +47,12 @@ func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *men
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Menu{
|
result := &Menu{
|
||||||
quitChannel: quitChannel,
|
|
||||||
menuChannel: menuChannel,
|
menuChannel: menuChannel,
|
||||||
logger: logger.CustomLogger("Menu Subsystem"),
|
logger: logger.CustomLogger("Menu Subsystem"),
|
||||||
bus: bus,
|
bus: bus,
|
||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
|
ctx: ctx,
|
||||||
|
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -58,15 +63,16 @@ func (m *Menu) Start() error {
|
|||||||
|
|
||||||
m.logger.Trace("Starting")
|
m.logger.Trace("Starting")
|
||||||
|
|
||||||
m.running = true
|
m.wg.Add(1)
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for m.running {
|
defer m.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-m.quitChannel:
|
case <-m.ctx.Done():
|
||||||
m.running = false
|
m.wg.Done()
|
||||||
break
|
return
|
||||||
case menuMessage := <-m.menuChannel:
|
case menuMessage := <-m.menuChannel:
|
||||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||||
menuMessageType := splitTopic[1]
|
menuMessageType := splitTopic[1]
|
||||||
@@ -147,14 +153,7 @@ func (m *Menu) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
m.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Menu) shutdown() {
|
|
||||||
m.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
@@ -12,7 +14,6 @@ import (
|
|||||||
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
||||||
// with "runtime:"
|
// with "runtime:"
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
quitChannel <-chan *servicebus.Message
|
|
||||||
runtimeChannel <-chan *servicebus.Message
|
runtimeChannel <-chan *servicebus.Message
|
||||||
|
|
||||||
// The hooks channel allows us to hook into frontend startup
|
// The hooks channel allows us to hook into frontend startup
|
||||||
@@ -20,22 +21,23 @@ type Runtime struct {
|
|||||||
startupCallback func(*runtime.Runtime)
|
startupCallback func(*runtime.Runtime)
|
||||||
shutdownCallback func()
|
shutdownCallback func()
|
||||||
|
|
||||||
running bool
|
// quit flag
|
||||||
|
shouldQuit bool
|
||||||
|
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// Runtime library
|
// Runtime library
|
||||||
runtime *runtime.Runtime
|
runtime *runtime.Runtime
|
||||||
|
|
||||||
|
//ctx
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
|
// Startup Hook
|
||||||
|
startupOnce sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime creates a new runtime subsystem
|
// NewRuntime creates a new runtime subsystem
|
||||||
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime)) (*Runtime, error) {
|
||||||
|
|
||||||
// Register quit channel
|
|
||||||
quitChannel, err := bus.Subscribe("quit")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Subscribe to log messages
|
||||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||||
@@ -50,13 +52,12 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
quitChannel: quitChannel,
|
runtimeChannel: runtimeChannel,
|
||||||
runtimeChannel: runtimeChannel,
|
hooksChannel: hooksChannel,
|
||||||
hooksChannel: hooksChannel,
|
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
runtime: runtime.New(bus),
|
||||||
runtime: runtime.New(bus),
|
startupCallback: startupCallback,
|
||||||
startupCallback: startupCallback,
|
ctx: ctx,
|
||||||
shutdownCallback: shutdownCallback,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -65,15 +66,11 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (r *Runtime) Start() error {
|
func (r *Runtime) Start() error {
|
||||||
|
|
||||||
r.running = true
|
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
for r.running {
|
defer r.logger.Trace("Shutdown")
|
||||||
|
for {
|
||||||
select {
|
select {
|
||||||
case <-r.quitChannel:
|
|
||||||
r.running = false
|
|
||||||
break
|
|
||||||
case hooksMessage := <-r.hooksChannel:
|
case hooksMessage := <-r.hooksChannel:
|
||||||
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
||||||
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
||||||
@@ -81,9 +78,11 @@ func (r *Runtime) Start() error {
|
|||||||
switch hook {
|
switch hook {
|
||||||
case "startup":
|
case "startup":
|
||||||
if r.startupCallback != nil {
|
if r.startupCallback != nil {
|
||||||
go r.startupCallback(r.runtime)
|
r.startupOnce.Do(func() {
|
||||||
|
go r.startupCallback(r.runtime)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
r.logger.Error("no startup callback registered!")
|
r.logger.Warning("no startup callback registered!")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
r.logger.Error("unknown hook message: %+v", hooksMessage)
|
r.logger.Error("unknown hook message: %+v", hooksMessage)
|
||||||
@@ -113,11 +112,10 @@ func (r *Runtime) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error(err.Error())
|
r.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
|
case <-r.ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call shutdown
|
|
||||||
r.shutdown()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -128,13 +126,6 @@ func (r *Runtime) GoRuntime() *runtime.Runtime {
|
|||||||
return r.runtime
|
return r.runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runtime) shutdown() {
|
|
||||||
if r.shutdownCallback != nil {
|
|
||||||
go r.shutdownCallback()
|
|
||||||
}
|
|
||||||
r.logger.Trace("Shutdown")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
||||||
switch method {
|
switch method {
|
||||||
case "open":
|
case "open":
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2"
|
"github.com/wailsapp/wails/v2"
|
||||||
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
app, err := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
app.Bind(newBasic())
|
app.Bind(newBasic())
|
||||||
|
|
||||||
app.Run()
|
err = app.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ type WebClient struct {
|
|||||||
running bool
|
running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (wc *WebClient) SetTrayMenu(trayMenuJSON string) {
|
||||||
|
wc.logger.Info("Not implemented in server build")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wc *WebClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
||||||
|
wc.logger.Info("Not implemented in server build")
|
||||||
|
}
|
||||||
|
|
||||||
func (wc *WebClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
func (wc *WebClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
||||||
wc.logger.Info("Not implemented in server build")
|
wc.logger.Info("Not implemented in server build")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/colour"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CLILogger is used by the cli
|
// CLILogger is used by the cli
|
||||||
@@ -51,9 +53,9 @@ func (c *CLILogger) Println(message string, args ...interface{}) {
|
|||||||
// Fatal prints the given message then aborts
|
// Fatal prints the given message then aborts
|
||||||
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
||||||
temp := fmt.Sprintf(message, args...)
|
temp := fmt.Sprintf(message, args...)
|
||||||
_, err := fmt.Fprintln(c.Writer, "FATAL: "+temp)
|
_, err := fmt.Fprintln(c.Writer, colour.Red("FATAL: "+temp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("FATAL: ", err)
|
println(colour.Red("FATAL: " + err.Error()))
|
||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,6 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
options.CompiledBinary = compiledBinary
|
options.CompiledBinary = compiledBinary
|
||||||
|
|
||||||
// Create the command
|
// Create the command
|
||||||
fmt.Printf("Compile command: %+v", commands.AsSlice())
|
|
||||||
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
||||||
|
|
||||||
// Set the directory
|
// Set the directory
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ func Build(options *Options) (string, error) {
|
|||||||
builder = newHybridBuilder(options)
|
builder = newHybridBuilder(options)
|
||||||
case "server":
|
case "server":
|
||||||
builder = newServerBuilder(options)
|
builder = newServerBuilder(options)
|
||||||
|
case "dev":
|
||||||
|
builder = newDesktopBuilder(options)
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
||||||
}
|
}
|
||||||
@@ -104,7 +106,7 @@ func Build(options *Options) (string, error) {
|
|||||||
// return "", err
|
// return "", err
|
||||||
// }
|
// }
|
||||||
if !options.IgnoreFrontend {
|
if !options.IgnoreFrontend {
|
||||||
outputLogger.Println(" - Building Wails Frontend")
|
outputLogger.Println(" - Building Project Frontend")
|
||||||
err = builder.BuildFrontend(outputLogger)
|
err = builder.BuildFrontend(outputLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// Modifier is actually a string
|
// Modifier is actually a string
|
||||||
type Modifier string
|
type Modifier string
|
||||||
|
|
||||||
@@ -16,6 +21,23 @@ const (
|
|||||||
ControlKey Modifier = "Control"
|
ControlKey Modifier = "Control"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var modifierMap = map[string]Modifier{
|
||||||
|
"cmdorctrl": CmdOrCtrlKey,
|
||||||
|
"optionoralt": OptionOrAltKey,
|
||||||
|
"shift": ShiftKey,
|
||||||
|
"super": SuperKey,
|
||||||
|
"ctrl": ControlKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseModifier(text string) (*Modifier, error) {
|
||||||
|
result, valid := modifierMap[text]
|
||||||
|
if !valid {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid modifier", text)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Accelerator holds the keyboard shortcut for a menu item
|
// Accelerator holds the keyboard shortcut for a menu item
|
||||||
type Accelerator struct {
|
type Accelerator struct {
|
||||||
Key string
|
Key string
|
||||||
@@ -25,14 +47,14 @@ type Accelerator struct {
|
|||||||
// Key creates a standard key Accelerator
|
// Key creates a standard key Accelerator
|
||||||
func Key(key string) *Accelerator {
|
func Key(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator
|
// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator
|
||||||
func CmdOrCtrl(key string) *Accelerator {
|
func CmdOrCtrl(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{CmdOrCtrlKey},
|
Modifiers: []Modifier{CmdOrCtrlKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +62,7 @@ func CmdOrCtrl(key string) *Accelerator {
|
|||||||
// OptionOrAlt creates a 'OptionOrAlt' Accelerator
|
// OptionOrAlt creates a 'OptionOrAlt' Accelerator
|
||||||
func OptionOrAlt(key string) *Accelerator {
|
func OptionOrAlt(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{OptionOrAltKey},
|
Modifiers: []Modifier{OptionOrAltKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +70,7 @@ func OptionOrAlt(key string) *Accelerator {
|
|||||||
// Shift creates a 'Shift' Accelerator
|
// Shift creates a 'Shift' Accelerator
|
||||||
func Shift(key string) *Accelerator {
|
func Shift(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{ShiftKey},
|
Modifiers: []Modifier{ShiftKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +78,7 @@ func Shift(key string) *Accelerator {
|
|||||||
// Control creates a 'Control' Accelerator
|
// Control creates a 'Control' Accelerator
|
||||||
func Control(key string) *Accelerator {
|
func Control(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{ControlKey},
|
Modifiers: []Modifier{ControlKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,7 +86,7 @@ func Control(key string) *Accelerator {
|
|||||||
// Super creates a 'Super' Accelerator
|
// Super creates a 'Super' Accelerator
|
||||||
func Super(key string) *Accelerator {
|
func Super(key string) *Accelerator {
|
||||||
return &Accelerator{
|
return &Accelerator{
|
||||||
Key: key,
|
Key: strings.ToLower(key),
|
||||||
Modifiers: []Modifier{SuperKey},
|
Modifiers: []Modifier{SuperKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
90
v2/pkg/menu/keys/parser.go
Normal file
90
v2/pkg/menu/keys/parser.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
|
)
|
||||||
|
|
||||||
|
var namedKeys = slicer.String([]string{"backspace", "tab", "return", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"})
|
||||||
|
|
||||||
|
func parseKey(key string) (string, bool) {
|
||||||
|
|
||||||
|
// Lowercase!
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
|
||||||
|
// Check special case
|
||||||
|
if key == "plus" {
|
||||||
|
return "+", true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle named keys
|
||||||
|
if namedKeys.Contains(key) {
|
||||||
|
return key, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check we only have a single character
|
||||||
|
if len(key) != 1 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
runeKey := rune(key[0])
|
||||||
|
|
||||||
|
// This may be too inclusive
|
||||||
|
if strconv.IsPrint(runeKey) {
|
||||||
|
return key, true
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(shortcut string) (*Accelerator, error) {
|
||||||
|
|
||||||
|
var result Accelerator
|
||||||
|
|
||||||
|
// Split the shortcut by +
|
||||||
|
components := strings.Split(shortcut, "+")
|
||||||
|
|
||||||
|
// If we only have one it should be a key
|
||||||
|
// We require components
|
||||||
|
if len(components) == 0 {
|
||||||
|
return nil, fmt.Errorf("no components given to validateComponents")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep track of modifiers we have processed
|
||||||
|
var modifiersProcessed slicer.StringSlicer
|
||||||
|
|
||||||
|
// Check components
|
||||||
|
for index, component := range components {
|
||||||
|
|
||||||
|
// If last component
|
||||||
|
if index == len(components)-1 {
|
||||||
|
processedkey, validKey := parseKey(component)
|
||||||
|
if !validKey {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid key", component)
|
||||||
|
}
|
||||||
|
result.Key = processedkey
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not last component - needs to be modifier
|
||||||
|
lowercaseComponent := strings.ToLower(component)
|
||||||
|
thisModifier, valid := modifierMap[lowercaseComponent]
|
||||||
|
if !valid {
|
||||||
|
return nil, fmt.Errorf("'%s' is not a valid modifier", component)
|
||||||
|
}
|
||||||
|
// Needs to be unique
|
||||||
|
if modifiersProcessed.Contains(lowercaseComponent) {
|
||||||
|
return nil, fmt.Errorf("Modifier '%s' is defined twice for shortcut: %s", component, shortcut)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save this data
|
||||||
|
result.Modifiers = append(result.Modifiers, thisModifier)
|
||||||
|
modifiersProcessed.Add(lowercaseComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
38
v2/pkg/menu/keys/parser_test.go
Normal file
38
v2/pkg/menu/keys/parser_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
|
||||||
|
i := is.New(t)
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
Input string
|
||||||
|
Expected *Accelerator
|
||||||
|
}
|
||||||
|
|
||||||
|
gooddata := []args{
|
||||||
|
{"CmdOrCtrl+A", CmdOrCtrl("A")},
|
||||||
|
{"SHIFT+.", Shift(".")},
|
||||||
|
{"CTRL+plus", Control("+")},
|
||||||
|
{"CTRL+SHIFT+escApe", Combo("escape", ControlKey, ShiftKey)},
|
||||||
|
{";", Key(";")},
|
||||||
|
{"Super+Tab", Super("tab")},
|
||||||
|
{"OptionOrAlt+Page Down", OptionOrAlt("Page Down")},
|
||||||
|
}
|
||||||
|
for _, tt := range gooddata {
|
||||||
|
result, err := Parse(tt.Input)
|
||||||
|
i.NoErr(err)
|
||||||
|
i.Equal(result, tt.Expected)
|
||||||
|
}
|
||||||
|
baddata := []string{"CmdOrCrl+A", "SHIT+.", "CTL+plus", "CTRL+SHIF+esApe", "escap", "Sper+Tab", "OptionOrAlt"}
|
||||||
|
for _, d := range baddata {
|
||||||
|
result, err := Parse(d)
|
||||||
|
i.True(err != nil)
|
||||||
|
i.Equal(result, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package menu
|
package menu
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MenuItem represents a menuitem contained in a menu
|
// MenuItem represents a menuitem contained in a menu
|
||||||
@@ -28,11 +29,21 @@ type MenuItem struct {
|
|||||||
// Callback function when menu clicked
|
// Callback function when menu clicked
|
||||||
Click Callback `json:"-"`
|
Click Callback `json:"-"`
|
||||||
|
|
||||||
// Foreground colour in hex RGBA format EG: 0xFF0000FF = #FF0000FF = red
|
// Colour
|
||||||
Foreground int
|
RGBA string
|
||||||
|
|
||||||
// Background colour
|
// Font
|
||||||
Background int
|
FontSize int
|
||||||
|
FontName string
|
||||||
|
|
||||||
|
// Image - base64 image data
|
||||||
|
Image string
|
||||||
|
|
||||||
|
// MacTemplateImage indicates that on a mac, this image is a template image
|
||||||
|
MacTemplateImage bool
|
||||||
|
|
||||||
|
// Tooltip
|
||||||
|
Tooltip string
|
||||||
|
|
||||||
// This holds the menu item's parent.
|
// This holds the menu item's parent.
|
||||||
parent *MenuItem
|
parent *MenuItem
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package options
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
wailsruntime "github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"log"
|
"log"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
wailsruntime "github.com/wailsapp/wails/v2/internal/runtime"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||||
@@ -13,26 +14,28 @@ import (
|
|||||||
|
|
||||||
// App contains options for creating the App
|
// App contains options for creating the App
|
||||||
type App struct {
|
type App struct {
|
||||||
Title string
|
Title string
|
||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
DisableResize bool
|
DisableResize bool
|
||||||
Fullscreen bool
|
Fullscreen bool
|
||||||
MinWidth int
|
MinWidth int
|
||||||
MinHeight int
|
MinHeight int
|
||||||
MaxWidth int
|
MaxWidth int
|
||||||
MaxHeight int
|
MaxHeight int
|
||||||
StartHidden bool
|
StartHidden bool
|
||||||
DevTools bool
|
HideWindowOnClose bool
|
||||||
RGBA int
|
DevTools bool
|
||||||
ContextMenus []*menu.ContextMenu
|
RGBA int
|
||||||
TrayMenus []*menu.TrayMenu
|
ContextMenus []*menu.ContextMenu
|
||||||
Menu *menu.Menu
|
TrayMenus []*menu.TrayMenu
|
||||||
Mac *mac.Options
|
Menu *menu.Menu
|
||||||
Logger logger.Logger `json:"-"`
|
Mac *mac.Options
|
||||||
LogLevel logger.LogLevel
|
Logger logger.Logger `json:"-"`
|
||||||
Startup func(*wailsruntime.Runtime) `json:"-"`
|
LogLevel logger.LogLevel
|
||||||
Shutdown func() `json:"-"`
|
Startup func(*wailsruntime.Runtime) `json:"-"`
|
||||||
|
Shutdown func() `json:"-"`
|
||||||
|
Bind []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeDefaults will set the minimum default values for an application
|
// MergeDefaults will set the minimum default values for an application
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S
|
|||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
|||||||
19
v2/wails.go
19
v2/wails.go
@@ -14,19 +14,12 @@ type Runtime = runtime.Runtime
|
|||||||
// Store is an alias for the Store object
|
// Store is an alias for the Store object
|
||||||
type Store = runtime.Store
|
type Store = runtime.Store
|
||||||
|
|
||||||
// CreateAppWithOptions creates an application based on the given config
|
// Run creates an application based on the given config and executes it
|
||||||
func CreateAppWithOptions(options *options.App) (*app.App, error) {
|
func Run(options *options.App) error {
|
||||||
return app.CreateApp(options)
|
app, err := app.CreateApp(options)
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
// CreateApp creates an application based on the given title, width and height
|
|
||||||
func CreateApp(title string, width int, height int) (*app.App, error) {
|
|
||||||
|
|
||||||
options := &options.App{
|
|
||||||
Title: title,
|
|
||||||
Width: width,
|
|
||||||
Height: height,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.CreateApp(options)
|
return app.Run()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user