mirror of
https://github.com/taigrr/wails.git
synced 2026-04-04 22:22:41 -07:00
Compare commits
72 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
feee2b3db2 | ||
|
|
9889c2bdbb | ||
|
|
2432fccf71 | ||
|
|
70510fd180 | ||
|
|
17c6201469 | ||
|
|
0f209c8900 | ||
|
|
cbf043585c | ||
|
|
5ae621ceaa | ||
|
|
1231b59443 | ||
|
|
b18d4fbf41 | ||
|
|
9ec5605e63 | ||
|
|
98a4de8878 | ||
|
|
5fe709f558 | ||
|
|
5231a6893b | ||
|
|
74f3ce990f | ||
|
|
998a913853 | ||
|
|
964844835c | ||
|
|
4e152bb849 | ||
|
|
51133d098c | ||
|
|
d4191e7d1b | ||
|
|
9c273bc745 | ||
|
|
c6f6ad6beb | ||
|
|
4362a14459 | ||
|
|
0080e9e311 | ||
|
|
83d9297cac | ||
|
|
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 |
@@ -3,12 +3,12 @@
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"amd": true,
|
||||
"node": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2016,
|
||||
"sourceType": "module",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
|
||||
@@ -1 +1 @@
|
||||
bridge.js
|
||||
index.js
|
||||
@@ -5,33 +5,42 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
func LogGreen(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Green(text))
|
||||
}
|
||||
|
||||
func LogRed(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Red(text))
|
||||
}
|
||||
|
||||
func LogDarkYellow(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.DarkYellow(text))
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
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
|
||||
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 := "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 {
|
||||
|
||||
// Validate inputs
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
app.PrintBanner()
|
||||
@@ -64,7 +75,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
defer watcher.Close()
|
||||
|
||||
var debugBinaryProcess *process.Process = nil
|
||||
var buildFrontend bool = true
|
||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||
|
||||
// Setup signal handler
|
||||
@@ -75,8 +85,13 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Do initial build
|
||||
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) {
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
@@ -89,7 +104,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
if err != nil {
|
||||
logger.Fatal("%s", err.Error())
|
||||
}
|
||||
logger.Println("Watching directory: %s", event.Name)
|
||||
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -98,38 +113,33 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
// logger.Println("modified file: %s", event.Name)
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
if rebuild {
|
||||
// Only build frontend when the file isn't a Go file
|
||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if buildFrontend {
|
||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
||||
} else {
|
||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||
}
|
||||
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// 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 newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
@@ -139,23 +149,28 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
projectDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
dirs, err := fs.GetSubdirectories(projectDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
LogGreen("Watching (sub)/directory: %s", projectDir)
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
logger.Println("Watching directory: %s", dir)
|
||||
// Ignore build directory
|
||||
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
||||
return
|
||||
}
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
@@ -167,7 +182,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
for quit == false {
|
||||
select {
|
||||
case <-quitChannel:
|
||||
println()
|
||||
LogGreen("\nCaught quit")
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- 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
|
||||
})
|
||||
@@ -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()
|
||||
if err != nil {
|
||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
||||
return nil
|
||||
LogRed("Build error - continuing to run current version")
|
||||
LogDarkYellow(err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
logger.Println("Build new binary: %s", appBinary)
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
@@ -233,7 +248,7 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
||||
// TODO: Generate `backend.js`
|
||||
|
||||
// Start up new binary
|
||||
newProcess := process.NewProcess(logger, appBinary)
|
||||
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
@@ -244,13 +259,13 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
||||
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
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
@@ -262,7 +277,7 @@ func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, co
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
OutputFile: outputFile,
|
||||
IgnoreFrontend: !buildFrontend,
|
||||
IgnoreFrontend: true,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
||||
@@ -17,12 +22,18 @@ func fatal(message string) {
|
||||
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() {
|
||||
|
||||
var err error
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
app.SetBannerFunction(banner)
|
||||
|
||||
build.AddBuildSubcommand(app, os.Stdout)
|
||||
err = initialise.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
@@ -48,6 +59,11 @@ func main() {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = update.AddSubcommand(app, os.Stdout, version)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
println("\n\nERROR: " + err.Error())
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v2.0.0-alpha.16"
|
||||
var version = "v2.0.0-alpha.36"
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
module github.com/wailsapp/wails/v2
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/jackmordaunt/icns v1.0.0
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
@@ -13,16 +15,15 @@ require (
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/matryer/is v1.4.0
|
||||
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/pkg/errors v0.9.1
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/wzshiming/ctc v1.2.3
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
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
|
||||
)
|
||||
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
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/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
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/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||
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/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
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/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e 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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/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/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
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/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
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/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||
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/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-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-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-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-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/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-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/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -4,8 +4,9 @@ package app
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
)
|
||||
|
||||
// Init initialises the application for a debug environment
|
||||
@@ -19,10 +20,10 @@ func (a *App) Init() error {
|
||||
}
|
||||
|
||||
// Set log levels
|
||||
greeting := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
flag.Parse()
|
||||
if len(*greeting) > 0 {
|
||||
switch strings.ToLower(*greeting) {
|
||||
if len(*loglevel) > 0 {
|
||||
switch strings.ToLower(*loglevel) {
|
||||
case "trace":
|
||||
a.logger.SetLogLevel(logger.TRACE)
|
||||
case "info":
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build !desktop,!hybrid,!server
|
||||
// +build !desktop,!hybrid,!server,!dev
|
||||
|
||||
package app
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
@@ -26,10 +29,10 @@ type App struct {
|
||||
options *options.App
|
||||
|
||||
// Subsystems
|
||||
log *subsystem.Log
|
||||
runtime *subsystem.Runtime
|
||||
event *subsystem.Event
|
||||
binding *subsystem.Binding
|
||||
log *subsystem.Log
|
||||
runtime *subsystem.Runtime
|
||||
event *subsystem.Event
|
||||
//binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
menu *subsystem.Menu
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
@@ -81,12 +84,15 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
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{
|
||||
appType: "desktop",
|
||||
window: window,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger, appoptions.Bind),
|
||||
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
||||
menuManager: menuManager,
|
||||
startupCallback: appoptions.Startup,
|
||||
shutdownCallback: appoptions.Shutdown,
|
||||
@@ -106,13 +112,10 @@ func (a *App) Run() error {
|
||||
|
||||
var err error
|
||||
|
||||
// Setup signal handler
|
||||
signalsubsystem, err := signal.NewManager(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signalsubsystem
|
||||
a.signal.Start()
|
||||
// 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()
|
||||
@@ -121,7 +124,7 @@ func (a *App) Run() error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -135,17 +138,6 @@ func (a *App) Run() error {
|
||||
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 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
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||
if err != nil {
|
||||
@@ -169,18 +161,18 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
// Start the eventing subsystem
|
||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.event = event
|
||||
a.event = eventsubsystem
|
||||
err = a.event.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@@ -191,11 +183,11 @@ func (a *App) Run() error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
a.call = call
|
||||
a.call = callSubsystem
|
||||
err = a.call.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -207,12 +199,42 @@ func (a *App) Run() error {
|
||||
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")
|
||||
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
|
||||
}
|
||||
|
||||
return result
|
||||
// Shutdown callback
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback()
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
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,23 +2,38 @@ package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
type Bindings struct {
|
||||
db *DB
|
||||
logger logger.CustomLogger
|
||||
db *DB
|
||||
logger logger.CustomLogger
|
||||
exemptions slicer.StringSlicer
|
||||
}
|
||||
|
||||
// NewBindings returns a new Bindings object
|
||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}) *Bindings {
|
||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
||||
result := &Bindings{
|
||||
db: newDB(),
|
||||
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)
|
||||
@@ -33,7 +48,7 @@ func NewBindings(logger *logger.Logger, structPointersToBind []interface{}) *Bin
|
||||
// Add the given struct methods to the Bindings
|
||||
func (b *Bindings) Add(structPtr interface{}) error {
|
||||
|
||||
methods, err := getMethods(structPtr)
|
||||
methods, err := b.getMethods(structPtr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
||||
}
|
||||
@@ -46,7 +61,6 @@ func (b *Bindings) Add(structPtr interface{}) error {
|
||||
|
||||
// Add it as a regular method
|
||||
b.db.AddMethod(packageName, structName, methodName, method)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ func (b *BoundMethod) OutputCount() int {
|
||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
||||
|
||||
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 {
|
||||
typ := b.Inputs[index].reflectType
|
||||
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
|
||||
}
|
||||
|
||||
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
|
||||
// Create result placeholder
|
||||
var result []*BoundMethod
|
||||
@@ -56,6 +56,11 @@ func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
fullMethodName := baseName + "." + methodName
|
||||
method := structValue.MethodByName(methodName)
|
||||
|
||||
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
||||
if b.exemptions.Contains(methodReflectName) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create new method
|
||||
boundMethod := &BoundMethod{
|
||||
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
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"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)
|
||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
||||
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
|
||||
a.app = (*C.struct_Application)(app)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdio.h>
|
||||
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 SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
||||
extern void Run(struct Application*, int argc, char **argv);
|
||||
|
||||
@@ -12,9 +12,10 @@ package ffenestri
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
"strconv"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
|
||||
"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))
|
||||
}
|
||||
|
||||
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
|
||||
func (c *Client) WindowSetColour(colour int) {
|
||||
r, g, b, a := intToColour(colour)
|
||||
|
||||
@@ -34,6 +34,12 @@ BOOL yes(id self, SEL cmd)
|
||||
return YES;
|
||||
}
|
||||
|
||||
// no command simply returns NO!
|
||||
BOOL no(id self, SEL cmd)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Prints a hashmap entry
|
||||
int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
||||
printf("%s: %p ", (char*)e->key, e->data);
|
||||
@@ -65,6 +71,7 @@ struct Application {
|
||||
// Cocoa data
|
||||
id application;
|
||||
id delegate;
|
||||
id windowDelegate;
|
||||
id mainWindow;
|
||||
id wkwebview;
|
||||
id manager;
|
||||
@@ -72,6 +79,7 @@ struct Application {
|
||||
id mouseEvent;
|
||||
id mouseDownMonitor;
|
||||
id mouseUpMonitor;
|
||||
int activationPolicy;
|
||||
|
||||
// Window Data
|
||||
const char *title;
|
||||
@@ -92,6 +100,7 @@ struct Application {
|
||||
const char *appearance;
|
||||
int decorations;
|
||||
int logLevel;
|
||||
int hideWindowOnClose;
|
||||
|
||||
// Features
|
||||
int frame;
|
||||
@@ -120,6 +129,9 @@ struct Application {
|
||||
// Bindings
|
||||
const char *bindings;
|
||||
|
||||
// shutting down flag
|
||||
bool shuttingDown;
|
||||
|
||||
};
|
||||
|
||||
// Debug works like sprintf but mutes if the global debug flag is true
|
||||
@@ -140,6 +152,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, ... ) {
|
||||
const char *temp = concat("LFFfenestri (C) | ", message);
|
||||
va_list args;
|
||||
@@ -212,6 +234,9 @@ void applyWindowColour(struct Application *app) {
|
||||
}
|
||||
|
||||
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->green = green;
|
||||
app->blue = blue;
|
||||
@@ -225,12 +250,18 @@ void FullSizeContent(struct Application *app) {
|
||||
}
|
||||
|
||||
void Hide(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
msg(app->application, s("hide:"))
|
||||
msg(app->application, s("hide:"));
|
||||
);
|
||||
}
|
||||
|
||||
void Show(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
msg(app->mainWindow, s("makeKeyAndOrderFront:"), NULL);
|
||||
msg(app->application, s("activateIgnoringOtherApps:"), YES);
|
||||
@@ -246,7 +277,10 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
||||
struct Application *app = (struct Application *)objc_getAssociatedObject(
|
||||
self, "application");
|
||||
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
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
||||
|
||||
@@ -409,6 +443,7 @@ void freeDialogIconCache(struct Application *app) {
|
||||
}
|
||||
|
||||
void DestroyApplication(struct Application *app) {
|
||||
app->shuttingDown = true;
|
||||
Debug(app, "Destroying Application");
|
||||
|
||||
// Free the bindings
|
||||
@@ -450,24 +485,25 @@ void DestroyApplication(struct Application *app) {
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("contextMenu"));
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("windowDrag"));
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("external"));
|
||||
msg(app->manager, s("removeScriptMessageHandlerForName:"), str("error"));
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// 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
|
||||
void SetTitle(struct Application *app, const char *title) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "SetTitle Called");
|
||||
ON_MAIN_THREAD(
|
||||
msg(app->mainWindow, s("setTitle:"), str(title));
|
||||
@@ -489,6 +525,9 @@ bool isFullScreen(struct Application *app) {
|
||||
|
||||
// Fullscreen sets the main window to be fullscreen
|
||||
void Fullscreen(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "Fullscreen Called");
|
||||
if( ! isFullScreen(app) ) {
|
||||
ToggleFullscreen(app);
|
||||
@@ -497,6 +536,9 @@ void Fullscreen(struct Application *app) {
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
void UnFullscreen(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "UnFullscreen Called");
|
||||
if( isFullScreen(app) ) {
|
||||
ToggleFullscreen(app);
|
||||
@@ -504,6 +546,9 @@ void UnFullscreen(struct Application *app) {
|
||||
}
|
||||
|
||||
void Center(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "Center Called");
|
||||
ON_MAIN_THREAD(
|
||||
MAIN_WINDOW_CALL("center");
|
||||
@@ -518,23 +563,35 @@ void ToggleMaximise(struct Application *app) {
|
||||
}
|
||||
|
||||
void Maximise(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
if( app->maximised == 0) {
|
||||
ToggleMaximise(app);
|
||||
}
|
||||
}
|
||||
|
||||
void Unmaximise(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
if( app->maximised == 1) {
|
||||
ToggleMaximise(app);
|
||||
}
|
||||
}
|
||||
|
||||
void Minimise(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
MAIN_WINDOW_CALL("miniaturize:");
|
||||
);
|
||||
}
|
||||
void Unminimise(struct Application *app) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
MAIN_WINDOW_CALL("deminiaturize:");
|
||||
);
|
||||
@@ -558,6 +615,9 @@ void dumpFrame(struct Application *app, const char *message, CGRect frame) {
|
||||
}
|
||||
|
||||
void SetSize(struct Application *app, int width, int height) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
id screen = getCurrentScreen(app);
|
||||
|
||||
@@ -574,6 +634,9 @@ void SetSize(struct Application *app, int width, int height) {
|
||||
}
|
||||
|
||||
void SetPosition(struct Application *app, int x, int y) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
id screen = getCurrentScreen(app);
|
||||
CGRect screenFrame = GET_FRAME(screen);
|
||||
@@ -599,6 +662,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) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
id alert = ALLOC_INIT("NSAlert");
|
||||
char *dialogType = type;
|
||||
@@ -695,7 +761,7 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
||||
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 *header = concat(callback, "|");
|
||||
const char *responseMessage = concat(header, buttonPressed);
|
||||
@@ -712,6 +778,9 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
||||
|
||||
// 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) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "OpenDialog Called with callback id: %s", callbackID);
|
||||
|
||||
// Create an open panel
|
||||
@@ -800,6 +869,9 @@ void OpenDialog(struct Application *app, char *callbackID, char *title, char *fi
|
||||
|
||||
// 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) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
Debug(app, "SaveDialog Called with callback id: %s", callbackID);
|
||||
|
||||
// Create an open panel
|
||||
@@ -877,6 +949,9 @@ void DisableFrame(struct Application *app)
|
||||
|
||||
void setMinMaxSize(struct Application *app)
|
||||
{
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
if (app->maxHeight > 0 && app->maxWidth > 0)
|
||||
{
|
||||
msg(app->mainWindow, s("setMaxSize:"), CGSizeMake(app->maxWidth, app->maxHeight));
|
||||
@@ -885,10 +960,27 @@ void setMinMaxSize(struct Application *app)
|
||||
{
|
||||
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)
|
||||
{
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
app->minWidth = minWidth;
|
||||
app->minHeight = minHeight;
|
||||
|
||||
@@ -902,6 +994,9 @@ void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
||||
|
||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||
{
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
app->maxWidth = maxWidth;
|
||||
app->maxHeight = maxHeight;
|
||||
|
||||
@@ -922,24 +1017,40 @@ void SetDebug(void *applicationPointer, int flag) {
|
||||
|
||||
// AddContextMenu sets the context menu map for this application
|
||||
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) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
||||
}
|
||||
|
||||
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
||||
}
|
||||
|
||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
||||
);
|
||||
}
|
||||
|
||||
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
||||
);
|
||||
@@ -1002,10 +1113,13 @@ void processDecorations(struct Application *app) {
|
||||
void createApplication(struct Application *app) {
|
||||
id application = msg(c("NSApplication"), s("sharedApplication"));
|
||||
app->application = application;
|
||||
msg(application, s("setActivationPolicy:"), 0);
|
||||
msg(application, s("setActivationPolicy:"), app->activationPolicy);
|
||||
}
|
||||
|
||||
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
ON_MAIN_THREAD(
|
||||
const char *result = isDarkMode(app) ? "T" : "F";
|
||||
|
||||
@@ -1026,9 +1140,9 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||
void createDelegate(struct Application *app) {
|
||||
// Define delegate
|
||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) yes, "c@:@");
|
||||
class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||
// class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||
|
||||
// All Menu Items use a common callback
|
||||
@@ -1054,6 +1168,11 @@ void createDelegate(struct Application *app) {
|
||||
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) {
|
||||
// Create main window
|
||||
id mainWindow = ALLOC("NSWindow");
|
||||
@@ -1072,6 +1191,15 @@ void createMainWindow(struct Application *app) {
|
||||
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1158,7 +1286,7 @@ void parseMenuRole(struct Application *app, id parentMenu, JsonNode *item) {
|
||||
return;
|
||||
}
|
||||
if ( STREQ(roleName, "quit")) {
|
||||
addMenuItem(parentMenu, "Quit (More work TBD)", "terminate:", "q", FALSE);
|
||||
addMenuItem(parentMenu, "Quit", "terminate:", "q", FALSE);
|
||||
return;
|
||||
}
|
||||
if ( STREQ(roleName, "togglefullscreen")) {
|
||||
@@ -1436,6 +1564,9 @@ void updateMenu(struct Application *app, const char *menuAsJSON) {
|
||||
|
||||
// SetApplicationMenu sets the initial menu for the application
|
||||
void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
||||
// Guard against calling during shutdown
|
||||
if( app->shuttingDown ) return;
|
||||
|
||||
if ( app->applicationMenu == NULL ) {
|
||||
app->applicationMenu = NewApplicationMenu(menuAsJSON);
|
||||
return;
|
||||
@@ -1534,6 +1665,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
||||
id manager = msg(config, s("userContentController"));
|
||||
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("error"));
|
||||
app->manager = manager;
|
||||
|
||||
id wkwebview = msg(c("WKWebView"), s("alloc"));
|
||||
@@ -1676,11 +1808,26 @@ void Run(struct Application *app, int argc, char **argv) {
|
||||
Debug(app, "Run called");
|
||||
msg(app->application, s("run"));
|
||||
|
||||
DestroyApplication(app);
|
||||
|
||||
MEMFREE(internalCode);
|
||||
}
|
||||
|
||||
void SetActivationPolicy(struct Application* app, int policy) {
|
||||
app->activationPolicy = policy;
|
||||
}
|
||||
|
||||
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel) {
|
||||
// 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, int hideWindowOnClose) {
|
||||
|
||||
// Load the tray icons
|
||||
LoadTrayIcons();
|
||||
@@ -1701,6 +1848,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
||||
result->startHidden = startHidden;
|
||||
result->decorations = 0;
|
||||
result->logLevel = logLevel;
|
||||
result->hideWindowOnClose = hideWindowOnClose;
|
||||
|
||||
result->mainWindow = NULL;
|
||||
result->mouseEvent = NULL;
|
||||
@@ -1729,12 +1877,19 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
||||
// Context Menus
|
||||
result->contextMenuStore = NewContextMenuStore();
|
||||
|
||||
// Window delegate
|
||||
result->windowDelegate = NULL;
|
||||
|
||||
// Window Appearance
|
||||
result->titlebarAppearsTransparent = 0;
|
||||
result->webviewIsTranparent = 0;
|
||||
|
||||
result->sendMessageToBackend = (ffenestriCallback) messageFromWindowCallback;
|
||||
|
||||
result->shuttingDown = false;
|
||||
|
||||
result->activationPolicy = NSApplicationActivationPolicyRegular;
|
||||
|
||||
return (void*) result;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ func (a *Application) processPlatformSettings() error {
|
||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||
}
|
||||
|
||||
// Set activation policy
|
||||
C.SetActivationPolicy(a.app, C.int(mac.ActivationPolicy))
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
|
||||
@@ -66,6 +66,10 @@
|
||||
#define NSControlStateValueOff 0
|
||||
#define NSControlStateValueOn 1
|
||||
|
||||
#define NSApplicationActivationPolicyRegular 0
|
||||
#define NSApplicationActivationPolicyAccessory 1
|
||||
#define NSApplicationActivationPolicyProhibited 2
|
||||
|
||||
// Unbelievably, if the user swaps their button preference
|
||||
// then right buttons are reported as left buttons
|
||||
#define NSEventMaskLeftMouseDown 1 << 1
|
||||
@@ -110,6 +114,8 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
||||
//void SetContextMenus(struct Application* app, const char *);
|
||||
void AddTrayMenu(struct Application* app, const char *);
|
||||
|
||||
void SetActivationPolicy(struct Application* app, int policy);
|
||||
|
||||
void* lookupStringConstant(id constantName);
|
||||
|
||||
#endif
|
||||
@@ -180,151 +180,151 @@ id processAcceleratorKey(const char *key) {
|
||||
return str("");
|
||||
}
|
||||
|
||||
if( STREQ(key, "Backspace") ) {
|
||||
if( STREQ(key, "backspace") ) {
|
||||
return strunicode(0x0008);
|
||||
}
|
||||
if( STREQ(key, "Tab") ) {
|
||||
if( STREQ(key, "tab") ) {
|
||||
return strunicode(0x0009);
|
||||
}
|
||||
if( STREQ(key, "Return") ) {
|
||||
if( STREQ(key, "return") ) {
|
||||
return strunicode(0x000d);
|
||||
}
|
||||
if( STREQ(key, "Escape") ) {
|
||||
if( STREQ(key, "escape") ) {
|
||||
return strunicode(0x001b);
|
||||
}
|
||||
if( STREQ(key, "Left") ) {
|
||||
if( STREQ(key, "left") ) {
|
||||
return strunicode(0x001c);
|
||||
}
|
||||
if( STREQ(key, "Right") ) {
|
||||
if( STREQ(key, "right") ) {
|
||||
return strunicode(0x001d);
|
||||
}
|
||||
if( STREQ(key, "Up") ) {
|
||||
if( STREQ(key, "up") ) {
|
||||
return strunicode(0x001e);
|
||||
}
|
||||
if( STREQ(key, "Down") ) {
|
||||
if( STREQ(key, "down") ) {
|
||||
return strunicode(0x001f);
|
||||
}
|
||||
if( STREQ(key, "Space") ) {
|
||||
if( STREQ(key, "space") ) {
|
||||
return strunicode(0x0020);
|
||||
}
|
||||
if( STREQ(key, "Delete") ) {
|
||||
if( STREQ(key, "delete") ) {
|
||||
return strunicode(0x007f);
|
||||
}
|
||||
if( STREQ(key, "Home") ) {
|
||||
if( STREQ(key, "home") ) {
|
||||
return strunicode(0x2196);
|
||||
}
|
||||
if( STREQ(key, "End") ) {
|
||||
if( STREQ(key, "end") ) {
|
||||
return strunicode(0x2198);
|
||||
}
|
||||
if( STREQ(key, "Page Up") ) {
|
||||
if( STREQ(key, "page up") ) {
|
||||
return strunicode(0x21de);
|
||||
}
|
||||
if( STREQ(key, "Page Down") ) {
|
||||
if( STREQ(key, "page down") ) {
|
||||
return strunicode(0x21df);
|
||||
}
|
||||
if( STREQ(key, "F1") ) {
|
||||
if( STREQ(key, "f1") ) {
|
||||
return strunicode(0xf704);
|
||||
}
|
||||
if( STREQ(key, "F2") ) {
|
||||
if( STREQ(key, "f2") ) {
|
||||
return strunicode(0xf705);
|
||||
}
|
||||
if( STREQ(key, "F3") ) {
|
||||
if( STREQ(key, "f3") ) {
|
||||
return strunicode(0xf706);
|
||||
}
|
||||
if( STREQ(key, "F4") ) {
|
||||
if( STREQ(key, "f4") ) {
|
||||
return strunicode(0xf707);
|
||||
}
|
||||
if( STREQ(key, "F5") ) {
|
||||
if( STREQ(key, "f5") ) {
|
||||
return strunicode(0xf708);
|
||||
}
|
||||
if( STREQ(key, "F6") ) {
|
||||
if( STREQ(key, "f6") ) {
|
||||
return strunicode(0xf709);
|
||||
}
|
||||
if( STREQ(key, "F7") ) {
|
||||
if( STREQ(key, "f7") ) {
|
||||
return strunicode(0xf70a);
|
||||
}
|
||||
if( STREQ(key, "F8") ) {
|
||||
if( STREQ(key, "f8") ) {
|
||||
return strunicode(0xf70b);
|
||||
}
|
||||
if( STREQ(key, "F9") ) {
|
||||
if( STREQ(key, "f9") ) {
|
||||
return strunicode(0xf70c);
|
||||
}
|
||||
if( STREQ(key, "F10") ) {
|
||||
if( STREQ(key, "f10") ) {
|
||||
return strunicode(0xf70d);
|
||||
}
|
||||
if( STREQ(key, "F11") ) {
|
||||
if( STREQ(key, "f11") ) {
|
||||
return strunicode(0xf70e);
|
||||
}
|
||||
if( STREQ(key, "F12") ) {
|
||||
if( STREQ(key, "f12") ) {
|
||||
return strunicode(0xf70f);
|
||||
}
|
||||
if( STREQ(key, "F13") ) {
|
||||
if( STREQ(key, "f13") ) {
|
||||
return strunicode(0xf710);
|
||||
}
|
||||
if( STREQ(key, "F14") ) {
|
||||
if( STREQ(key, "f14") ) {
|
||||
return strunicode(0xf711);
|
||||
}
|
||||
if( STREQ(key, "F15") ) {
|
||||
if( STREQ(key, "f15") ) {
|
||||
return strunicode(0xf712);
|
||||
}
|
||||
if( STREQ(key, "F16") ) {
|
||||
if( STREQ(key, "f16") ) {
|
||||
return strunicode(0xf713);
|
||||
}
|
||||
if( STREQ(key, "F17") ) {
|
||||
if( STREQ(key, "f17") ) {
|
||||
return strunicode(0xf714);
|
||||
}
|
||||
if( STREQ(key, "F18") ) {
|
||||
if( STREQ(key, "f18") ) {
|
||||
return strunicode(0xf715);
|
||||
}
|
||||
if( STREQ(key, "F19") ) {
|
||||
if( STREQ(key, "f19") ) {
|
||||
return strunicode(0xf716);
|
||||
}
|
||||
if( STREQ(key, "F20") ) {
|
||||
if( STREQ(key, "f20") ) {
|
||||
return strunicode(0xf717);
|
||||
}
|
||||
if( STREQ(key, "F21") ) {
|
||||
if( STREQ(key, "f21") ) {
|
||||
return strunicode(0xf718);
|
||||
}
|
||||
if( STREQ(key, "F22") ) {
|
||||
if( STREQ(key, "f22") ) {
|
||||
return strunicode(0xf719);
|
||||
}
|
||||
if( STREQ(key, "F23") ) {
|
||||
if( STREQ(key, "f23") ) {
|
||||
return strunicode(0xf71a);
|
||||
}
|
||||
if( STREQ(key, "F24") ) {
|
||||
if( STREQ(key, "f24") ) {
|
||||
return strunicode(0xf71b);
|
||||
}
|
||||
if( STREQ(key, "F25") ) {
|
||||
if( STREQ(key, "f25") ) {
|
||||
return strunicode(0xf71c);
|
||||
}
|
||||
if( STREQ(key, "F26") ) {
|
||||
if( STREQ(key, "f26") ) {
|
||||
return strunicode(0xf71d);
|
||||
}
|
||||
if( STREQ(key, "F27") ) {
|
||||
if( STREQ(key, "f27") ) {
|
||||
return strunicode(0xf71e);
|
||||
}
|
||||
if( STREQ(key, "F28") ) {
|
||||
if( STREQ(key, "f28") ) {
|
||||
return strunicode(0xf71f);
|
||||
}
|
||||
if( STREQ(key, "F29") ) {
|
||||
if( STREQ(key, "f29") ) {
|
||||
return strunicode(0xf720);
|
||||
}
|
||||
if( STREQ(key, "F30") ) {
|
||||
if( STREQ(key, "f30") ) {
|
||||
return strunicode(0xf721);
|
||||
}
|
||||
if( STREQ(key, "F31") ) {
|
||||
if( STREQ(key, "f31") ) {
|
||||
return strunicode(0xf722);
|
||||
}
|
||||
if( STREQ(key, "F32") ) {
|
||||
if( STREQ(key, "f32") ) {
|
||||
return strunicode(0xf723);
|
||||
}
|
||||
if( STREQ(key, "F33") ) {
|
||||
if( STREQ(key, "f33") ) {
|
||||
return strunicode(0xf724);
|
||||
}
|
||||
if( STREQ(key, "F34") ) {
|
||||
if( STREQ(key, "f34") ) {
|
||||
return strunicode(0xf725);
|
||||
}
|
||||
if( STREQ(key, "F35") ) {
|
||||
if( STREQ(key, "f35") ) {
|
||||
return strunicode(0xf726);
|
||||
}
|
||||
// if( STREQ(key, "Insert") ) {
|
||||
@@ -336,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
||||
// if( STREQ(key, "ScrollLock") ) {
|
||||
// return strunicode(0xf72f);
|
||||
// }
|
||||
if( STREQ(key, "NumLock") ) {
|
||||
if( STREQ(key, "numLock") ) {
|
||||
return strunicode(0xf739);
|
||||
}
|
||||
|
||||
@@ -509,19 +509,19 @@ unsigned long parseModifiers(const char **modifiers) {
|
||||
int count = 0;
|
||||
while( thisModifier != NULL ) {
|
||||
// Determine flags
|
||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
||||
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
||||
if( STREQ(thisModifier, "optionoralt") ) {
|
||||
result |= NSEventModifierFlagOption;
|
||||
}
|
||||
if( STREQ(thisModifier, "Shift") ) {
|
||||
if( STREQ(thisModifier, "shift") ) {
|
||||
result |= NSEventModifierFlagShift;
|
||||
}
|
||||
if( STREQ(thisModifier, "Super") ) {
|
||||
if( STREQ(thisModifier, "super") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "Control") ) {
|
||||
if( STREQ(thisModifier, "control") ) {
|
||||
result |= NSEventModifierFlagControl;
|
||||
}
|
||||
count++;
|
||||
@@ -575,7 +575,7 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
||||
return item;
|
||||
}
|
||||
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA) {
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
|
||||
// Create a MenuItemCallbackData
|
||||
@@ -598,6 +598,9 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -740,7 +743,10 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
const char *image = getJSONString(item, "Image");
|
||||
const char *fontName = getJSONString(item, "FontName");
|
||||
const char *RGBA = getJSONString(item, "RGBA");
|
||||
int fontSize = 0;
|
||||
bool templateImage = false;
|
||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||
|
||||
int fontSize = 12;
|
||||
getJSONInt(item, "FontSize", &fontSize);
|
||||
|
||||
// If we have an accelerator
|
||||
@@ -775,7 +781,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
if( type != NULL ) {
|
||||
|
||||
if( STREQ(type->string_, "Text")) {
|
||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA);
|
||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
||||
}
|
||||
else if ( STREQ(type->string_, "Separator")) {
|
||||
addSeparator(parentMenu);
|
||||
|
||||
@@ -22,7 +22,7 @@ typedef struct _NSRange {
|
||||
#define NSFontWeightUltraLight -0.8
|
||||
#define NSFontWeightThin -0.6
|
||||
#define NSFontWeightLight -0.4
|
||||
#define NSFontWeightRegular 0
|
||||
#define NSFontWeightRegular 0.0
|
||||
#define NSFontWeightMedium 0.23
|
||||
#define NSFontWeightSemibold 0.3
|
||||
#define NSFontWeightBold 0.4
|
||||
@@ -105,7 +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 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);
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage);
|
||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
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 (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
)
|
||||
@@ -34,7 +35,8 @@ type ProcessedMenuItem struct {
|
||||
FontName string `json:",omitempty"`
|
||||
|
||||
// Image - base64 image data
|
||||
Image string `json:",omitempty"`
|
||||
Image string `json:",omitempty"`
|
||||
MacTemplateImage bool `json:", omitempty"`
|
||||
|
||||
// Tooltip
|
||||
Tooltip string `json:",omitempty"`
|
||||
@@ -44,20 +46,21 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
|
||||
|
||||
ID := menuItemMap.menuItemToIDMap[menuItem]
|
||||
result := &ProcessedMenuItem{
|
||||
ID: ID,
|
||||
Label: menuItem.Label,
|
||||
Role: menuItem.Role,
|
||||
Accelerator: menuItem.Accelerator,
|
||||
Type: menuItem.Type,
|
||||
Disabled: menuItem.Disabled,
|
||||
Hidden: menuItem.Hidden,
|
||||
Checked: menuItem.Checked,
|
||||
SubMenu: nil,
|
||||
RGBA: menuItem.RGBA,
|
||||
FontSize: menuItem.FontSize,
|
||||
FontName: menuItem.FontName,
|
||||
Image: menuItem.Image,
|
||||
Tooltip: menuItem.Tooltip,
|
||||
ID: ID,
|
||||
Label: menuItem.Label,
|
||||
Role: menuItem.Role,
|
||||
Accelerator: menuItem.Accelerator,
|
||||
Type: menuItem.Type,
|
||||
Disabled: menuItem.Disabled,
|
||||
Hidden: menuItem.Hidden,
|
||||
Checked: menuItem.Checked,
|
||||
SubMenu: nil,
|
||||
RGBA: menuItem.RGBA,
|
||||
FontSize: menuItem.FontSize,
|
||||
FontName: menuItem.FontName,
|
||||
Image: menuItem.Image,
|
||||
MacTemplateImage: menuItem.MacTemplateImage,
|
||||
Tooltip: menuItem.Tooltip,
|
||||
}
|
||||
|
||||
if menuItem.SubMenu != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package messagedispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
@@ -26,6 +27,8 @@ type Client interface {
|
||||
WindowUnminimise()
|
||||
WindowPosition(x int, y int)
|
||||
WindowSize(width int, height int)
|
||||
WindowSetMinSize(width int, height int)
|
||||
WindowSetMaxSize(width int, height int)
|
||||
WindowFullscreen()
|
||||
WindowUnFullscreen()
|
||||
WindowSetColour(colour int)
|
||||
|
||||
@@ -27,7 +27,7 @@ func dialogMessageParser(message string) (*parsedMessage, error) {
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid dialog response message format: %+v", message)
|
||||
}
|
||||
callbackID := message[:idx+1]
|
||||
callbackID := message[:idx]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
switch dialogType {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package messagedispatcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
@@ -23,7 +25,6 @@ type Dispatcher struct {
|
||||
dialogChannel <-chan *servicebus.Message
|
||||
systemChannel <-chan *servicebus.Message
|
||||
menuChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
servicebus *servicebus.ServiceBus
|
||||
logger logger.CustomLogger
|
||||
@@ -31,6 +32,13 @@ type Dispatcher struct {
|
||||
// Clients
|
||||
clients map[string]*DispatchClient
|
||||
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.
|
||||
@@ -75,6 +83,9 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
result := &Dispatcher{
|
||||
servicebus: servicebus,
|
||||
eventChannel: eventChannel,
|
||||
@@ -86,6 +97,8 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
||||
dialogChannel: dialogChannel,
|
||||
systemChannel: systemChannel,
|
||||
menuChannel: menuChannel,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -96,15 +109,18 @@ func (d *Dispatcher) Start() error {
|
||||
|
||||
d.logger.Trace("Starting")
|
||||
|
||||
d.running = true
|
||||
d.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for d.running {
|
||||
defer d.logger.Trace("Shutdown")
|
||||
for {
|
||||
select {
|
||||
case <-d.ctx.Done():
|
||||
d.wg.Done()
|
||||
return
|
||||
case <-d.quitChannel:
|
||||
d.processQuit()
|
||||
d.running = false
|
||||
case resultMessage := <-d.resultChannel:
|
||||
d.processCallResult(resultMessage)
|
||||
case eventMessage := <-d.eventChannel:
|
||||
@@ -119,9 +135,6 @@ func (d *Dispatcher) Start() error {
|
||||
d.processMenuMessage(menuMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
d.shutdown()
|
||||
}()
|
||||
|
||||
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
|
||||
// and return a DispatchClient that the caller can use to send messages
|
||||
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
||||
@@ -349,6 +358,38 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
||||
for _, client := range d.clients {
|
||||
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:
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dispatcher) Close() {
|
||||
d.cancel()
|
||||
d.wg.Wait()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
@@ -16,11 +17,14 @@ type Process struct {
|
||||
|
||||
// NewProcess creates a new process struct
|
||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
return &Process{
|
||||
result := &Process{
|
||||
logger: logger,
|
||||
cmd: exec.Command(cmd, args...),
|
||||
exitChannel: make(chan bool, 1),
|
||||
}
|
||||
result.cmd.Stdout = os.Stdout
|
||||
result.cmd.Stderr = os.Stderr
|
||||
return result
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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)
|
||||
*running = false
|
||||
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 () {
|
||||
|
||||
// No timeout by default
|
||||
var timeout = 0;
|
||||
let timeout = 0;
|
||||
|
||||
// Actual function
|
||||
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 */
|
||||
import { SetBindings } from './bindings';
|
||||
import { Init } from './main';
|
||||
import {RaiseError} from '../desktop/darwin';
|
||||
|
||||
// Setup global error handler
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
@@ -21,7 +22,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
error: JSON.stringify(error),
|
||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
||||
};
|
||||
window.wails.Log.Error(JSON.stringify(errorMessage));
|
||||
RaiseError(errorMessage);
|
||||
};
|
||||
|
||||
// Initialise the Runtime
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Error } from './log';
|
||||
import { SendMessage } from 'ipc';
|
||||
|
||||
// Defines a single listener with a maximum number of times to callback
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -96,7 +97,7 @@ export function Once(eventName, callback) {
|
||||
function notifyListeners(eventData) {
|
||||
|
||||
// Get the event name
|
||||
var eventName = eventData.name;
|
||||
let eventName = eventData.name;
|
||||
|
||||
// Check if we have any listeners for this event
|
||||
if (eventListeners[eventName]) {
|
||||
@@ -110,7 +111,7 @@ function notifyListeners(eventData) {
|
||||
// Get next listener
|
||||
const listener = eventListeners[eventName][count];
|
||||
|
||||
var data = eventData.data;
|
||||
let data = eventData.data;
|
||||
|
||||
// Do the callback
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,4 @@ export function Init() {
|
||||
|
||||
// Do platform specific Init
|
||||
Platform.Init();
|
||||
|
||||
window.wailsloader.runtime = true;
|
||||
}
|
||||
@@ -25,6 +25,10 @@ export function SendMessage(message) {
|
||||
window.webkit.messageHandlers.external.postMessage(message);
|
||||
}
|
||||
|
||||
export function RaiseError(message) {
|
||||
window.webkit.messageHandlers.error.postMessage(message);
|
||||
}
|
||||
|
||||
export function Init() {
|
||||
|
||||
// Setup drag handler
|
||||
|
||||
@@ -27,7 +27,7 @@ export function Init() {
|
||||
// Setup drag handler
|
||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||
window.addEventListener('mousedown', function (e) {
|
||||
var currentElement = e.target;
|
||||
let currentElement = e.target;
|
||||
while (currentElement != null) {
|
||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||
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 */
|
||||
|
||||
import bridge from './bridge';
|
||||
|
||||
/**
|
||||
* Initialises the Wails runtime
|
||||
* ready will execute the callback when Wails has loaded
|
||||
* and initialised.
|
||||
*
|
||||
* @param {function} callback
|
||||
*/
|
||||
function Init(callback) {
|
||||
window.wails._.Init(callback);
|
||||
function ready(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,
|
||||
Dialog: Dialog,
|
||||
Events: Events,
|
||||
Init: Init,
|
||||
ready: Init.ready,
|
||||
Log: Log,
|
||||
System: System,
|
||||
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",
|
||||
"version": "1.2.13",
|
||||
"version": "1.2.24",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wails/runtime",
|
||||
"version": "1.2.22",
|
||||
"version": "1.3.10",
|
||||
"description": "Wails V2 Javascript runtime library",
|
||||
"main": "main.js",
|
||||
"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
|
||||
* 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.
|
||||
*
|
||||
* @export
|
||||
@@ -43,12 +43,12 @@ function DarkModeEnabled() {
|
||||
* Mac Title Bar Config
|
||||
* Check out https://github.com/lukakerr/NSWindowStyles for some examples of these settings
|
||||
* @typedef {Object} MacTitleBar
|
||||
* @param {bool} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
||||
* @param {bool} HideTitle - NSWindow.hideTitle
|
||||
* @param {bool} HideTitleBar - NSWindow.hideTitleBar
|
||||
* @param {bool} 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 {bool} HideToolbarSeparator - Set true to remove the separator between the toolbar and the main content area
|
||||
* @param {boolean} TitleBarAppearsTransparent - NSWindow.titleBarAppearsTransparent
|
||||
* @param {boolean} HideTitle - NSWindow.hideTitle
|
||||
* @param {boolean} HideTitleBar - NSWindow.hideTitleBar
|
||||
* @param {boolean} FullSizeContent - Makes the webview portion of the window the full size of the window, even over the titlebar
|
||||
* @param {boolean} UseToolbar - Set true to add a blank toolbar to the window (makes the title bar larger)
|
||||
* @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} MaxWidth - Window Maximum Width
|
||||
* @param {number} MaxHeight - Window Maximum Height
|
||||
* @param {bool} StartHidden - Start with window hidden
|
||||
* @param {bool} DevTools - Enables the window devtools
|
||||
* @param {boolean} StartHidden - Start with window hidden
|
||||
* @param {boolean} DevTools - Enables the window devtools
|
||||
* @param {number} RBGA - The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
||||
* @param {MacAppConfig} [Mac] - Configuration when running on Mac
|
||||
* @param {LinuxAppConfig} [Linux] - Configuration when running on Linux
|
||||
@@ -88,11 +88,23 @@ function AppConfig() {
|
||||
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 = {
|
||||
OnThemeChange: OnThemeChange,
|
||||
DarkModeEnabled: DarkModeEnabled,
|
||||
LogLevel: window.wails.System.LogLevel,
|
||||
Platform: window.wails.System.Platform,
|
||||
AppType: window.wails.System.AppType,
|
||||
LogLevel: LogLevel,
|
||||
Platform: Platform,
|
||||
AppType: AppType,
|
||||
AppConfig: AppConfig,
|
||||
};
|
||||
@@ -13,7 +13,7 @@ module.exports = {
|
||||
mode: 'production',
|
||||
output: {
|
||||
path: path.resolve(__dirname, '..', 'assets'),
|
||||
filename: 'desktop.js',
|
||||
filename: 'desktop_'+platform+'.js',
|
||||
library: 'Wails'
|
||||
},
|
||||
resolve: {
|
||||
|
||||
@@ -35,5 +35,6 @@ func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||
|
||||
// Quit the application
|
||||
func (r *Runtime) Quit() {
|
||||
// Start shutdown of Wails
|
||||
r.bus.Publish("quit", "runtime.Quit()")
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/shell"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -49,12 +50,20 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
wailsJS := fs.RelativePath("../../../internal/runtime/assets/desktop.js")
|
||||
wailsJS := fs.RelativePath("../assets/desktop_" + platform + ".js")
|
||||
runtimeData, err := ioutil.ReadFile(wailsJS)
|
||||
if err != nil {
|
||||
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
|
||||
runtimeC := `
|
||||
// runtime.c (c) 2019-Present Lea Anthony.
|
||||
|
||||
@@ -6,9 +6,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
golog "log"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/deepcopy"
|
||||
)
|
||||
|
||||
// Options defines the optional data that may be used
|
||||
@@ -64,21 +67,31 @@ func fatal(err error) {
|
||||
// New creates a new 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{
|
||||
name: name,
|
||||
runtime: p.runtime,
|
||||
data: reflect.ValueOf(defaultValue),
|
||||
dataType: dataType,
|
||||
name: name,
|
||||
runtime: p.runtime,
|
||||
}
|
||||
|
||||
// Setup the sync listener
|
||||
result.setupListener()
|
||||
|
||||
result.Set(defaultValue)
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
func (s *Store) lock() {
|
||||
s.mux.Lock()
|
||||
}
|
||||
|
||||
func (s *Store) unlock() {
|
||||
s.mux.Unlock()
|
||||
}
|
||||
|
||||
// OnError takes a function that will be called
|
||||
// whenever an error occurs
|
||||
func (s *Store) OnError(callback func(error)) {
|
||||
@@ -105,7 +118,7 @@ func (s *Store) processUpdatedData(data string) error {
|
||||
}
|
||||
|
||||
// Lock mutex for writing
|
||||
s.mux.Lock()
|
||||
s.lock()
|
||||
|
||||
// Handle nulls
|
||||
if newData == nil {
|
||||
@@ -116,7 +129,7 @@ func (s *Store) processUpdatedData(data string) error {
|
||||
}
|
||||
|
||||
// Unlock mutex
|
||||
s.mux.Unlock()
|
||||
s.unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -153,17 +166,27 @@ func (s *Store) setupListener() {
|
||||
|
||||
func (s *Store) resync() {
|
||||
|
||||
// Stringify data
|
||||
newdata, err := json.Marshal(s.data.Interface())
|
||||
if err != nil {
|
||||
if s.errorHandler != nil {
|
||||
s.errorHandler(err)
|
||||
return
|
||||
// Lock
|
||||
s.lock()
|
||||
defer s.unlock()
|
||||
|
||||
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
|
||||
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
|
||||
s.notify()
|
||||
@@ -176,7 +199,9 @@ func (s *Store) notify() {
|
||||
for _, callback := range s.callbacks {
|
||||
|
||||
// Build args
|
||||
s.lock()
|
||||
args := []reflect.Value{s.data}
|
||||
s.unlock()
|
||||
|
||||
if s.notifySynchronously {
|
||||
callback.Call(args)
|
||||
@@ -191,16 +216,31 @@ func (s *Store) notify() {
|
||||
// and notify listeners of the change
|
||||
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 {
|
||||
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
||||
s.lock()
|
||||
|
||||
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
|
||||
s.mux.Lock()
|
||||
s.data = reflect.ValueOf(data)
|
||||
s.mux.Unlock()
|
||||
s.data = reflect.ValueOf(dataCopy)
|
||||
|
||||
s.unlock()
|
||||
|
||||
// Resync with subscribers
|
||||
s.resync()
|
||||
@@ -251,7 +291,9 @@ func (s *Store) Subscribe(callback interface{}) {
|
||||
|
||||
callbackFunc := reflect.ValueOf(callback)
|
||||
|
||||
s.lock()
|
||||
s.callbacks = append(s.callbacks, callbackFunc)
|
||||
s.unlock()
|
||||
}
|
||||
|
||||
// updaterCheck ensures the given function to Update() is
|
||||
@@ -301,7 +343,9 @@ func (s *Store) Update(updater interface{}) {
|
||||
}
|
||||
|
||||
// Build args
|
||||
s.lock()
|
||||
args := []reflect.Value{s.data}
|
||||
s.unlock()
|
||||
|
||||
// Make call
|
||||
results := reflect.ValueOf(updater).Call(args)
|
||||
@@ -312,5 +356,12 @@ func (s *Store) Update(updater interface{}) {
|
||||
|
||||
// Get returns the value of the data that's kept in the current state / Store
|
||||
func (s *Store) Get() interface{} {
|
||||
s.lock()
|
||||
defer s.unlock()
|
||||
|
||||
if !s.data.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
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()
|
||||
SetTitle(title string)
|
||||
SetSize(width int, height int)
|
||||
SetMinSize(width int, height int)
|
||||
SetMaxSize(width int, height int)
|
||||
SetPosition(x int, y int)
|
||||
Fullscreen()
|
||||
UnFullscreen()
|
||||
@@ -85,6 +87,18 @@ func (w *window) SetSize(width int, height int) {
|
||||
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
|
||||
func (w *window) SetPosition(x int, y int) {
|
||||
message := fmt.Sprintf("window:position:%d:%d", x, y)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package servicebus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -12,23 +13,26 @@ import (
|
||||
type ServiceBus struct {
|
||||
listeners map[string][]chan *Message
|
||||
messageQueue chan *Message
|
||||
quitChannel chan struct{}
|
||||
wg sync.WaitGroup
|
||||
lock sync.RWMutex
|
||||
closed bool
|
||||
debug bool
|
||||
logger logger.CustomLogger
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// New creates a new ServiceBus
|
||||
// The internal message queue is set to 100 messages
|
||||
// Listener queues are set to 10
|
||||
func New(logger *logger.Logger) *ServiceBus {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &ServiceBus{
|
||||
listeners: make(map[string][]chan *Message),
|
||||
messageQueue: make(chan *Message, 100),
|
||||
quitChannel: make(chan struct{}, 1),
|
||||
logger: logger.CustomLogger("Service Bus"),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,23 +67,22 @@ func (s *ServiceBus) Debug() {
|
||||
// Start the service bus
|
||||
func (s *ServiceBus) Start() error {
|
||||
|
||||
s.logger.Trace("Starting")
|
||||
|
||||
// Prevent starting when closed
|
||||
if s.closed {
|
||||
return fmt.Errorf("cannot call start on closed servicebus")
|
||||
}
|
||||
|
||||
// We run in a different thread
|
||||
go func() {
|
||||
s.logger.Trace("Starting")
|
||||
|
||||
quit := false
|
||||
s.wg.Add(1)
|
||||
go func() {
|
||||
defer s.logger.Trace("Stopped")
|
||||
|
||||
// Loop until we get a quit message
|
||||
for !quit {
|
||||
for {
|
||||
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
|
||||
// Listen for messages
|
||||
case message := <-s.messageQueue:
|
||||
@@ -90,16 +93,9 @@ func (s *ServiceBus) Start() error {
|
||||
}
|
||||
// Dispatch message
|
||||
s.dispatchMessage(message)
|
||||
|
||||
// Listen for quit messages
|
||||
case <-s.quitChannel:
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate we have shut down
|
||||
s.wg.Done()
|
||||
|
||||
}()
|
||||
|
||||
return nil
|
||||
@@ -116,10 +112,7 @@ func (s *ServiceBus) Stop() error {
|
||||
s.closed = true
|
||||
|
||||
// Send quit message
|
||||
s.quitChannel <- struct{}{}
|
||||
|
||||
// Wait for dispatcher to stop
|
||||
s.wg.Wait()
|
||||
s.cancel()
|
||||
|
||||
// Close down subscriber channels
|
||||
s.lock.Lock()
|
||||
@@ -134,7 +127,6 @@ func (s *ServiceBus) Stop() error {
|
||||
// Close message queue
|
||||
close(s.messageQueue)
|
||||
|
||||
s.logger.Trace("Stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -171,7 +163,6 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
||||
// Prevent publish when closed
|
||||
if s.closed {
|
||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -183,7 +174,6 @@ func (s *ServiceBus) Publish(topic string, data interface{}) {
|
||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
||||
// Prevent publish when closed
|
||||
if s.closed {
|
||||
s.logger.Fatal("cannot call publish on closed servicebus")
|
||||
return
|
||||
}
|
||||
message := NewMessageForTarget(topic, data, target)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package signal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
gosignal "os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
@@ -20,24 +22,24 @@ type Manager struct {
|
||||
// signalChannel
|
||||
signalchannel chan os.Signal
|
||||
|
||||
// Quit channel
|
||||
quitChannel <-chan *servicebus.Message
|
||||
// ctx
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// Parent waitgroup
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewManager creates a new signal manager
|
||||
func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||
|
||||
result := &Manager{
|
||||
bus: bus,
|
||||
logger: logger.CustomLogger("Event Manager"),
|
||||
signalchannel: make(chan os.Signal, 2),
|
||||
quitChannel: quitChannel,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -49,20 +51,23 @@ func (m *Manager) Start() {
|
||||
// Hook into interrupts
|
||||
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() {
|
||||
running := true
|
||||
for running {
|
||||
select {
|
||||
case <-m.signalchannel:
|
||||
println()
|
||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||
m.bus.Publish("quit", "ctrl-c pressed")
|
||||
case <-m.quitChannel:
|
||||
running = false
|
||||
break
|
||||
}
|
||||
select {
|
||||
case <-m.signalchannel:
|
||||
println()
|
||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||
m.bus.Publish("quit", "ctrl-c pressed")
|
||||
|
||||
// Start shutdown of Wails
|
||||
m.cancel()
|
||||
|
||||
case <-m.ctx.Done():
|
||||
}
|
||||
m.logger.Trace("Shutdown")
|
||||
m.wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
// Binding is the Binding subsystem. It manages all service bus messages
|
||||
// starting with "binding".
|
||||
type Binding struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
bindingChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
running bool
|
||||
|
||||
// Binding db
|
||||
bindings *binding.Bindings
|
||||
@@ -27,12 +27,6 @@ type Binding struct {
|
||||
// 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) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Subscribe to event messages
|
||||
bindingChannel, err := bus.Subscribe("binding")
|
||||
if err != nil {
|
||||
@@ -40,7 +34,6 @@ func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *bin
|
||||
}
|
||||
|
||||
result := &Binding{
|
||||
quitChannel: quitChannel,
|
||||
bindingChannel: bindingChannel,
|
||||
logger: logger.CustomLogger("Binding Subsystem"),
|
||||
bindings: bindings,
|
||||
@@ -61,20 +54,16 @@ func (b *Binding) Start() error {
|
||||
go func() {
|
||||
for b.running {
|
||||
select {
|
||||
case <-b.quitChannel:
|
||||
b.running = false
|
||||
case bindingMessage := <-b.bindingChannel:
|
||||
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
b.shutdown()
|
||||
b.logger.Trace("Shutdown")
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Binding) shutdown() {
|
||||
b.logger.Trace("Shutdown")
|
||||
func (b *Binding) Close() {
|
||||
b.running = false
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
@@ -16,9 +19,10 @@ import (
|
||||
// Call is the Call subsystem. It manages all service bus messages
|
||||
// starting with "call".
|
||||
type Call struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
callChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
// quit flag
|
||||
shouldQuit bool
|
||||
|
||||
// bindings DB
|
||||
DB *binding.DB
|
||||
@@ -31,16 +35,16 @@ type Call struct {
|
||||
|
||||
// runtime
|
||||
runtime *runtime.Runtime
|
||||
|
||||
// context
|
||||
ctx context.Context
|
||||
|
||||
// parent waitgroup
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewCall creates a new call subsystem
|
||||
func NewCall(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
|
||||
}
|
||||
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
||||
|
||||
// Subscribe to event messages
|
||||
callChannel, err := bus.Subscribe("call:invoke")
|
||||
@@ -49,12 +53,13 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
||||
}
|
||||
|
||||
result := &Call{
|
||||
quitChannel: quitChannel,
|
||||
callChannel: callChannel,
|
||||
logger: logger.CustomLogger("Call Subsystem"),
|
||||
DB: DB,
|
||||
bus: bus,
|
||||
runtime: runtime,
|
||||
ctx: ctx,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -63,22 +68,21 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB,
|
||||
// Start the subsystem
|
||||
func (c *Call) Start() error {
|
||||
|
||||
c.running = true
|
||||
c.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for c.running {
|
||||
defer c.logger.Trace("Shutdown")
|
||||
for {
|
||||
select {
|
||||
case <-c.quitChannel:
|
||||
c.running = false
|
||||
case <-c.ctx.Done():
|
||||
c.wg.Done()
|
||||
return
|
||||
case callMessage := <-c.callChannel:
|
||||
// TODO: Check if this works ok in a goroutine
|
||||
c.processCall(callMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
c.shutdown()
|
||||
}()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (c *Call) shutdown() {
|
||||
c.logger.Trace("Shutdown")
|
||||
}
|
||||
|
||||
// CallbackMessage defines a message that contains the result of a call
|
||||
type CallbackMessage struct {
|
||||
Result interface{} `json:"result"`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -22,9 +23,7 @@ type eventListener struct {
|
||||
// Event is the Eventing subsystem. It manages all service bus messages
|
||||
// starting with "event".
|
||||
type Event struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
eventChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
// Event listeners
|
||||
listeners map[string][]*eventListener
|
||||
@@ -32,16 +31,16 @@ type Event struct {
|
||||
|
||||
// logger
|
||||
logger logger.CustomLogger
|
||||
|
||||
// ctx
|
||||
ctx context.Context
|
||||
|
||||
// parent waitgroup
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewEvent creates a new log subsystem
|
||||
func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
||||
|
||||
// Subscribe to event messages
|
||||
eventChannel, err := bus.Subscribe("event")
|
||||
@@ -50,10 +49,11 @@ func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error)
|
||||
}
|
||||
|
||||
result := &Event{
|
||||
quitChannel: quitChannel,
|
||||
eventChannel: eventChannel,
|
||||
logger: logger.CustomLogger("Event Subsystem"),
|
||||
listeners: make(map[string][]*eventListener),
|
||||
ctx: ctx,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -80,15 +80,16 @@ func (e *Event) Start() error {
|
||||
|
||||
e.logger.Trace("Starting")
|
||||
|
||||
e.running = true
|
||||
e.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for e.running {
|
||||
defer e.logger.Trace("Shutdown")
|
||||
for {
|
||||
select {
|
||||
case <-e.quitChannel:
|
||||
e.running = false
|
||||
break
|
||||
case <-e.ctx.Done():
|
||||
e.wg.Done()
|
||||
return
|
||||
case eventMessage := <-e.eventChannel:
|
||||
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
||||
eventType := splitTopic[1]
|
||||
@@ -128,8 +129,6 @@ func (e *Event) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
e.shutdown()
|
||||
}()
|
||||
|
||||
return nil
|
||||
@@ -190,7 +189,3 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
||||
// Unlock
|
||||
e.notifyLock.Unlock()
|
||||
}
|
||||
|
||||
func (e *Event) shutdown() {
|
||||
e.logger.Trace("Shutdown")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
@@ -12,15 +14,23 @@ import (
|
||||
// Log is the Logging subsystem. It handles messages with topics starting
|
||||
// with "log:"
|
||||
type Log struct {
|
||||
logChannel <-chan *servicebus.Message
|
||||
quitChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
logChannel <-chan *servicebus.Message
|
||||
|
||||
// quit flag
|
||||
shouldQuit bool
|
||||
|
||||
// Logger!
|
||||
logger *logger.Logger
|
||||
|
||||
// Loglevel store
|
||||
logLevelStore *runtime.Store
|
||||
|
||||
// Context for shutdown
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// internal waitgroup
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewLog creates a new log subsystem
|
||||
@@ -32,17 +42,14 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Subscribe to quit messages
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
result := &Log{
|
||||
logChannel: logChannel,
|
||||
quitChannel: quitChannel,
|
||||
logger: logger,
|
||||
logLevelStore: logLevelStore,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -51,15 +58,17 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
||||
// Start the subsystem
|
||||
func (l *Log) Start() error {
|
||||
|
||||
l.running = true
|
||||
l.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for l.running {
|
||||
defer l.logger.Trace("Logger Shutdown")
|
||||
|
||||
for l.shouldQuit == false {
|
||||
select {
|
||||
case <-l.quitChannel:
|
||||
l.running = false
|
||||
break
|
||||
case <-l.ctx.Done():
|
||||
l.wg.Done()
|
||||
return
|
||||
case logMessage := <-l.logChannel:
|
||||
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
||||
switch logType {
|
||||
@@ -98,8 +107,12 @@ func (l *Log) Start() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
l.logger.Trace("Logger Shutdown")
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) Close() {
|
||||
l.cancel()
|
||||
l.wg.Wait()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"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
|
||||
// starting with "menu".
|
||||
type Menu struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
menuChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
// shutdown flag
|
||||
shouldQuit bool
|
||||
|
||||
// logger
|
||||
logger logger.CustomLogger
|
||||
@@ -25,16 +29,16 @@ type Menu struct {
|
||||
|
||||
// Menu Manager
|
||||
menuManager *menumanager.Manager
|
||||
|
||||
// ctx
|
||||
ctx context.Context
|
||||
|
||||
// parent waitgroup
|
||||
wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewMenu creates a new menu subsystem
|
||||
func NewMenu(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
|
||||
}
|
||||
func NewMenu(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *menumanager.Manager) (*Menu, error) {
|
||||
|
||||
// Subscribe to menu messages
|
||||
menuChannel, err := bus.Subscribe("menu:")
|
||||
@@ -43,11 +47,12 @@ func NewMenu(bus *servicebus.ServiceBus, logger *logger.Logger, menuManager *men
|
||||
}
|
||||
|
||||
result := &Menu{
|
||||
quitChannel: quitChannel,
|
||||
menuChannel: menuChannel,
|
||||
logger: logger.CustomLogger("Menu Subsystem"),
|
||||
bus: bus,
|
||||
menuManager: menuManager,
|
||||
ctx: ctx,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -58,15 +63,16 @@ func (m *Menu) Start() error {
|
||||
|
||||
m.logger.Trace("Starting")
|
||||
|
||||
m.running = true
|
||||
m.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for m.running {
|
||||
defer m.logger.Trace("Shutdown")
|
||||
for {
|
||||
select {
|
||||
case <-m.quitChannel:
|
||||
m.running = false
|
||||
break
|
||||
case <-m.ctx.Done():
|
||||
m.wg.Done()
|
||||
return
|
||||
case menuMessage := <-m.menuChannel:
|
||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||
menuMessageType := splitTopic[1]
|
||||
@@ -147,14 +153,7 @@ func (m *Menu) Start() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
m.shutdown()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Menu) shutdown() {
|
||||
m.logger.Trace("Shutdown")
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
@@ -12,7 +14,6 @@ import (
|
||||
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
||||
// with "runtime:"
|
||||
type Runtime struct {
|
||||
quitChannel <-chan *servicebus.Message
|
||||
runtimeChannel <-chan *servicebus.Message
|
||||
|
||||
// The hooks channel allows us to hook into frontend startup
|
||||
@@ -20,22 +21,23 @@ type Runtime struct {
|
||||
startupCallback func(*runtime.Runtime)
|
||||
shutdownCallback func()
|
||||
|
||||
running bool
|
||||
// quit flag
|
||||
shouldQuit bool
|
||||
|
||||
logger logger.CustomLogger
|
||||
|
||||
// Runtime library
|
||||
runtime *runtime.Runtime
|
||||
|
||||
//ctx
|
||||
ctx context.Context
|
||||
|
||||
// Startup Hook
|
||||
startupOnce sync.Once
|
||||
}
|
||||
|
||||
// NewRuntime creates a new runtime subsystem
|
||||
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime)) (*Runtime, error) {
|
||||
|
||||
// Subscribe to log messages
|
||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||
@@ -50,13 +52,12 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
||||
}
|
||||
|
||||
result := &Runtime{
|
||||
quitChannel: quitChannel,
|
||||
runtimeChannel: runtimeChannel,
|
||||
hooksChannel: hooksChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: runtime.New(bus),
|
||||
startupCallback: startupCallback,
|
||||
shutdownCallback: shutdownCallback,
|
||||
runtimeChannel: runtimeChannel,
|
||||
hooksChannel: hooksChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: runtime.New(bus),
|
||||
startupCallback: startupCallback,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -65,15 +66,11 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger, startupCallba
|
||||
// Start the subsystem
|
||||
func (r *Runtime) Start() error {
|
||||
|
||||
r.running = true
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
for r.running {
|
||||
defer r.logger.Trace("Shutdown")
|
||||
for {
|
||||
select {
|
||||
case <-r.quitChannel:
|
||||
r.running = false
|
||||
break
|
||||
case hooksMessage := <-r.hooksChannel:
|
||||
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
||||
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
||||
@@ -81,7 +78,9 @@ func (r *Runtime) Start() error {
|
||||
switch hook {
|
||||
case "startup":
|
||||
if r.startupCallback != nil {
|
||||
go r.startupCallback(r.runtime)
|
||||
r.startupOnce.Do(func() {
|
||||
go r.startupCallback(r.runtime)
|
||||
})
|
||||
} else {
|
||||
r.logger.Warning("no startup callback registered!")
|
||||
}
|
||||
@@ -113,11 +112,10 @@ func (r *Runtime) Start() error {
|
||||
if err != nil {
|
||||
r.logger.Error(err.Error())
|
||||
}
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Call shutdown
|
||||
r.shutdown()
|
||||
}()
|
||||
|
||||
return nil
|
||||
@@ -128,15 +126,6 @@ func (r *Runtime) GoRuntime() *runtime.Runtime {
|
||||
return r.runtime
|
||||
}
|
||||
|
||||
func (r *Runtime) shutdown() {
|
||||
if r.shutdownCallback != nil {
|
||||
go r.shutdownCallback()
|
||||
} else {
|
||||
r.logger.Warning("no shutdown callback registered!")
|
||||
}
|
||||
r.logger.Trace("Shutdown")
|
||||
}
|
||||
|
||||
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
||||
switch method {
|
||||
case "open":
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
)
|
||||
|
||||
// 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
|
||||
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
||||
temp := fmt.Sprintf(message, args...)
|
||||
_, err := fmt.Fprintln(c.Writer, "FATAL: "+temp)
|
||||
_, err := fmt.Fprintln(c.Writer, colour.Red("FATAL: "+temp))
|
||||
if err != nil {
|
||||
println("FATAL: ", err)
|
||||
println(colour.Red("FATAL: " + err.Error()))
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ func Build(options *Options) (string, error) {
|
||||
builder = newHybridBuilder(options)
|
||||
case "server":
|
||||
builder = newServerBuilder(options)
|
||||
case "dev":
|
||||
builder = newDesktopBuilder(options)
|
||||
default:
|
||||
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Modifier is actually a string
|
||||
type Modifier string
|
||||
|
||||
@@ -16,6 +21,23 @@ const (
|
||||
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
|
||||
type Accelerator struct {
|
||||
Key string
|
||||
@@ -25,14 +47,14 @@ type Accelerator struct {
|
||||
// Key creates a standard key Accelerator
|
||||
func Key(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
}
|
||||
}
|
||||
|
||||
// CmdOrCtrl creates a 'CmdOrCtrl' Accelerator
|
||||
func CmdOrCtrl(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
Modifiers: []Modifier{CmdOrCtrlKey},
|
||||
}
|
||||
}
|
||||
@@ -40,7 +62,7 @@ func CmdOrCtrl(key string) *Accelerator {
|
||||
// OptionOrAlt creates a 'OptionOrAlt' Accelerator
|
||||
func OptionOrAlt(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
Modifiers: []Modifier{OptionOrAltKey},
|
||||
}
|
||||
}
|
||||
@@ -48,7 +70,7 @@ func OptionOrAlt(key string) *Accelerator {
|
||||
// Shift creates a 'Shift' Accelerator
|
||||
func Shift(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
Modifiers: []Modifier{ShiftKey},
|
||||
}
|
||||
}
|
||||
@@ -56,7 +78,7 @@ func Shift(key string) *Accelerator {
|
||||
// Control creates a 'Control' Accelerator
|
||||
func Control(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
Modifiers: []Modifier{ControlKey},
|
||||
}
|
||||
}
|
||||
@@ -64,7 +86,7 @@ func Control(key string) *Accelerator {
|
||||
// Super creates a 'Super' Accelerator
|
||||
func Super(key string) *Accelerator {
|
||||
return &Accelerator{
|
||||
Key: key,
|
||||
Key: strings.ToLower(key),
|
||||
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
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
)
|
||||
|
||||
// MenuItem represents a menuitem contained in a menu
|
||||
@@ -38,6 +39,9 @@ type MenuItem struct {
|
||||
// Image - base64 image data
|
||||
Image string
|
||||
|
||||
// MacTemplateImage indicates that on a mac, this image is a template image
|
||||
MacTemplateImage bool
|
||||
|
||||
// Tooltip
|
||||
Tooltip string
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@ package mac
|
||||
|
||||
import "github.com/wailsapp/wails/v2/pkg/menu"
|
||||
|
||||
type ActivationPolicy int
|
||||
|
||||
const (
|
||||
NSApplicationActivationPolicyRegular ActivationPolicy = 0
|
||||
NSApplicationActivationPolicyAccessory ActivationPolicy = 1
|
||||
NSApplicationActivationPolicyProhibited ActivationPolicy = 2
|
||||
)
|
||||
|
||||
// Options are options specific to Mac
|
||||
type Options struct {
|
||||
TitleBar *TitleBar
|
||||
@@ -11,4 +19,5 @@ type Options struct {
|
||||
Menu *menu.Menu
|
||||
TrayMenus []*menu.TrayMenu
|
||||
ContextMenus []*menu.ContextMenu
|
||||
ActivationPolicy ActivationPolicy
|
||||
}
|
||||
|
||||
@@ -14,27 +14,28 @@ import (
|
||||
|
||||
// App contains options for creating the App
|
||||
type App struct {
|
||||
Title string
|
||||
Width int
|
||||
Height int
|
||||
DisableResize bool
|
||||
Fullscreen bool
|
||||
MinWidth int
|
||||
MinHeight int
|
||||
MaxWidth int
|
||||
MaxHeight int
|
||||
StartHidden bool
|
||||
DevTools bool
|
||||
RGBA int
|
||||
ContextMenus []*menu.ContextMenu
|
||||
TrayMenus []*menu.TrayMenu
|
||||
Menu *menu.Menu
|
||||
Mac *mac.Options
|
||||
Logger logger.Logger `json:"-"`
|
||||
LogLevel logger.LogLevel
|
||||
Startup func(*wailsruntime.Runtime) `json:"-"`
|
||||
Shutdown func() `json:"-"`
|
||||
Bind []interface{}
|
||||
Title string
|
||||
Width int
|
||||
Height int
|
||||
DisableResize bool
|
||||
Fullscreen bool
|
||||
MinWidth int
|
||||
MinHeight int
|
||||
MaxWidth int
|
||||
MaxHeight int
|
||||
StartHidden bool
|
||||
HideWindowOnClose bool
|
||||
DevTools bool
|
||||
RGBA int
|
||||
ContextMenus []*menu.ContextMenu
|
||||
TrayMenus []*menu.TrayMenu
|
||||
Menu *menu.Menu
|
||||
Mac *mac.Options
|
||||
Logger logger.Logger `json:"-"`
|
||||
LogLevel logger.LogLevel
|
||||
Startup func(*wailsruntime.Runtime) `json:"-"`
|
||||
Shutdown func() `json:"-"`
|
||||
Bind []interface{}
|
||||
}
|
||||
|
||||
// MergeDefaults will set the minimum default values for an application
|
||||
|
||||
Reference in New Issue
Block a user