mirror of
https://github.com/taigrr/wails.git
synced 2026-04-13 10:28:13 -07:00
Compare commits
7 Commits
patch-1
...
v2-alpha-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
febd867fa7 | ||
|
|
9806b9c651 | ||
|
|
2a20867d00 | ||
|
|
48ff661150 | ||
|
|
19d59bef51 | ||
|
|
bdcb2fe810 | ||
|
|
25a157e661 |
@@ -2,19 +2,19 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
// AddBuildSubcommand adds the `build` command for the Wails application
|
||||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
func AddBuildSubcommand(app *clir.Cli) {
|
||||||
|
|
||||||
outputType := "desktop"
|
outputType := "desktop"
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// Setup pack flag
|
// Setup pack flag
|
||||||
pack := false
|
pack := false
|
||||||
command.BoolFlag("package", "Create a platform specific package", &pack)
|
command.BoolFlag("pack", "Create a platform specific package", &pack)
|
||||||
|
|
||||||
compilerCommand := "go"
|
compilerCommand := "go"
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||||
@@ -43,7 +43,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// Quiet Build
|
// Quiet Build
|
||||||
quiet := false
|
quiet := false
|
||||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||||
|
|
||||||
// ldflags to pass to `go`
|
// ldflags to pass to `go`
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
@@ -53,38 +53,28 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
logFile := ""
|
logFile := ""
|
||||||
command.StringFlag("l", "Log to file", &logFile)
|
command.StringFlag("l", "Log to file", &logFile)
|
||||||
|
|
||||||
// Retain assets
|
|
||||||
keepAssets := false
|
|
||||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
|
||||||
|
|
||||||
appleIdentity := ""
|
|
||||||
if runtime.GOOS == "darwin" {
|
|
||||||
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
|
||||||
}
|
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
logger.Mute(quiet)
|
|
||||||
|
if !quiet {
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate output type
|
// Validate output type
|
||||||
if !validTargetTypes.Contains(outputType) {
|
if !validTargetTypes.Contains(outputType) {
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
logger.Fatal(fmt.Sprintf("Output type '%s' is not valid.", outputType))
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !quiet {
|
if !quiet {
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure package is used with apple identity
|
|
||||||
if appleIdentity != "" && pack == false {
|
|
||||||
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
|
||||||
}
|
|
||||||
|
|
||||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||||
logger.Println(task)
|
logger.Writeln(task)
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
logger.Writeln(strings.Repeat("-", len(task)))
|
||||||
|
|
||||||
// Setup mode
|
// Setup mode
|
||||||
mode := build.Debug
|
mode := build.Debug
|
||||||
@@ -94,15 +84,13 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
OutputType: outputType,
|
OutputType: outputType,
|
||||||
Mode: mode,
|
Mode: mode,
|
||||||
Pack: pack,
|
Pack: pack,
|
||||||
Platform: platform,
|
Platform: platform,
|
||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
KeepAssets: keepAssets,
|
|
||||||
AppleIdentity: appleIdentity,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return doBuild(buildOptions)
|
return doBuild(buildOptions)
|
||||||
@@ -119,12 +107,11 @@ func doBuild(buildOptions *build.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output stats
|
// Output stats
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
buildOptions.Logger.Println("")
|
buildOptions.Logger.Writeln("")
|
||||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
buildOptions.Logger.Writeln(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||||
buildOptions.Logger.Println("")
|
buildOptions.Logger.Writeln("")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,45 +2,35 @@ package dev
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/colour"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/process"
|
"github.com/wailsapp/wails/v2/internal/process"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
func LogGreen(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.Green(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogRed(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.Red(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogDarkYellow(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.DarkYellow(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
// AddSubcommand adds the `dev` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("dev", "Development mode")
|
command := app.NewSubCommand("dev", "Development mode")
|
||||||
|
|
||||||
|
outputType := "desktop"
|
||||||
|
|
||||||
|
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||||
|
|
||||||
|
// Setup target type flag
|
||||||
|
description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
|
||||||
|
command.StringFlag("t", description, &outputType)
|
||||||
|
|
||||||
// Passthrough ldflags
|
// Passthrough ldflags
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
@@ -51,19 +41,18 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// extensions to trigger rebuilds
|
// extensions to trigger rebuilds
|
||||||
extensions := "go"
|
extensions := "go"
|
||||||
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||||
|
|
||||||
// extensions to trigger rebuilds
|
|
||||||
showWarnings := false
|
|
||||||
command.BoolFlag("w", "Show warnings", &showWarnings)
|
|
||||||
|
|
||||||
loglevel := ""
|
|
||||||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if !validTargetTypes.Contains(outputType) {
|
||||||
|
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||||
|
}
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
|
|
||||||
// TODO: Check you are in a project directory
|
// TODO: Check you are in a project directory
|
||||||
@@ -75,6 +64,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
var debugBinaryProcess *process.Process = nil
|
||||||
|
var buildFrontend bool = true
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
@@ -84,27 +74,19 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
debounceQuit := make(chan bool, 1)
|
debounceQuit := make(chan bool, 1)
|
||||||
|
|
||||||
// Do initial build
|
// Do initial build
|
||||||
logger.Println("Building application for development...")
|
logger.Info("Building application for development...")
|
||||||
newProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||||
if newProcess != nil {
|
|
||||||
debugBinaryProcess = newProcess
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||||
// logger.Println("event: %+v", event)
|
// logger.Info("event: %+v", event)
|
||||||
|
|
||||||
// Check for new directories
|
// Check for new directories
|
||||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||||
// If this is a folder, add it to our watch list
|
// If this is a folder, add it to our watch list
|
||||||
if fs.DirExists(event.Name) {
|
if fs.DirExists(event.Name) {
|
||||||
if !strings.Contains(event.Name, "node_modules") {
|
if !strings.Contains(event.Name, "node_modules") {
|
||||||
err := watcher.Add(event.Name)
|
watcher.Add(event.Name)
|
||||||
if err != nil {
|
logger.Info("Watching directory: %s", event.Name)
|
||||||
logger.Fatal("%s", err.Error())
|
|
||||||
}
|
|
||||||
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -113,33 +95,38 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
// Check for file writes
|
// Check for file writes
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
|
||||||
|
// logger.Info("modified file: %s", event.Name)
|
||||||
var rebuild bool = false
|
var rebuild bool = false
|
||||||
|
|
||||||
// Iterate all file patterns
|
// Iterate all file patterns
|
||||||
for _, pattern := range extensionsThatTriggerARebuild {
|
for _, pattern := range extensionsThatTriggerARebuild {
|
||||||
if strings.HasSuffix(event.Name, pattern) {
|
rebuild = strings.HasSuffix(event.Name, pattern)
|
||||||
rebuild = true
|
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")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rebuild {
|
if !rebuild {
|
||||||
if showWarnings {
|
logger.Info("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||||
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
if buildFrontend {
|
||||||
|
logger.Info("Full rebuild triggered: %s updated", event.Name)
|
||||||
|
} else {
|
||||||
|
logger.Info("Partial build triggered: %s updated", event.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// Do a rebuild
|
// Do a rebuild
|
||||||
|
|
||||||
// Try and build the app
|
// Try and build the app
|
||||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error during build: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If we have a new process, save it
|
// If we have a new process, save it
|
||||||
if newBinaryProcess != nil {
|
if newBinaryProcess != nil {
|
||||||
debugBinaryProcess = newBinaryProcess
|
debugBinaryProcess = newBinaryProcess
|
||||||
@@ -149,28 +136,23 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Get project dir
|
// Get project dir
|
||||||
projectDir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all subdirectories
|
// Get all subdirectories
|
||||||
dirs, err := fs.GetSubdirectories(projectDir)
|
dirs, err := fs.GetSubdirectories(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
LogGreen("Watching (sub)/directory: %s", projectDir)
|
|
||||||
|
|
||||||
// Setup a watcher for non-node_modules directories
|
// Setup a watcher for non-node_modules directories
|
||||||
dirs.Each(func(dir string) {
|
dirs.Each(func(dir string) {
|
||||||
if strings.Contains(dir, "node_modules") {
|
if strings.Contains(dir, "node_modules") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Ignore build directory
|
logger.Info("Watching directory: %s", dir)
|
||||||
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = watcher.Add(dir)
|
err = watcher.Add(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
logger.Fatal(err.Error())
|
||||||
@@ -182,7 +164,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
for quit == false {
|
for quit == false {
|
||||||
select {
|
select {
|
||||||
case <-quitChannel:
|
case <-quitChannel:
|
||||||
LogGreen("\nCaught quit")
|
println()
|
||||||
// Notify debouncer to quit
|
// Notify debouncer to quit
|
||||||
debounceQuit <- true
|
debounceQuit <- true
|
||||||
quit = true
|
quit = true
|
||||||
@@ -191,13 +173,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Kill the current program if running
|
// Kill the current program if running
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
err := debugBinaryProcess.Kill()
|
debugBinaryProcess.Kill()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LogGreen("Development mode exited")
|
logger.Info("Development mode exited")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -224,15 +203,15 @@ exit:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string) (*process.Process, error) {
|
func restartApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
||||||
println()
|
println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogRed("Build error - continuing to run current version")
|
logger.Error("Build Failed: %s", err.Error())
|
||||||
LogDarkYellow(err.Error())
|
return nil
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
logger.Info("Build new binary: %s", appBinary)
|
||||||
|
|
||||||
// Kill existing binary if need be
|
// Kill existing binary if need be
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
@@ -248,24 +227,21 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
// TODO: Generate `backend.js`
|
// TODO: Generate `backend.js`
|
||||||
|
|
||||||
// Start up new binary
|
// Start up new binary
|
||||||
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
newProcess := process.NewProcess(logger, appBinary)
|
||||||
err = newProcess.Start()
|
err = newProcess.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove binary
|
// Remove binary
|
||||||
deleteError := fs.DeleteFile(appBinary)
|
fs.DeleteFile(appBinary)
|
||||||
if deleteError != nil {
|
|
||||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
|
||||||
}
|
|
||||||
logger.Fatal("Unable to start application: %s", err.Error())
|
logger.Fatal("Unable to start application: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProcess, nil
|
return newProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
func buildApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||||
|
|
||||||
// Create random output file
|
// Create random output file
|
||||||
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||||
|
|
||||||
// Create BuildOptions
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
@@ -277,7 +253,7 @@ func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, co
|
|||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
OutputFile: outputFile,
|
OutputFile: outputFile,
|
||||||
IgnoreFrontend: true,
|
IgnoreFrontend: !buildFrontend,
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Build(buildOptions)
|
return build.Build(buildOptions)
|
||||||
|
|||||||
@@ -2,37 +2,37 @@ package doctor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/system"
|
"github.com/wailsapp/wails/v2/internal/system"
|
||||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `doctor` command for the Wails application
|
// AddSubcommand adds the `doctor` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
logger := clilogger.New(w)
|
// Create logger
|
||||||
|
logger := logger.New()
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
logger.Print("Scanning system - please wait...")
|
print("Scanning system - please wait...")
|
||||||
|
|
||||||
// Get system info
|
// Get system info
|
||||||
info, err := system.GetInfo()
|
info, err := system.GetInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Println("Done.")
|
println("Done.")
|
||||||
|
|
||||||
// Start a new tabwriter
|
// Start a new tabwriter
|
||||||
w := new(tabwriter.Writer)
|
w := new(tabwriter.Writer)
|
||||||
@@ -112,22 +112,22 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
logger.Println("")
|
println()
|
||||||
logger.Println("Diagnosis")
|
println("Diagnosis")
|
||||||
logger.Println("---------\n")
|
println("---------\n")
|
||||||
|
|
||||||
// Generate an appropriate diagnosis
|
// Generate an appropriate diagnosis
|
||||||
|
|
||||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||||
logger.Println("Your system is ready for Wails development!")
|
println("Your system is ready for Wails development!")
|
||||||
}
|
}
|
||||||
|
|
||||||
if dependenciesAvailableRequired != 0 {
|
if dependenciesAvailableRequired != 0 {
|
||||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
if dependenciesAvailableOptional != 0 {
|
if dependenciesAvailableOptional != 0 {
|
||||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(externalPackages) > 0 {
|
if len(externalPackages) > 0 {
|
||||||
@@ -135,18 +135,18 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
if p.Optional {
|
if p.Optional {
|
||||||
print("[Optional] ")
|
print("[Optional] ")
|
||||||
}
|
}
|
||||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
println("Install " + p.Name + ": " + p.InstallCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dependenciesMissing) != 0 {
|
if len(dependenciesMissing) != 0 {
|
||||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
||||||
log.Println("Fatal:")
|
println("Fatal:")
|
||||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("")
|
println()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,17 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/pkg/errors"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/templates"
|
"github.com/wailsapp/wails/v2/internal/templates"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/git"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `init` command for the Wails application
|
// AddSubcommand adds the `init` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
// Load the template shortnames
|
// Load the template shortnames
|
||||||
validShortNames, err := templates.TemplateShortNames()
|
validShortNames, err := templates.TemplateShortNames()
|
||||||
@@ -34,24 +32,13 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
command.StringFlag("n", "Name of project", &projectName)
|
command.StringFlag("n", "Name of project", &projectName)
|
||||||
|
|
||||||
// Setup project directory
|
// Setup project directory
|
||||||
projectDirectory := ""
|
projectDirectory := "."
|
||||||
command.StringFlag("d", "Project directory", &projectDirectory)
|
command.StringFlag("d", "Project directory", &projectDirectory)
|
||||||
|
|
||||||
// Quiet Init
|
// Quiet Init
|
||||||
quiet := false
|
quiet := false
|
||||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||||
|
|
||||||
initGit := false
|
|
||||||
gitInstalled := git.IsInstalled()
|
|
||||||
if gitInstalled {
|
|
||||||
// Git Init
|
|
||||||
command.BoolFlag("g", "Initialise git repository", &initGit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VSCode project files
|
|
||||||
vscode := false
|
|
||||||
command.BoolFlag("vscode", "Generate VSCode project files", &vscode)
|
|
||||||
|
|
||||||
// List templates
|
// List templates
|
||||||
list := false
|
list := false
|
||||||
command.BoolFlag("l", "List templates", &list)
|
command.BoolFlag("l", "List templates", &list)
|
||||||
@@ -59,29 +46,32 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
logger.Mute(quiet)
|
|
||||||
|
if !quiet {
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
// Are we listing templates?
|
// Are we listing templates?
|
||||||
if list {
|
if list {
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
err := templates.OutputList(logger)
|
err := templates.OutputList(logger)
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate output type
|
// Validate output type
|
||||||
if !validShortNames.Contains(templateName) {
|
if !validShortNames.Contains(templateName) {
|
||||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
logger.Write(fmt.Sprintf("ERROR: Template '%s' is not valid", templateName))
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
command.PrintHelp()
|
command.PrintHelp()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate name
|
// Validate name
|
||||||
if len(projectName) == 0 {
|
if len(projectName) == 0 {
|
||||||
logger.Println("ERROR: Project name required")
|
logger.Writeln("ERROR: Project name required")
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
command.PrintHelp()
|
command.PrintHelp()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -91,23 +81,15 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
||||||
logger.Println(task)
|
logger.Writeln(task)
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
logger.Writeln(strings.Repeat("-", len(task)))
|
||||||
|
|
||||||
// Create Template Options
|
// Create Template Options
|
||||||
options := &templates.Options{
|
options := &templates.Options{
|
||||||
ProjectName: projectName,
|
ProjectName: projectName,
|
||||||
TargetDir: projectDirectory,
|
TargetDir: projectDirectory,
|
||||||
TemplateName: templateName,
|
TemplateName: templateName,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
GenerateVSCode: vscode,
|
|
||||||
InitGit: initGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to discover author details from git config
|
|
||||||
err := findAuthorDetails(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return initProject(options)
|
return initProject(options)
|
||||||
@@ -128,55 +110,11 @@ func initProject(options *templates.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.InitGit {
|
|
||||||
err = initGit(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output stats
|
// Output stats
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
options.Logger.Println("")
|
options.Logger.Writeln("")
|
||||||
options.Logger.Println("Project Name: " + options.ProjectName)
|
options.Logger.Writeln(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
||||||
options.Logger.Println("Project Directory: " + options.TargetDir)
|
options.Logger.Writeln("")
|
||||||
options.Logger.Println("Project Template: " + options.TemplateName)
|
|
||||||
if options.GenerateVSCode {
|
|
||||||
options.Logger.Println("VSCode config files generated.")
|
|
||||||
}
|
|
||||||
if options.InitGit {
|
|
||||||
options.Logger.Println("Git repository initialised.")
|
|
||||||
}
|
|
||||||
options.Logger.Println("")
|
|
||||||
options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
|
||||||
options.Logger.Println("")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGit(options *templates.Options) error {
|
|
||||||
err := git.InitRepo(options.TargetDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Unable to initialise git repository:")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findAuthorDetails(options *templates.Options) error {
|
|
||||||
if git.IsInstalled() {
|
|
||||||
name, err := git.Name()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorName = strings.TrimSpace(name)
|
|
||||||
|
|
||||||
email, err := git.Email()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorEmail = strings.TrimSpace(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/colour"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,44 +14,19 @@ func fatal(message string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func banner(_ *clir.Cli) string {
|
|
||||||
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
version := "v2.0.0-alpha"
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||||
|
|
||||||
app.SetBannerFunction(banner)
|
build.AddBuildSubcommand(app)
|
||||||
|
err = initialise.AddSubcommand(app)
|
||||||
build.AddBuildSubcommand(app, os.Stdout)
|
|
||||||
err = initialise.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
err = doctor.AddSubcommand(app)
|
||||||
err = debug.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
err = doctor.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dev.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = generate.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = update.AddSubcommand(app, os.Stdout, version)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
29
v2/go.mod
29
v2/go.mod
@@ -1,29 +1,14 @@
|
|||||||
module github.com/wailsapp/wails/v2
|
module github.com/wailsapp/wails/v2
|
||||||
|
|
||||||
go 1.16
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/leaanthony/clir v1.0.2
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/leaanthony/gosod v0.0.3
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/leaanthony/slicer v1.4.1
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/matryer/is v1.3.0
|
||||||
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
|
|
||||||
github.com/leaanthony/gosod v0.0.4
|
|
||||||
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/olekukonko/tablewriter v0.0.4
|
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
|
github.com/xyproto/xpm v1.2.1
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
nhooyr.io/websocket v1.7.4
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
|
||||||
nhooyr.io/websocket v1.8.6
|
|
||||||
)
|
)
|
||||||
|
|||||||
132
v2/go.sum
132
v2/go.sum
@@ -1,125 +1,57 @@
|
|||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
|
||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
|
||||||
github.com/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=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/leaanthony/clir v1.0.2 h1:Ham9Ep7NH95il0x/ci5s2ExJa/K4B2dE8L2uaSj2cxo=
|
||||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
github.com/leaanthony/clir v1.0.2/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/leaanthony/gosod v0.0.3 h1:VlPilye0zoH4I0WihShyoiTHyiAN1v6dcBW4mMvGJao=
|
||||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
github.com/leaanthony/gosod v0.0.3/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
github.com/leaanthony/slicer v1.4.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w=
|
||||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
github.com/leaanthony/slicer v1.4.1/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
github.com/matryer/is v1.3.0 h1:9qiso3jaJrOe6qBRJRBt2Ldht05qDiFP9le0JOIhRSI=
|
||||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
|
||||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
|
||||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
|
||||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
|
||||||
github.com/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 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-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/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.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-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
nhooyr.io/websocket v1.7.4 h1:w/LGB2sZT0RV8lZYR7nfyaYz4PUbYZ5oF7NBon2M0NY=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
nhooyr.io/websocket v1.7.4/go.mod h1:PxYxCwFdFYQ0yRvtQz3s/dC+VEm7CSuC/4b9t8MQQxw=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
|
||||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !desktop,!hybrid,!server,!dev
|
// +build !desktop,!hybrid,!server
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
@@ -10,9 +10,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
@@ -22,15 +20,12 @@ type App struct {
|
|||||||
Height int
|
Height int
|
||||||
Resizable bool
|
Resizable bool
|
||||||
|
|
||||||
// Indicates if the app is running in debug mode
|
Features *features.Features
|
||||||
debug bool
|
|
||||||
|
|
||||||
logger *logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateApp returns a null application
|
// CreateApp returns a null application
|
||||||
func CreateApp(_ *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
return &App{}, nil
|
return &App{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
@@ -39,3 +34,8 @@ func (a *App) Run() error {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind the dummy interface
|
||||||
|
func (a *App) Bind(dummy interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,152 +3,117 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"os"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/internal/signal"
|
"github.com/wailsapp/wails/v2/internal/signal"
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
appType string
|
|
||||||
|
|
||||||
window *ffenestri.Application
|
window *ffenestri.Application
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
signal *signal.Manager
|
signal *signal.Manager
|
||||||
options *options.App
|
|
||||||
|
|
||||||
// Subsystems
|
// Subsystems
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
//binding *subsystem.Binding
|
binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
menu *subsystem.Menu
|
|
||||||
url *subsystem.URL
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Indicates if the app is in debug mode
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// This is our binding DB
|
// This is our binding DB
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
|
|
||||||
// Application Stores
|
// Feature flags
|
||||||
loglevelStore *runtime.Store
|
Features *features.Features
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
|
|
||||||
// Merge default options
|
// Merge default options
|
||||||
options.MergeDefaults(appoptions)
|
options.mergeDefaults()
|
||||||
|
|
||||||
// Set up logger
|
// Set up logger
|
||||||
myLogger := logger.New(appoptions.Logger)
|
myLogger := logger.New(os.Stdout)
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
myLogger.SetLogLevel(logger.TRACE)
|
||||||
|
|
||||||
// Create the menu manager
|
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||||
menuManager := menumanager.NewManager()
|
Title: options.Title,
|
||||||
|
Width: options.Width,
|
||||||
|
Height: options.Height,
|
||||||
|
MinWidth: options.MinWidth,
|
||||||
|
MinHeight: options.MinHeight,
|
||||||
|
MaxWidth: options.MaxWidth,
|
||||||
|
MaxHeight: options.MaxHeight,
|
||||||
|
Frameless: options.Frameless,
|
||||||
|
|
||||||
// Process the application menu
|
// This should be controlled by the compile time flags...
|
||||||
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
DevTools: true,
|
||||||
|
|
||||||
// Process context menus
|
Resizable: !options.DisableResize,
|
||||||
contextMenus := options.GetContextMenus(appoptions)
|
Fullscreen: options.Fullscreen,
|
||||||
for _, contextMenu := range contextMenus {
|
}, myLogger)
|
||||||
menuManager.AddContextMenu(contextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process tray menus
|
|
||||||
trayMenus := options.GetTrayMenus(appoptions)
|
|
||||||
for _, trayMenu := range trayMenus {
|
|
||||||
menuManager.AddTrayMenu(trayMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
|
||||||
|
|
||||||
// Create binding exemptions - Ugly hack. There must be a better way
|
|
||||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
appType: "desktop",
|
window: window,
|
||||||
window: window,
|
servicebus: servicebus.New(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
logger: myLogger,
|
||||||
logger: myLogger,
|
bindings: binding.NewBindings(myLogger),
|
||||||
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
Features: features.New(),
|
||||||
menuManager: menuManager,
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.options = appoptions
|
return result
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
err := result.Init()
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
|
|
||||||
var err error
|
// Setup signal handler
|
||||||
|
signal, err := signal.NewManager(a.servicebus, a.logger)
|
||||||
// Setup a context
|
if err != nil {
|
||||||
var subsystemWaitGroup sync.WaitGroup
|
return err
|
||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
}
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
a.signal = signal
|
||||||
|
a.signal.Start()
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
err = a.servicebus.Start()
|
a.servicebus.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
// Start the runtime
|
||||||
if err != nil {
|
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtimesubsystem
|
|
||||||
err = a.runtime.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.runtime = runtime
|
||||||
|
a.runtime.Start()
|
||||||
|
|
||||||
// Application Stores
|
// Start the binding subsystem
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.binding = binding
|
||||||
|
a.binding.Start()
|
||||||
|
|
||||||
// Start the logging subsystem
|
// Start the logging subsystem
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.log = log
|
a.log = log
|
||||||
err = a.log.Start()
|
a.log.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the dispatcher
|
// create the dispatcher
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||||
@@ -156,56 +121,23 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.dispatcher = dispatcher
|
a.dispatcher = dispatcher
|
||||||
err = dispatcher.Start()
|
dispatcher.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.options.Mac.URLHandlers != nil {
|
|
||||||
// Start the url handler subsystem
|
|
||||||
url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.url = url
|
|
||||||
err = a.url.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
// Start the eventing subsystem
|
||||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
event, err := subsystem.NewEvent(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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.event = event
|
||||||
|
a.event.Start()
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = callSubsystem
|
|
||||||
err = a.call.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.call = call
|
||||||
|
a.call.Start()
|
||||||
|
|
||||||
// Dump bindings as a debug
|
// Dump bindings as a debug
|
||||||
bindingDump, err := a.bindings.ToJSON()
|
bindingDump, err := a.bindings.ToJSON()
|
||||||
@@ -213,42 +145,20 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup signal handler
|
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
|
||||||
a.logger.Trace("Ffenestri.Run() exited")
|
a.logger.Trace("Ffenestri.Run() exited")
|
||||||
if err != nil {
|
a.servicebus.Stop()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close down all the subsystems
|
return result
|
||||||
a.logger.Trace("Cancelling subsystems")
|
}
|
||||||
cancel()
|
|
||||||
subsystemWaitGroup.Wait()
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
a.logger.Trace("Cancelling dispatcher")
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
dispatcher.Close()
|
|
||||||
|
// Add the struct to the bindings
|
||||||
// Close log
|
err := a.bindings.Add(structPtr)
|
||||||
a.logger.Trace("Stopping log")
|
if err != nil {
|
||||||
log.Close()
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
@@ -42,7 +43,8 @@ type App struct {
|
|||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
|
|
||||||
debug bool
|
// Feature flags
|
||||||
|
Features *features.Features
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
@@ -56,15 +58,17 @@ func CreateApp(options *Options) *App {
|
|||||||
myLogger.SetLogLevel(logger.INFO)
|
myLogger.SetLogLevel(logger.INFO)
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||||
Title: options.Title,
|
Title: options.Title,
|
||||||
Width: options.Width,
|
Width: options.Width,
|
||||||
Height: options.Height,
|
Height: options.Height,
|
||||||
MinWidth: options.MinWidth,
|
MinWidth: options.MinWidth,
|
||||||
MinHeight: options.MinHeight,
|
MinHeight: options.MinHeight,
|
||||||
MaxWidth: options.MaxWidth,
|
MaxWidth: options.MaxWidth,
|
||||||
MaxHeight: options.MaxHeight,
|
MaxHeight: options.MaxHeight,
|
||||||
StartHidden: options.StartHidden,
|
Frameless: options.Frameless,
|
||||||
DevTools: options.DevTools,
|
|
||||||
|
// This should be controlled by the compile time flags...
|
||||||
|
DevTools: true,
|
||||||
|
|
||||||
Resizable: !options.DisableResize,
|
Resizable: !options.DisableResize,
|
||||||
Fullscreen: options.Fullscreen,
|
Fullscreen: options.Fullscreen,
|
||||||
@@ -75,12 +79,9 @@ func CreateApp(options *Options) *App {
|
|||||||
webserver: webserver.NewWebServer(myLogger),
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
bindings: binding.NewBindings(myLogger),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
app.Init()
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
result := a.window.Run(dispatcher, bindingDump)
|
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||||
a.servicebus.Stop()
|
a.servicebus.Stop()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -192,3 +193,14 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
|
|
||||||
|
// Add the struct to the bindings
|
||||||
|
err := a.bindings.Add(structPtr)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
34
v2/internal/app/options.go
Normal file
34
v2/internal/app/options.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
// Options for creating the App
|
||||||
|
type Options struct {
|
||||||
|
Title string
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
DisableResize bool
|
||||||
|
Fullscreen bool
|
||||||
|
Frameless bool
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
MaxWidth int
|
||||||
|
MaxHeight int
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeDefaults will set the minimum default values for an application
|
||||||
|
func (o *Options) mergeDefaults() {
|
||||||
|
|
||||||
|
// Create a default title
|
||||||
|
if len(o.Title) == 0 {
|
||||||
|
o.Title = "My Wails App"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default width
|
||||||
|
if o.Width == 0 {
|
||||||
|
o.Width = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default height
|
||||||
|
if o.Height == 0 {
|
||||||
|
o.Height = 768
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,13 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||||
@@ -20,57 +17,36 @@ import (
|
|||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
appType string
|
|
||||||
|
|
||||||
binding *subsystem.Binding
|
binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
|
|
||||||
options *options.App
|
|
||||||
|
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
webserver *webserver.WebServer
|
webserver *webserver.WebServer
|
||||||
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
loglevelStore *runtime.Store
|
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
|
options.mergeDefaults()
|
||||||
|
// We ignore the inputs (for now)
|
||||||
|
|
||||||
// Merge default options
|
// TODO: Allow logger output override on CLI
|
||||||
options.MergeDefaults(appoptions)
|
myLogger := logger.New(os.Stdout)
|
||||||
|
myLogger.SetLogLevel(logger.TRACE)
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(appoptions.Logger)
|
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
appType: "server",
|
bindings: binding.NewBindings(myLogger),
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
logger: myLogger,
|
||||||
logger: myLogger,
|
servicebus: servicebus.New(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
webserver: webserver.NewWebServer(myLogger),
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise app
|
return result
|
||||||
result.Init()
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
@@ -107,21 +83,8 @@ func (a *App) Run() error {
|
|||||||
if debugMode {
|
if debugMode {
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the runtime
|
|
||||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtime
|
|
||||||
a.runtime.Start()
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
|
||||||
|
|
||||||
a.servicebus.Start()
|
a.servicebus.Start()
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,6 +97,14 @@ func (a *App) Run() error {
|
|||||||
a.dispatcher = dispatcher
|
a.dispatcher = dispatcher
|
||||||
a.dispatcher.Start()
|
a.dispatcher.Start()
|
||||||
|
|
||||||
|
// Start the runtime
|
||||||
|
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.runtime = runtime
|
||||||
|
a.runtime.Start()
|
||||||
|
|
||||||
// Start the binding subsystem
|
// Start the binding subsystem
|
||||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,7 +122,7 @@ func (a *App) Run() error {
|
|||||||
a.event.Start()
|
a.event.Start()
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -171,3 +142,14 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
|
|
||||||
|
// Add the struct to the bindings
|
||||||
|
err := a.bindings.Add(structPtr)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,55 +2,30 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bindings struct {
|
type Bindings struct {
|
||||||
db *DB
|
db *DB
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
exemptions slicer.StringSlicer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
// NewBindings returns a new Bindings object
|
||||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
func NewBindings(logger *logger.Logger) *Bindings {
|
||||||
result := &Bindings{
|
return &Bindings{
|
||||||
db: newDB(),
|
db: newDB(),
|
||||||
logger: logger.CustomLogger("Bindings"),
|
logger: logger.CustomLogger("Bindings"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, exemption := range exemptions {
|
|
||||||
if exemptions == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
|
||||||
// Yuk yuk yuk! Is there a better way?
|
|
||||||
name = strings.TrimSuffix(name, "-fm")
|
|
||||||
result.exemptions.Add(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the structs to bind
|
|
||||||
for _, ptr := range structPointersToBind {
|
|
||||||
err := result.Add(ptr)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the given struct methods to the Bindings
|
// Add the given struct methods to the Bindings
|
||||||
func (b *Bindings) Add(structPtr interface{}) error {
|
func (b *Bindings) Add(structPtr interface{}) error {
|
||||||
|
|
||||||
methods, err := b.getMethods(structPtr)
|
methods, err := getMethods(structPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
return fmt.Errorf("unable to Add() - %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
@@ -59,8 +34,29 @@ func (b *Bindings) Add(structPtr interface{}) error {
|
|||||||
structName := splitName[1]
|
structName := splitName[1]
|
||||||
methodName := splitName[2]
|
methodName := splitName[2]
|
||||||
|
|
||||||
|
// Is this WailsInit?
|
||||||
|
if method.IsWailsInit() {
|
||||||
|
err := b.db.AddWailsInit(method)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.logger.Trace("Registered WailsInit method: %s", method.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this WailsShutdown?
|
||||||
|
if method.IsWailsShutdown() {
|
||||||
|
err := b.db.AddWailsShutdown(method)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.logger.Trace("Registered WailsShutdown method: %s", method.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Add it as a regular method
|
// Add it as a regular method
|
||||||
b.db.AddMethod(packageName, structName, methodName, method)
|
b.db.AddMethod(packageName, structName, methodName, method)
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package binding
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BoundMethod defines all the data related to a Go method that is
|
// BoundMethod defines all the data related to a Go method that is
|
||||||
@@ -16,6 +16,58 @@ type BoundMethod struct {
|
|||||||
Method reflect.Value `json:"-"`
|
Method reflect.Value `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsWailsInit returns true if the method name is "WailsInit"
|
||||||
|
func (b *BoundMethod) IsWailsInit() bool {
|
||||||
|
return strings.HasSuffix(b.Name, "WailsInit")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWailsShutdown returns true if the method name is "WailsShutdown"
|
||||||
|
func (b *BoundMethod) IsWailsShutdown() bool {
|
||||||
|
return strings.HasSuffix(b.Name, "WailsShutdown")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWailsInit checks if the WailsInit signature is correct
|
||||||
|
func (b *BoundMethod) VerifyWailsInit() error {
|
||||||
|
// Must only have 1 input
|
||||||
|
if b.InputCount() != 1 {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check input type
|
||||||
|
if !b.Inputs[0].IsType("*goruntime.Runtime") {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must only have 1 output
|
||||||
|
if b.OutputCount() != 1 {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check output type
|
||||||
|
if !b.Outputs[0].IsError() {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input must be of type Runtime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWailsShutdown checks if the WailsShutdown signature is correct
|
||||||
|
func (b *BoundMethod) VerifyWailsShutdown() error {
|
||||||
|
// Must have no inputs
|
||||||
|
if b.InputCount() != 0 {
|
||||||
|
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must have no outputs
|
||||||
|
if b.OutputCount() != 0 {
|
||||||
|
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input must be of type Runtime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// InputCount returns the number of inputs this bound method has
|
// InputCount returns the number of inputs this bound method has
|
||||||
func (b *BoundMethod) InputCount() int {
|
func (b *BoundMethod) InputCount() int {
|
||||||
return len(b.Inputs)
|
return len(b.Inputs)
|
||||||
@@ -26,29 +78,6 @@ func (b *BoundMethod) OutputCount() int {
|
|||||||
return len(b.Outputs)
|
return len(b.Outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseArgs method converts the input json into the types expected by the method
|
|
||||||
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()
|
|
||||||
err := json.Unmarshal(arg, inputValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if inputValue == nil {
|
|
||||||
result[index] = reflect.Zero(typ).Interface()
|
|
||||||
} else {
|
|
||||||
result[index] = reflect.ValueOf(inputValue).Elem().Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call will attempt to call this bound method with the given args
|
// Call will attempt to call this bound method with the given args
|
||||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||||
// Check inputs
|
// Check inputs
|
||||||
@@ -65,8 +94,17 @@ func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
|||||||
|
|
||||||
// Iterate over given arguments
|
// Iterate over given arguments
|
||||||
for index, arg := range args {
|
for index, arg := range args {
|
||||||
|
|
||||||
|
// Attempt to convert the argument to the type expected by the method
|
||||||
|
value, err := convertArgToValue(arg, b.Inputs[index])
|
||||||
|
|
||||||
|
// If it fails, return a suitable error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s (parameter %d): %s", b.Name, index+1, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Save the converted argument
|
// Save the converted argument
|
||||||
callArgs[index] = reflect.ValueOf(arg)
|
callArgs[index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the call
|
// Do the call
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ type DB struct {
|
|||||||
// It used for performance gains at runtime
|
// It used for performance gains at runtime
|
||||||
methodMap map[string]*BoundMethod
|
methodMap map[string]*BoundMethod
|
||||||
|
|
||||||
|
// These are slices of methods registered using WailsInit and WailsShutdown
|
||||||
|
wailsInitMethods []*BoundMethod
|
||||||
|
wailsShutdownMethods []*BoundMethod
|
||||||
|
|
||||||
// Lock to ensure sync access to the data
|
// Lock to ensure sync access to the data
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -90,6 +94,38 @@ func (d *DB) AddMethod(packageName string, structName string, methodName string,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddWailsInit checks the given method is a WailsInit method and if it
|
||||||
|
// is, adds it to the list of WailsInit methods
|
||||||
|
func (d *DB) AddWailsInit(method *BoundMethod) error {
|
||||||
|
err := method.VerifyWailsInit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the db whilst processing and unlock on return
|
||||||
|
d.lock.Lock()
|
||||||
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
|
d.wailsInitMethods = append(d.wailsInitMethods, method)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWailsShutdown checks the given method is a WailsInit method and if it
|
||||||
|
// is, adds it to the list of WailsShutdown methods
|
||||||
|
func (d *DB) AddWailsShutdown(method *BoundMethod) error {
|
||||||
|
err := method.VerifyWailsShutdown()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the db whilst processing and unlock on return
|
||||||
|
d.lock.Lock()
|
||||||
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
|
d.wailsShutdownMethods = append(d.wailsShutdownMethods, method)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToJSON converts the method map to JSON
|
// ToJSON converts the method map to JSON
|
||||||
func (d *DB) ToJSON() (string, error) {
|
func (d *DB) ToJSON() (string, error) {
|
||||||
|
|
||||||
@@ -102,3 +138,13 @@ func (d *DB) ToJSON() (string, error) {
|
|||||||
// Return zero copy string as this string will be read only
|
// Return zero copy string as this string will be read only
|
||||||
return *(*string)(unsafe.Pointer(&bytes)), err
|
return *(*string)(unsafe.Pointer(&bytes)), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WailsInitMethods returns the list of registered WailsInit methods
|
||||||
|
func (d *DB) WailsInitMethods() []*BoundMethod {
|
||||||
|
return d.wailsInitMethods
|
||||||
|
}
|
||||||
|
|
||||||
|
// WailsShutdownMethods returns the list of registered WailsInit methods
|
||||||
|
func (d *DB) WailsShutdownMethods() []*BoundMethod {
|
||||||
|
return d.wailsShutdownMethods
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package binding
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a
|
// isStructPtr returns true if the value given is a
|
||||||
@@ -13,35 +12,14 @@ func isStructPtr(value interface{}) bool {
|
|||||||
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFunction returns true if the given value is a function
|
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||||
func isFunction(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Func
|
|
||||||
}
|
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a struct
|
|
||||||
func isStruct(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|
||||||
|
|
||||||
// Create result placeholder
|
// Create result placeholder
|
||||||
var result []*BoundMethod
|
var result []*BoundMethod
|
||||||
|
|
||||||
// Check type
|
// Check type
|
||||||
if !isStructPtr(value) {
|
if !isStructPtr(value) {
|
||||||
|
return nil, fmt.Errorf("not a pointer to an interface")
|
||||||
if isStruct(value) {
|
|
||||||
name := reflect.ValueOf(value).Type().Name()
|
|
||||||
return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFunction(value) {
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
|
|
||||||
return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct.", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a pointer to a struct.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process Struct
|
// Process Struct
|
||||||
@@ -56,11 +34,6 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
fullMethodName := baseName + "." + methodName
|
fullMethodName := baseName + "." + methodName
|
||||||
method := structValue.MethodByName(methodName)
|
method := structValue.MethodByName(methodName)
|
||||||
|
|
||||||
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
|
||||||
if b.exemptions.Contains(methodReflectName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new method
|
// Create new method
|
||||||
boundMethod := &BoundMethod{
|
boundMethod := &BoundMethod{
|
||||||
Name: fullMethodName,
|
Name: fullMethodName,
|
||||||
@@ -83,8 +56,6 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
boundMethod.Inputs = inputs
|
boundMethod.Inputs = inputs
|
||||||
|
|
||||||
// Iterate outputs
|
// Iterate outputs
|
||||||
// TODO: Determine what to do about limiting return types
|
|
||||||
// especially around errors.
|
|
||||||
outputParamCount := methodType.NumOut()
|
outputParamCount := methodType.NumOut()
|
||||||
var outputs []*Parameter
|
var outputs []*Parameter
|
||||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
||||||
@@ -100,3 +71,39 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertArgToValue
|
||||||
|
func convertArgToValue(input interface{}, target *Parameter) (result reflect.Value, err error) {
|
||||||
|
|
||||||
|
// Catch type conversion panics thrown by convert
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// Modify error
|
||||||
|
err = fmt.Errorf("%s", r.(string)[23:])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Do the conversion
|
||||||
|
|
||||||
|
// Handle nil values
|
||||||
|
if input == nil {
|
||||||
|
switch target.reflectType.Kind() {
|
||||||
|
case reflect.Chan,
|
||||||
|
reflect.Func,
|
||||||
|
reflect.Interface,
|
||||||
|
reflect.Map,
|
||||||
|
reflect.Ptr,
|
||||||
|
reflect.Slice:
|
||||||
|
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||||
|
default:
|
||||||
|
return reflect.Zero(target.reflectType), fmt.Errorf("Unable to use null value")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't like doing this but it's the only way to
|
||||||
|
// handle recover() correctly
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
13
v2/internal/features/features.go
Normal file
13
v2/internal/features/features.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
// Features holds generic and platform specific feature flags
|
||||||
|
type Features struct {
|
||||||
|
Linux *Linux
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Features object
|
||||||
|
func New() *Features {
|
||||||
|
return &Features{
|
||||||
|
Linux: &Linux{},
|
||||||
|
}
|
||||||
|
}
|
||||||
5
v2/internal/features/features_linux.go
Normal file
5
v2/internal/features/features_linux.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
// Linux holds linux specific feature flags
|
||||||
|
type Linux struct {
|
||||||
|
}
|
||||||
23
v2/internal/ffenestri/features_linux.go
Normal file
23
v2/internal/ffenestri/features_linux.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package ffenestri
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||||
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "ffenestri.h"
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
|
func (a *Application) processOSFeatureFlags(features *features.Features) {
|
||||||
|
|
||||||
|
// Process Linux features
|
||||||
|
// linux := features.Linux
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,11 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -17,9 +15,6 @@ import (
|
|||||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
|
||||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ffenestri.h"
|
#include "ffenestri.h"
|
||||||
|
|
||||||
@@ -27,16 +22,42 @@ import (
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
// DEBUG is the global Ffenestri debug flag.
|
||||||
|
// TODO: move to compile time.
|
||||||
|
var DEBUG bool = true
|
||||||
|
|
||||||
|
// Config defines how our application should be configured
|
||||||
|
type Config struct {
|
||||||
|
Title string
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
MaxWidth int
|
||||||
|
MaxHeight int
|
||||||
|
DevTools bool
|
||||||
|
Resizable bool
|
||||||
|
Fullscreen bool
|
||||||
|
Frameless bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultConfig = &Config{
|
||||||
|
Title: "My Wails App",
|
||||||
|
Width: 800,
|
||||||
|
Height: 600,
|
||||||
|
DevTools: true,
|
||||||
|
Resizable: true,
|
||||||
|
Fullscreen: false,
|
||||||
|
Frameless: false,
|
||||||
|
}
|
||||||
|
|
||||||
// Application is our main application object
|
// Application is our main application object
|
||||||
type Application struct {
|
type Application struct {
|
||||||
config *options.App
|
config *Config
|
||||||
memory []unsafe.Pointer
|
memory []unsafe.Pointer
|
||||||
|
|
||||||
// This is the main app pointer
|
// This is the main app pointer
|
||||||
app *C.struct_Application
|
app unsafe.Pointer
|
||||||
|
|
||||||
// Manages menus
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -57,18 +78,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewApplicationWithConfig creates a new application based on the given config
|
// NewApplicationWithConfig creates a new application based on the given config
|
||||||
func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuManager *menumanager.Manager) *Application {
|
func NewApplicationWithConfig(config *Config, logger *logger.Logger) *Application {
|
||||||
return &Application{
|
return &Application{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.CustomLogger("Ffenestri"),
|
logger: logger.CustomLogger("Ffenestri"),
|
||||||
menuManager: menuManager,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApplication creates a new Application with the default config
|
// NewApplication creates a new Application with the default config
|
||||||
func NewApplication(logger *logger.Logger) *Application {
|
func NewApplication(logger *logger.Logger) *Application {
|
||||||
return &Application{
|
return &Application{
|
||||||
config: options.Default,
|
config: defaultConfig,
|
||||||
logger: logger.CustomLogger("Ffenestri"),
|
logger: logger.CustomLogger("Ffenestri"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,29 +121,18 @@ type DispatchClient interface {
|
|||||||
SendMessage(string)
|
SendMessage(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func intToColour(colour int) (C.int, C.int, C.int, C.int) {
|
|
||||||
var alpha = C.int(colour & 0xFF)
|
|
||||||
var blue = C.int((colour >> 8) & 0xFF)
|
|
||||||
var green = C.int((colour >> 16) & 0xFF)
|
|
||||||
var red = C.int((colour >> 24) & 0xFF)
|
|
||||||
return red, green, blue, alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug bool) error {
|
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, features *features.Features) error {
|
||||||
title := a.string2CString(a.config.Title)
|
title := a.string2CString(a.config.Title)
|
||||||
width := C.int(a.config.Width)
|
width := C.int(a.config.Width)
|
||||||
height := C.int(a.config.Height)
|
height := C.int(a.config.Height)
|
||||||
resizable := a.bool2Cint(!a.config.DisableResize)
|
resizable := a.bool2Cint(a.config.Resizable)
|
||||||
devtools := a.bool2Cint(a.config.DevTools)
|
devtools := a.bool2Cint(a.config.DevTools)
|
||||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen)
|
||||||
logLevel := C.int(a.config.LogLevel)
|
|
||||||
hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose)
|
|
||||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose)
|
|
||||||
|
|
||||||
// Save app reference
|
// Save app reference
|
||||||
a.app = (*C.struct_Application)(app)
|
a.app = unsafe.Pointer(app)
|
||||||
|
|
||||||
// Set Min Window Size
|
// Set Min Window Size
|
||||||
minWidth := C.int(a.config.MinWidth)
|
minWidth := C.int(a.config.MinWidth)
|
||||||
@@ -136,16 +145,11 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
||||||
|
|
||||||
// Set debug if needed
|
// Set debug if needed
|
||||||
C.SetDebug(app, a.bool2Cint(debug))
|
C.SetDebug(app, a.bool2Cint(DEBUG))
|
||||||
|
|
||||||
// TODO: Move frameless to Linux options
|
// Set Frameless
|
||||||
// if a.config.Frameless {
|
if a.config.Frameless {
|
||||||
// C.DisableFrame(a.app)
|
C.DisableFrame(a.app)
|
||||||
// }
|
|
||||||
|
|
||||||
if a.config.RGBA != 0 {
|
|
||||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
|
||||||
C.SetColour(a.app, r, g, b, alpha)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape bindings so C doesn't freak out
|
// Escape bindings so C doesn't freak out
|
||||||
@@ -154,16 +158,13 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
// Set bindings
|
// Set bindings
|
||||||
C.SetBindings(app, a.string2CString(bindings))
|
C.SetBindings(app, a.string2CString(bindings))
|
||||||
|
|
||||||
|
// Process feature flags
|
||||||
|
a.processFeatureFlags(features)
|
||||||
|
|
||||||
// save the dispatcher in a package variable so that the C callbacks
|
// save the dispatcher in a package variable so that the C callbacks
|
||||||
// can access it
|
// can access it
|
||||||
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
||||||
|
|
||||||
// Process platform settings
|
|
||||||
err := a.processPlatformSettings()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we could initialise the application
|
// Check we could initialise the application
|
||||||
if app != nil {
|
if app != nil {
|
||||||
// Yes - Save memory reference and run app, cleaning up afterwards
|
// Yes - Save memory reference and run app, cleaning up afterwards
|
||||||
@@ -185,3 +186,11 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
func messageFromWindowCallback(data *C.char) {
|
func messageFromWindowCallback(data *C.char) {
|
||||||
dispatcher.DispatchMessage(C.GoString(data))
|
dispatcher.DispatchMessage(C.GoString(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Application) processFeatureFlags(features *features.Features) {
|
||||||
|
|
||||||
|
// Process generic features
|
||||||
|
|
||||||
|
// Process OS Specific flags
|
||||||
|
a.processOSFeatureFlags(features)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,44 +2,23 @@
|
|||||||
#define __FFENESTRI_H__
|
#define __FFENESTRI_H__
|
||||||
|
|
||||||
#include <stdio.h>
|
#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, int hideWindowOnClose);
|
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen);
|
||||||
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
|
||||||
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
|
||||||
extern void Run(struct Application*, int argc, char **argv);
|
extern void Run(void *app, int argc, char **argv);
|
||||||
extern void DestroyApplication(struct Application*);
|
extern void DestroyApplication(void *app);
|
||||||
extern void SetDebug(struct Application*, int flag);
|
extern void SetDebug(void *app, int flag);
|
||||||
extern void SetBindings(struct Application*, const char *bindings);
|
extern void SetBindings(void *app, const char *bindings);
|
||||||
extern void ExecJS(struct Application*, const char *script);
|
extern void ExecJS(void *app, const char *script);
|
||||||
extern void Hide(struct Application*);
|
extern void Quit(void *app);
|
||||||
extern void Show(struct Application*);
|
extern void SetTitle(void *app, const char *title);
|
||||||
extern void Center(struct Application*);
|
extern void Fullscreen(void *app);
|
||||||
extern void Maximise(struct Application*);
|
extern void UnFullscreen(void *app);
|
||||||
extern void Unmaximise(struct Application*);
|
extern int SetColour(void *app, const char *colourString);
|
||||||
extern void ToggleMaximise(struct Application*);
|
extern void DisableFrame(void *app);
|
||||||
extern void Minimise(struct Application*);
|
extern char *SaveFileDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void Unminimise(struct Application*);
|
extern char *OpenFileDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void ToggleMinimise(struct Application*);
|
extern char *OpenDirectoryDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void SetColour(struct Application*, int red, int green, int blue, int alpha);
|
|
||||||
extern void SetSize(struct Application*, int width, int height);
|
|
||||||
extern void SetPosition(struct Application*, int x, int y);
|
|
||||||
extern void Quit(struct Application*);
|
|
||||||
extern void SetTitle(struct Application*, const char *title);
|
|
||||||
extern void Fullscreen(struct Application*);
|
|
||||||
extern void UnFullscreen(struct Application*);
|
|
||||||
extern void ToggleFullscreen(struct Application*);
|
|
||||||
extern void DisableFrame(struct Application*);
|
|
||||||
extern void OpenDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories);
|
|
||||||
extern void SaveDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
|
||||||
extern void MessageDialog(struct Application*, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton);
|
|
||||||
extern void DarkModeEnabled(struct Application*, char *callbackID);
|
|
||||||
extern void SetApplicationMenu(struct Application*, const char *);
|
|
||||||
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void DeleteTrayMenuByID(struct Application*, const char *id);
|
|
||||||
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
|
||||||
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
@@ -67,148 +66,44 @@ func (c *Client) WindowUnFullscreen() {
|
|||||||
C.UnFullscreen(c.app.app)
|
C.UnFullscreen(c.app.app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowShow will show the window
|
|
||||||
func (c *Client) WindowShow() {
|
|
||||||
C.Show(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowHide will hide the window
|
|
||||||
func (c *Client) WindowHide() {
|
|
||||||
C.Hide(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowCenter will hide the window
|
|
||||||
func (c *Client) WindowCenter() {
|
|
||||||
C.Center(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowMaximise will maximise the window
|
|
||||||
func (c *Client) WindowMaximise() {
|
|
||||||
C.Maximise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowMinimise will minimise the window
|
|
||||||
func (c *Client) WindowMinimise() {
|
|
||||||
C.Minimise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowUnmaximise will unmaximise the window
|
|
||||||
func (c *Client) WindowUnmaximise() {
|
|
||||||
C.Unmaximise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowUnminimise will unminimise the window
|
|
||||||
func (c *Client) WindowUnminimise() {
|
|
||||||
C.Unminimise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowPosition will position the window to x,y on the
|
|
||||||
// monitor that the window is mostly on
|
|
||||||
func (c *Client) WindowPosition(x int, y int) {
|
|
||||||
C.SetPosition(c.app.app, C.int(x), C.int(y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowSize will resize the window to the given
|
|
||||||
// width and height
|
|
||||||
func (c *Client) WindowSize(width int, height int) {
|
|
||||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WindowSetMinSize(width int, height int) {
|
|
||||||
C.SetMinWindowSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WindowSetMaxSize(width int, height int) {
|
|
||||||
C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowSetColour sets the window colour
|
// WindowSetColour sets the window colour
|
||||||
func (c *Client) WindowSetColour(colour int) {
|
func (c *Client) WindowSetColour(colour string) bool {
|
||||||
r, g, b, a := intToColour(colour)
|
result := C.SetColour(c.app.app, c.app.string2CString(colour))
|
||||||
C.SetColour(c.app.app, r, g, b, a)
|
return result == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDialog will open a dialog with the given title and filter
|
// OpenFileDialog will open a file dialog with the given title
|
||||||
func (c *Client) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
func (c *Client) OpenFileDialog(title string) string {
|
||||||
C.OpenDialog(c.app.app,
|
cstring := C.OpenFileDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
c.app.string2CString(callbackID),
|
var result string
|
||||||
c.app.string2CString(dialogOptions.Title),
|
if cstring != nil {
|
||||||
c.app.string2CString(dialogOptions.Filters),
|
result = C.GoString(cstring)
|
||||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
// Free the C string that was allocated by the dialog
|
||||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
C.free(unsafe.Pointer(cstring))
|
||||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.AllowMultiple),
|
|
||||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.ResolvesAliases),
|
|
||||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveDialog will open a dialog with the given title and filter
|
|
||||||
func (c *Client) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
|
||||||
C.SaveDialog(c.app.app,
|
|
||||||
c.app.string2CString(callbackID),
|
|
||||||
c.app.string2CString(dialogOptions.Title),
|
|
||||||
c.app.string2CString(dialogOptions.Filters),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
|
||||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageDialog will open a message dialog with the given options
|
|
||||||
func (c *Client) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
|
||||||
|
|
||||||
// Sanity check button length
|
|
||||||
if len(dialogOptions.Buttons) > 4 {
|
|
||||||
c.app.logger.Error("Given %d message dialog buttons. Maximum is 4", len(dialogOptions.Buttons))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Process buttons
|
// SaveFileDialog will open a save file dialog with the given title
|
||||||
buttons := []string{"", "", "", ""}
|
func (c *Client) SaveFileDialog(title string) string {
|
||||||
for i, button := range dialogOptions.Buttons {
|
cstring := C.SaveFileDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
buttons[i] = button
|
var result string
|
||||||
|
if cstring != nil {
|
||||||
|
result = C.GoString(cstring)
|
||||||
|
// Free the C string that was allocated by the dialog
|
||||||
|
C.free(unsafe.Pointer(cstring))
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
C.MessageDialog(c.app.app,
|
|
||||||
c.app.string2CString(callbackID),
|
|
||||||
c.app.string2CString(string(dialogOptions.Type)),
|
|
||||||
c.app.string2CString(dialogOptions.Title),
|
|
||||||
c.app.string2CString(dialogOptions.Message),
|
|
||||||
c.app.string2CString(dialogOptions.Icon),
|
|
||||||
c.app.string2CString(buttons[0]),
|
|
||||||
c.app.string2CString(buttons[1]),
|
|
||||||
c.app.string2CString(buttons[2]),
|
|
||||||
c.app.string2CString(buttons[3]),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultButton),
|
|
||||||
c.app.string2CString(dialogOptions.CancelButton))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
// OpenDirectoryDialog will open a directory dialog with the given title
|
||||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
func (c *Client) OpenDirectoryDialog(title string) string {
|
||||||
}
|
cstring := C.OpenDirectoryDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
|
var result string
|
||||||
func (c *Client) SetApplicationMenu(applicationMenuJSON string) {
|
if cstring != nil {
|
||||||
C.SetApplicationMenu(c.app.app, c.app.string2CString(applicationMenuJSON))
|
result = C.GoString(cstring)
|
||||||
}
|
// Free the C string that was allocated by the dialog
|
||||||
|
C.free(unsafe.Pointer(cstring))
|
||||||
func (c *Client) SetTrayMenu(trayMenuJSON string) {
|
}
|
||||||
C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON))
|
return result
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
|
||||||
C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
|
||||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) DeleteTrayMenuByID(id string) {
|
|
||||||
C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ struct Application
|
|||||||
int height;
|
int height;
|
||||||
int resizable;
|
int resizable;
|
||||||
int devtools;
|
int devtools;
|
||||||
int startHidden;
|
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
int minWidth;
|
int minWidth;
|
||||||
int minHeight;
|
int minHeight;
|
||||||
@@ -101,7 +100,7 @@ struct Application
|
|||||||
int lock;
|
int lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden)
|
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen)
|
||||||
{
|
{
|
||||||
// Setup main application struct
|
// Setup main application struct
|
||||||
struct Application *result = malloc(sizeof(struct Application));
|
struct Application *result = malloc(sizeof(struct Application));
|
||||||
@@ -116,11 +115,15 @@ void *NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->maxWidth = 0;
|
result->maxWidth = 0;
|
||||||
result->maxHeight = 0;
|
result->maxHeight = 0;
|
||||||
result->frame = 1;
|
result->frame = 1;
|
||||||
result->startHidden = startHidden;
|
|
||||||
|
|
||||||
// Default drag button is PRIMARY
|
// Default drag button is PRIMARY
|
||||||
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
||||||
|
|
||||||
|
// printf("\n\nWidth: %d\n", result->width);
|
||||||
|
// printf("Height: %d\n\n", result->height);
|
||||||
|
|
||||||
|
// Features
|
||||||
|
|
||||||
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
||||||
|
|
||||||
// Create a unique ID based on the current unix timestamp
|
// Create a unique ID based on the current unix timestamp
|
||||||
@@ -136,9 +139,10 @@ void *NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
return (void *)result;
|
return (void *)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyApplication(struct Application *app)
|
void DestroyApplication(void *appPointer)
|
||||||
{
|
{
|
||||||
Debug("Destroying Application");
|
Debug("Destroying Application");
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
|
||||||
g_application_quit(G_APPLICATION(app->application));
|
g_application_quit(G_APPLICATION(app->application));
|
||||||
|
|
||||||
@@ -167,11 +171,9 @@ void DestroyApplication(struct Application *app)
|
|||||||
// Disconnect signal handlers
|
// Disconnect signal handlers
|
||||||
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
||||||
g_signal_handler_disconnect(manager, app->signalInvoke);
|
g_signal_handler_disconnect(manager, app->signalInvoke);
|
||||||
if( app->frame == 0) {
|
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
||||||
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
||||||
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
||||||
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
|
||||||
}
|
|
||||||
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
||||||
|
|
||||||
// Release the main GTK Application
|
// Release the main GTK Application
|
||||||
@@ -189,34 +191,45 @@ void DestroyApplication(struct Application *app)
|
|||||||
|
|
||||||
// Quit will stop the gtk application and free up all the memory
|
// Quit will stop the gtk application and free up all the memory
|
||||||
// used by the application
|
// used by the application
|
||||||
void Quit(struct Application *app)
|
void Quit(void *appPointer)
|
||||||
{
|
{
|
||||||
Debug("Quit Called");
|
Debug("Quit Called");
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_close((GtkWindow *)app->mainWindow);
|
gtk_window_close((GtkWindow *)app->mainWindow);
|
||||||
g_application_quit((GApplication *)app->application);
|
g_application_quit((GApplication *)app->application);
|
||||||
DestroyApplication(app);
|
DestroyApplication(appPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTitle sets the main window title to the given string
|
// SetTitle sets the main window title to the given string
|
||||||
void SetTitle(struct Application *app, const char *title)
|
void SetTitle(void *appPointer, const char *title)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_set_title(app->mainWindow, title);
|
gtk_window_set_title(app->mainWindow, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fullscreen sets the main window to be fullscreen
|
// Fullscreen sets the main window to be fullscreen
|
||||||
void Fullscreen(struct Application *app)
|
void Fullscreen(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_fullscreen(app->mainWindow);
|
gtk_window_fullscreen(app->mainWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnFullscreen resets the main window after a fullscreen
|
// UnFullscreen resets the main window after a fullscreen
|
||||||
void UnFullscreen(struct Application *app)
|
void UnFullscreen(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_unfullscreen(app->mainWindow);
|
gtk_window_unfullscreen(app->mainWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMinMaxSize(struct Application *app)
|
void Center(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinMaxSize(void *appPointer)
|
||||||
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GdkGeometry size;
|
GdkGeometry size;
|
||||||
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
@@ -232,43 +245,32 @@ void setMinMaxSize(struct Application *app)
|
|||||||
size.min_width = app->minWidth;
|
size.min_width = app->minWidth;
|
||||||
flags |= GDK_HINT_MIN_SIZE;
|
flags |= GDK_HINT_MIN_SIZE;
|
||||||
}
|
}
|
||||||
|
Debug("size: %dx%d", app->width, app->height);
|
||||||
|
Debug("min: %dx%d", size.min_width, size.min_height);
|
||||||
|
Debug("max: %dx%d", size.max_width, size.max_height);
|
||||||
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) {
|
// OpenFileDialog opens a dialog to select a file
|
||||||
|
// NOTE: The result is a string that will need to be freed!
|
||||||
|
char *OpenFileDialog(void *appPointer, char *title)
|
||||||
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GtkFileChooserNative *native;
|
GtkFileChooserNative *native;
|
||||||
GtkFileChooserAction action = chooserAction;
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||||
gint res;
|
gint res;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
char *title = args[0];
|
|
||||||
char *filter = args[1];
|
|
||||||
|
|
||||||
native = gtk_file_chooser_native_new(title,
|
native = gtk_file_chooser_native_new(title,
|
||||||
app->mainWindow,
|
app->mainWindow,
|
||||||
action,
|
action,
|
||||||
"_Open",
|
"_Open",
|
||||||
"_Cancel");
|
"_Cancel");
|
||||||
|
|
||||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
|
||||||
|
|
||||||
// If we have filters, process them
|
|
||||||
if (filter[0] != '\0') {
|
|
||||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
|
||||||
gchar **filters = g_strsplit(filter, ",", -1);
|
|
||||||
gint i;
|
|
||||||
for(i = 0; filters && filters[i]; i++) {
|
|
||||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
|
||||||
// Debug("Adding filter pattern: %s\n", filters[i]);
|
|
||||||
}
|
|
||||||
gtk_file_filter_set_name(file_filter, filter);
|
|
||||||
gtk_file_chooser_add_filter(chooser, file_filter);
|
|
||||||
g_strfreev(filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
if (res == GTK_RESPONSE_ACCEPT)
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
{
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
filename = gtk_file_chooser_get_filename(chooser);
|
filename = gtk_file_chooser_get_filename(chooser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,65 +279,104 @@ char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAc
|
|||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// openFileDialogInternal opens a dialog to select a file
|
// SaveFileDialog opens a dialog to select a file
|
||||||
// NOTE: The result is a string that will need to be freed!
|
// NOTE: The result is a string that will need to be freed!
|
||||||
char *openFileDialogInternal(struct Application *app, char **args)
|
char *SaveFileDialog(void *appPointer, char *title)
|
||||||
{
|
{
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args);
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
GtkFileChooserNative *native;
|
||||||
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
|
||||||
|
gint res;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
native = gtk_file_chooser_native_new(title,
|
||||||
|
app->mainWindow,
|
||||||
|
action,
|
||||||
|
"_Save",
|
||||||
|
"_Cancel");
|
||||||
|
|
||||||
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
|
filename = gtk_file_chooser_get_filename(chooser);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(native);
|
||||||
|
|
||||||
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveFileDialogInternal opens a dialog to select a file
|
// OpenDirectoryDialog opens a dialog to select a directory
|
||||||
// NOTE: The result is a string that will need to be freed!
|
// NOTE: The result is a string that will need to be freed!
|
||||||
char *saveFileDialogInternal(struct Application *app, char **args)
|
char *OpenDirectoryDialog(void *appPointer, char *title)
|
||||||
{
|
{
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args);
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
GtkFileChooserNative *native;
|
||||||
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||||
|
gint res;
|
||||||
|
char *foldername;
|
||||||
|
|
||||||
|
native = gtk_file_chooser_native_new(title,
|
||||||
|
app->mainWindow,
|
||||||
|
action,
|
||||||
|
"_Open",
|
||||||
|
"_Cancel");
|
||||||
|
|
||||||
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
|
foldername = gtk_file_chooser_get_filename(chooser);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(native);
|
||||||
|
|
||||||
|
return foldername;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetMinWindowSize(void *appPointer, int minWidth, int minHeight)
|
||||||
// openDirectoryDialogInternal opens a dialog to select a directory
|
|
||||||
// NOTE: The result is a string that will need to be freed!
|
|
||||||
char *openDirectoryDialogInternal(struct Application *app, char **args)
|
|
||||||
{
|
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->minWidth = minWidth;
|
app->minWidth = minWidth;
|
||||||
app->minHeight = minHeight;
|
app->minHeight = minHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
void SetMaxWindowSize(void *appPointer, int maxWidth, int maxHeight)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->maxWidth = maxWidth;
|
app->maxWidth = maxWidth;
|
||||||
app->maxHeight = maxHeight;
|
app->maxHeight = maxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetColour sets the colour of the webview to the given colour string
|
// SetColour sets the colour of the webview to the given colour string
|
||||||
int SetColour(struct Application *app, const char *colourString)
|
int SetColour(void *appPointer, const char *colourString)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GdkRGBA rgba;
|
GdkRGBA rgba;
|
||||||
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||||
if (result == FALSE)
|
if (result == FALSE)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Debug("Setting webview colour to: %s", colourString);
|
Debug("Setting webview colour to: %s", colourString);
|
||||||
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableFrame disables the window frame
|
// DisableFrame disables the window frame
|
||||||
void DisableFrame(struct Application *app)
|
void DisableFrame(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->frame = 0;
|
app->frame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncCallback(GObject *source_object,
|
void syncCallback(GObject *source_object,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
void *data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
struct Application *app = (struct Application *)data;
|
|
||||||
|
struct Application *app = (struct Application *)user_data;
|
||||||
app->lock = 0;
|
app->lock = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,6 +384,8 @@ void syncEval(struct Application *app, const gchar *script)
|
|||||||
{
|
{
|
||||||
|
|
||||||
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
||||||
|
// Debug("[%p] webview\n", webView);
|
||||||
|
// Debug("[%p] Running sync\n", script);
|
||||||
|
|
||||||
// wait for lock to free
|
// wait for lock to free
|
||||||
while (app->lock == 1)
|
while (app->lock == 1)
|
||||||
@@ -352,15 +395,18 @@ void syncEval(struct Application *app, const gchar *script)
|
|||||||
// Set lock
|
// Set lock
|
||||||
app->lock = 1;
|
app->lock = 1;
|
||||||
|
|
||||||
|
//
|
||||||
webkit_web_view_run_javascript(
|
webkit_web_view_run_javascript(
|
||||||
webView,
|
webView,
|
||||||
script,
|
script,
|
||||||
NULL, syncCallback, (void*)app);
|
NULL, syncCallback, app);
|
||||||
|
|
||||||
while (app->lock == 1)
|
while (app->lock == 1)
|
||||||
{
|
{
|
||||||
|
// Debug("[%p] Waiting for callback\n", script);
|
||||||
g_main_context_iteration(0, true);
|
g_main_context_iteration(0, true);
|
||||||
}
|
}
|
||||||
|
// Debug("[%p] Finished\n", script);
|
||||||
}
|
}
|
||||||
|
|
||||||
void asyncEval(WebKitWebView *webView, const gchar *script)
|
void asyncEval(WebKitWebView *webView, const gchar *script)
|
||||||
@@ -371,7 +417,7 @@ void asyncEval(WebKitWebView *webView, const gchar *script)
|
|||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*dispatchMethod)(struct Application *app, void *);
|
typedef void (*dispatchMethod)(void *app, void *);
|
||||||
|
|
||||||
struct dispatchData
|
struct dispatchData
|
||||||
{
|
{
|
||||||
@@ -383,14 +429,20 @@ struct dispatchData
|
|||||||
gboolean executeMethod(gpointer data)
|
gboolean executeMethod(gpointer data)
|
||||||
{
|
{
|
||||||
struct dispatchData *d = (struct dispatchData *)data;
|
struct dispatchData *d = (struct dispatchData *)data;
|
||||||
(d->method)(d->app, d->args);
|
struct Application *app = (struct Application *)(d->app);
|
||||||
|
// Debug("Webview %p\n", app->webView);
|
||||||
|
// Debug("Args %s\n", d->args);
|
||||||
|
// Debug("Method %p\n", (d->method));
|
||||||
|
(d->method)(app, d->args);
|
||||||
|
// Debug("Method Execute Complete. Freeing memory");
|
||||||
g_free(d);
|
g_free(d);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecJS(struct Application *app, char *js)
|
void ExecJS(void *app, char *js)
|
||||||
{
|
{
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
struct dispatchData *data =
|
||||||
|
(struct dispatchData *)g_new(struct dispatchData, 1);
|
||||||
data->method = (dispatchMethod)syncEval;
|
data->method = (dispatchMethod)syncEval;
|
||||||
data->args = js;
|
data->args = js;
|
||||||
data->app = app;
|
data->app = app;
|
||||||
@@ -398,14 +450,13 @@ void ExecJS(struct Application *app, char *js)
|
|||||||
gdk_threads_add_idle(executeMethod, data);
|
gdk_threads_add_idle(executeMethod, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef char *(*dialogMethod)(struct Application *app, void *);
|
typedef char *(*dialogMethod)(void *app, void *);
|
||||||
|
|
||||||
struct dialogCall
|
struct dialogCall
|
||||||
{
|
{
|
||||||
struct Application *app;
|
struct Application *app;
|
||||||
dialogMethod method;
|
dialogMethod method;
|
||||||
void *args;
|
void *args;
|
||||||
void *filter;
|
|
||||||
char *result;
|
char *result;
|
||||||
int done;
|
int done;
|
||||||
};
|
};
|
||||||
@@ -413,70 +464,79 @@ struct dialogCall
|
|||||||
gboolean executeMethodWithReturn(gpointer data)
|
gboolean executeMethodWithReturn(gpointer data)
|
||||||
{
|
{
|
||||||
struct dialogCall *d = (struct dialogCall *)data;
|
struct dialogCall *d = (struct dialogCall *)data;
|
||||||
|
struct Application *app = (struct Application *)(d->app);
|
||||||
d->result = (d->method)(d->app, d->args);
|
Debug("Webview %p\n", app->webView);
|
||||||
|
Debug("Args %s\n", d->args);
|
||||||
|
Debug("Method %p\n", (d->method));
|
||||||
|
d->result = (d->method)(app, d->args);
|
||||||
d->done = 1;
|
d->done = 1;
|
||||||
|
// Debug("Method Execute Complete. Freeing memory");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *OpenFileDialog(struct Application *app, char *title, char *filter)
|
char *OpenFileDialogOnMainThread(void *app, char *title)
|
||||||
{
|
{
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
data->result = NULL;
|
data->result = NULL;
|
||||||
data->done = 0;
|
data->done = 0;
|
||||||
data->method = (dialogMethod)openFileDialogInternal;
|
data->method = (dialogMethod)OpenFileDialog;
|
||||||
const char* dialogArgs[]={ title, filter };
|
data->args = title;
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
|
||||||
|
|
||||||
while (data->done == 0)
|
|
||||||
{
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
g_free(data);
|
|
||||||
return data->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *SaveFileDialog(struct Application *app, char *title, char *filter)
|
|
||||||
{
|
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
|
||||||
data->result = NULL;
|
|
||||||
data->done = 0;
|
|
||||||
data->method = (dialogMethod)saveFileDialogInternal;
|
|
||||||
const char* dialogArgs[]={ title, filter };
|
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
data->app = app;
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
while (data->done == 0)
|
while (data->done == 0)
|
||||||
{
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
Debug("Dialog done");
|
Debug("Dialog done");
|
||||||
Debug("Result = %s\n", data->result);
|
Debug("Result = %s\n", data->result);
|
||||||
|
|
||||||
g_free(data);
|
g_free(data);
|
||||||
// Fingers crossed this wasn't freed by g_free above
|
// Fingers crossed this wasn't freed by g_free above
|
||||||
return data->result;
|
return data->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *OpenDirectoryDialog(struct Application *app, char *title, char *filter)
|
char *SaveFileDialogOnMainThread(void *app, char *title)
|
||||||
{
|
{
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
data->result = NULL;
|
data->result = NULL;
|
||||||
data->done = 0;
|
data->done = 0;
|
||||||
data->method = (dialogMethod)openDirectoryDialogInternal;
|
data->method = (dialogMethod)SaveFileDialog;
|
||||||
const char* dialogArgs[]={ title, filter };
|
data->args = title;
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
data->app = app;
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
while (data->done == 0)
|
while (data->done == 0)
|
||||||
{
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
Debug("Dialog done");
|
||||||
|
Debug("Result = %s\n", data->result);
|
||||||
|
g_free(data);
|
||||||
|
// Fingers crossed this wasn't freed by g_free above
|
||||||
|
return data->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *OpenDirectoryDialogOnMainThread(void *app, char *title)
|
||||||
|
{
|
||||||
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
|
data->result = NULL;
|
||||||
|
data->done = 0;
|
||||||
|
data->method = (dialogMethod)OpenDirectoryDialog;
|
||||||
|
data->args = title;
|
||||||
|
data->app = app;
|
||||||
|
|
||||||
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
|
while (data->done == 0)
|
||||||
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
Debug("Directory Dialog done");
|
Debug("Directory Dialog done");
|
||||||
@@ -495,8 +555,10 @@ void setIcon(struct Application *app)
|
|||||||
|
|
||||||
static void load_finished_cb(WebKitWebView *webView,
|
static void load_finished_cb(WebKitWebView *webView,
|
||||||
WebKitLoadEvent load_event,
|
WebKitLoadEvent load_event,
|
||||||
struct Application *app)
|
gpointer userData)
|
||||||
{
|
{
|
||||||
|
struct Application *app;
|
||||||
|
|
||||||
switch (load_event)
|
switch (load_event)
|
||||||
{
|
{
|
||||||
// case WEBKIT_LOAD_STARTED:
|
// case WEBKIT_LOAD_STARTED:
|
||||||
@@ -518,6 +580,7 @@ static void load_finished_cb(WebKitWebView *webView,
|
|||||||
case WEBKIT_LOAD_FINISHED:
|
case WEBKIT_LOAD_FINISHED:
|
||||||
/* Load finished, we can now stop the spinner */
|
/* Load finished, we can now stop the spinner */
|
||||||
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||||
|
app = (struct Application *)userData;
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
Debug("Binding Methods");
|
Debug("Binding Methods");
|
||||||
@@ -574,23 +637,21 @@ static void load_finished_cb(WebKitWebView *webView,
|
|||||||
setMinMaxSize(app);
|
setMinMaxSize(app);
|
||||||
|
|
||||||
// Centre by default
|
// Centre by default
|
||||||
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
Center(app);
|
||||||
|
|
||||||
// Show window and focus
|
// Show window and focus
|
||||||
if( app->startHidden == 0) {
|
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
gtk_widget_grab_focus(app->webView);
|
||||||
gtk_widget_grab_focus(app->webView);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean disable_context_menu_cb(
|
static gboolean
|
||||||
WebKitWebView *web_view,
|
disable_context_menu_cb(WebKitWebView *web_view,
|
||||||
WebKitContextMenu *context_menu,
|
WebKitContextMenu *context_menu,
|
||||||
GdkEvent *event,
|
GdkEvent *event,
|
||||||
WebKitHitTestResult *hit_test_result,
|
WebKitHitTestResult *hit_test_result,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -605,11 +666,12 @@ static void printEvent(const char *message, GdkEventButton *event)
|
|||||||
event->time);
|
event->time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dragWindow(WebKitUserContentManager *contentManager,
|
static void dragWindow(WebKitUserContentManager *contentManager,
|
||||||
WebKitJavascriptResult *result,
|
WebKitJavascriptResult *result,
|
||||||
struct Application *app)
|
gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
|
||||||
// If we get this message erroneously, ignore
|
// If we get this message erroneously, ignore
|
||||||
if (app->dragButtonEvent == NULL)
|
if (app->dragButtonEvent == NULL)
|
||||||
{
|
{
|
||||||
@@ -634,22 +696,31 @@ static void dragWindow(WebKitUserContentManager *contentManager,
|
|||||||
// Clear the event
|
// Clear the event
|
||||||
app->dragButtonEvent = NULL;
|
app->dragButtonEvent = NULL;
|
||||||
|
|
||||||
return;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
Debug("I am pressing a button");
|
||||||
|
|
||||||
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
||||||
{
|
{
|
||||||
|
printEvent("Drag button event was saved", event);
|
||||||
app->dragButtonEvent = event;
|
app->dragButtonEvent = event;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
Debug("I am releasing a button");
|
||||||
|
|
||||||
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
||||||
{
|
{
|
||||||
|
printEvent("Drag button event was reset", event);
|
||||||
|
|
||||||
app->dragButtonEvent = NULL;
|
app->dragButtonEvent = NULL;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -657,8 +728,9 @@ gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Applicat
|
|||||||
|
|
||||||
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
||||||
WebKitJavascriptResult *result,
|
WebKitJavascriptResult *result,
|
||||||
struct Application *app)
|
gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
||||||
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
||||||
char *message = jsc_value_to_string(value);
|
char *message = jsc_value_to_string(value);
|
||||||
@@ -675,225 +747,16 @@ static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
|||||||
g_free(message);
|
g_free(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDebug(struct Application *app, int flag)
|
void SetDebug(void *applicationPointer, int flag)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
debug = flag;
|
debug = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCurrentMonitorGeometry gets the geometry of the monitor
|
void SetBindings(void *applicationPointer, const char *bindings)
|
||||||
// that the window is mostly on.
|
|
||||||
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
|
|
||||||
// Get the monitor that the window is currently on
|
|
||||||
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
|
|
||||||
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
|
||||||
GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window);
|
|
||||||
|
|
||||||
// Get the geometry of the monitor
|
|
||||||
GdkRectangle result;
|
|
||||||
gdk_monitor_get_geometry (monitor,&result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
* Window Position *
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
// Position holds an x/y corrdinate
|
|
||||||
struct Position {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Internal call for setting the position of the window.
|
|
||||||
void setPositionInternal(struct Application *app, struct Position *pos) {
|
|
||||||
|
|
||||||
// Get the monitor geometry
|
|
||||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
|
||||||
|
|
||||||
// Move the window relative to the monitor
|
|
||||||
gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y);
|
|
||||||
|
|
||||||
// Free memory
|
|
||||||
free(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPosition sets the position of the window to the given x/y
|
|
||||||
// coordinates. The x/y values are relative to the monitor
|
|
||||||
// the window is mostly on.
|
|
||||||
void SetPosition(struct Application *app, int x, int y) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)setPositionInternal;
|
|
||||||
struct Position *pos = malloc(sizeof(struct Position));
|
|
||||||
pos->x = x;
|
|
||||||
pos->y = y;
|
|
||||||
data->args = pos;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************
|
|
||||||
* Window Size *
|
|
||||||
***************/
|
|
||||||
|
|
||||||
// Size holds a width/height
|
|
||||||
struct Size {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Internal call for setting the size of the window.
|
|
||||||
void setSizeInternal(struct Application *app, struct Size *size) {
|
|
||||||
gtk_window_resize(app->mainWindow, size->width, size->height);
|
|
||||||
free(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize sets the size of the window to the given width/height
|
|
||||||
void SetSize(struct Application *app, int width, int height) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)setSizeInternal;
|
|
||||||
struct Size *size = malloc(sizeof(struct Size));
|
|
||||||
size->width = width;
|
|
||||||
size->height = height;
|
|
||||||
data->args = size;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// centerInternal will center the main window on the monitor it is mostly in
|
|
||||||
void centerInternal(struct Application *app)
|
|
||||||
{
|
{
|
||||||
// Get the geometry of the monitor
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
|
||||||
|
|
||||||
// Get the window width/height
|
|
||||||
int windowWidth, windowHeight;
|
|
||||||
gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight);
|
|
||||||
|
|
||||||
// Place the window at the center of the monitor
|
|
||||||
gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center the window
|
|
||||||
void Center(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to centerInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)centerInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// hideInternal hides the main window
|
|
||||||
void hideInternal(struct Application *app) {
|
|
||||||
gtk_widget_hide (GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide places the hideInternal method onto the main thread for execution
|
|
||||||
void Hide(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to hideInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)hideInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// showInternal shows the main window
|
|
||||||
void showInternal(struct Application *app) {
|
|
||||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
|
||||||
gtk_widget_grab_focus(app->webView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show places the showInternal method onto the main thread for execution
|
|
||||||
void Show(struct Application *app) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)showInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// maximiseInternal maximises the main window
|
|
||||||
void maximiseInternal(struct Application *app) {
|
|
||||||
gtk_window_maximize(GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximise places the maximiseInternal method onto the main thread for execution
|
|
||||||
void Maximise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to maximiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)maximiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmaximiseInternal unmaximises the main window
|
|
||||||
void unmaximiseInternal(struct Application *app) {
|
|
||||||
gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmaximise places the unmaximiseInternal method onto the main thread for execution
|
|
||||||
void Unmaximise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to unmaximiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)unmaximiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// minimiseInternal minimises the main window
|
|
||||||
void minimiseInternal(struct Application *app) {
|
|
||||||
gtk_window_iconify(app->mainWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimise places the minimiseInternal method onto the main thread for execution
|
|
||||||
void Minimise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to minimiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)minimiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unminimiseInternal unminimises the main window
|
|
||||||
void unminimiseInternal(struct Application *app) {
|
|
||||||
gtk_window_present(app->mainWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unminimise places the unminimiseInternal method onto the main thread for execution
|
|
||||||
void Unminimise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to unminimiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)unminimiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetBindings(struct Application *app, const char *bindings)
|
|
||||||
{
|
|
||||||
const char *temp = concat("window.wailsbindings = \"", bindings);
|
const char *temp = concat("window.wailsbindings = \"", bindings);
|
||||||
const char *jscall = concat(temp, "\";");
|
const char *jscall = concat(temp, "\";");
|
||||||
free((void *)temp);
|
free((void *)temp);
|
||||||
@@ -903,15 +766,18 @@ void SetBindings(struct Application *app, const char *bindings)
|
|||||||
// This is called when the close button on the window is pressed
|
// This is called when the close button on the window is pressed
|
||||||
gboolean close_button_pressed(GtkWidget *widget,
|
gboolean close_button_pressed(GtkWidget *widget,
|
||||||
GdkEvent *event,
|
GdkEvent *event,
|
||||||
struct Application *app)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)user_data;
|
||||||
app->sendMessageToBackend("WC"); // Window Close
|
app->sendMessageToBackend("WC"); // Window Close
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setupWindow(struct Application *app)
|
static void setupWindow(void *applicationPointer)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
|
|
||||||
// Create the window
|
// Create the window
|
||||||
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
||||||
// Save reference
|
// Save reference
|
||||||
@@ -930,14 +796,10 @@ static void setupWindow(struct Application *app)
|
|||||||
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
||||||
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
||||||
|
|
||||||
// Setup the window drag handler if this is a frameless app
|
// Setup the window drag handler
|
||||||
if ( app->frame == 0 ) {
|
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
||||||
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
||||||
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
|
||||||
// Setup the mouse handlers
|
|
||||||
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
|
||||||
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
|
||||||
}
|
|
||||||
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
||||||
|
|
||||||
// Save reference
|
// Save reference
|
||||||
@@ -946,6 +808,9 @@ static void setupWindow(struct Application *app)
|
|||||||
// Add the webview to the window
|
// Add the webview to the window
|
||||||
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
||||||
|
|
||||||
|
// Setup the mouse handlers
|
||||||
|
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
||||||
|
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
||||||
|
|
||||||
// Load default HTML
|
// Load default HTML
|
||||||
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
||||||
@@ -970,13 +835,20 @@ static void setupWindow(struct Application *app)
|
|||||||
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void activate(GtkApplication* _, struct Application *app)
|
static void
|
||||||
|
activate(GtkApplication *app,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
setupWindow(app);
|
struct Application *mainApp = (struct Application *)user_data;
|
||||||
|
|
||||||
|
// Main Window
|
||||||
|
setupWindow(mainApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run(struct Application *app, int argc, char **argv)
|
void Run(void *applicationPointer, int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
|
|
||||||
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
||||||
g_application_run(G_APPLICATION(app->application), argc, argv);
|
g_application_run(G_APPLICATION(app->application), argc, argv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalDirectory gets the caller's file directory
|
// LocalDirectory gets the caller's file directory
|
||||||
@@ -20,45 +18,11 @@ func LocalDirectory() string {
|
|||||||
return filepath.Dir(thisFile)
|
return filepath.Dir(thisFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelativeToCwd returns an absolute path based on the cwd
|
|
||||||
// and the given relative path
|
|
||||||
func RelativeToCwd(relativePath string) (string, error) {
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(cwd, relativePath), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mkdir will create the given directory
|
// Mkdir will create the given directory
|
||||||
func Mkdir(dirname string) error {
|
func Mkdir(dirname string) error {
|
||||||
return os.Mkdir(dirname, 0755)
|
return os.Mkdir(dirname, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkDirs creates the given nested directories.
|
|
||||||
// Returns error on failure
|
|
||||||
func MkDirs(fullPath string, mode ...os.FileMode) error {
|
|
||||||
var perms os.FileMode
|
|
||||||
perms = 0755
|
|
||||||
if len(mode) == 1 {
|
|
||||||
perms = mode[0]
|
|
||||||
}
|
|
||||||
return os.MkdirAll(fullPath, perms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveFile attempts to move the source file to the target
|
|
||||||
// Target is a fully qualified path to a file *name*, not a
|
|
||||||
// directory
|
|
||||||
func MoveFile(source string, target string) error {
|
|
||||||
return os.Rename(source, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteFile will delete the given file
|
|
||||||
func DeleteFile(filename string) error {
|
|
||||||
return os.Remove(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFile from source to target
|
// CopyFile from source to target
|
||||||
func CopyFile(source string, target string) error {
|
func CopyFile(source string, target string) error {
|
||||||
s, err := os.Open(source)
|
s, err := os.Open(source)
|
||||||
@@ -180,100 +144,3 @@ func fatal(message ...string) {
|
|||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubdirectories returns a list of subdirectories for the given root directory
|
|
||||||
func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
|
||||||
var result slicer.StringSlicer
|
|
||||||
|
|
||||||
// Iterate root dir
|
|
||||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// If we have a directory, save it
|
|
||||||
if info.IsDir() {
|
|
||||||
result.Add(path)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return &result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DirIsEmpty(dir string) (bool, error) {
|
|
||||||
|
|
||||||
if !DirExists(dir) {
|
|
||||||
return false, fmt.Errorf("DirIsEmpty called with a non-existant directory: %s", dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
|
||||||
f, err := os.Open(dir)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
|
||||||
if err == io.EOF {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, err // Either not empty or error, suits both cases
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04
|
|
||||||
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
|
|
||||||
// Source directory must exist, destination directory must *not* exist.
|
|
||||||
// Symlinks are ignored and skipped.
|
|
||||||
func CopyDir(src string, dst string) (err error) {
|
|
||||||
src = filepath.Clean(src)
|
|
||||||
dst = filepath.Clean(dst)
|
|
||||||
|
|
||||||
si, err := os.Stat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !si.IsDir() {
|
|
||||||
return fmt.Errorf("source is not a directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stat(dst)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("destination already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = MkDirs(dst)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := ioutil.ReadDir(src)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
srcPath := filepath.Join(src, entry.Name())
|
|
||||||
dstPath := filepath.Join(dst, entry.Name())
|
|
||||||
|
|
||||||
if entry.IsDir() {
|
|
||||||
err = CopyDir(srcPath, dstPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Skip symlinks.
|
|
||||||
if entry.Mode()&os.ModeSymlink != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = CopyFile(srcPath, dstPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ package html
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/tdewolff/minify"
|
|
||||||
"github.com/tdewolff/minify/js"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type assetTypes struct {
|
type assetTypes struct {
|
||||||
@@ -67,10 +63,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
result = strings.ReplaceAll(result, "\n", "")
|
result = strings.ReplaceAll(result, "\n", "")
|
||||||
result = strings.ReplaceAll(result, "\r\n", "")
|
result = strings.ReplaceAll(result, "\r\n", "")
|
||||||
result = strings.ReplaceAll(result, "\n", "")
|
result = strings.ReplaceAll(result, "\n", "")
|
||||||
|
|
||||||
// Inject wailsloader code
|
|
||||||
result = strings.Replace(result, `</body>`, `<script id='wailsloader'>window.wailsloader = { html: true, runtime: false, userjs: false, usercss: false };var self=document.querySelector('#wailsloader');self.parentNode.removeChild(self);</script></body>`, 1)
|
|
||||||
|
|
||||||
url := url.URL{Path: result}
|
url := url.URL{Path: result}
|
||||||
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
||||||
|
|
||||||
@@ -92,16 +84,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
result = strings.ReplaceAll(result, ` {`, `{`)
|
result = strings.ReplaceAll(result, ` {`, `{`)
|
||||||
result = strings.ReplaceAll(result, `: `, `:`)
|
result = strings.ReplaceAll(result, `: `, `:`)
|
||||||
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
||||||
|
|
||||||
case AssetTypes.JS:
|
|
||||||
m := minify.New()
|
|
||||||
m.AddFunc("application/javascript", js.Minify)
|
|
||||||
var err error
|
|
||||||
dataString, err = m.String("application/javascript", a.Data+";")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
a.Data = dataString
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get byte data of the string
|
// Get byte data of the string
|
||||||
@@ -121,7 +103,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
return cdata.String()
|
return cdata.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump will output the asset to the terminal
|
|
||||||
func (a *Asset) Dump() {
|
func (a *Asset) Dump() {
|
||||||
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
buf := bytes.NewBufferString(htmldata)
|
buf := bytes.NewBufferString(htmldata)
|
||||||
tokenizer := html.NewTokenizer(buf)
|
tokenizer := html.NewTokenizer(buf)
|
||||||
|
|
||||||
paths := slicer.String()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
//get the next token type
|
//get the next token type
|
||||||
tokenType := tokenizer.Next()
|
tokenType := tokenizer.Next()
|
||||||
@@ -102,29 +100,19 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
if attr.Key == "href" {
|
if attr.Key == "href" {
|
||||||
asset.Path = attr.Val
|
asset.Path = attr.Val
|
||||||
}
|
}
|
||||||
// standard stylesheet
|
// stylesheet
|
||||||
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
||||||
asset.Type = AssetTypes.CSS
|
asset.Type = AssetTypes.CSS
|
||||||
}
|
}
|
||||||
if attr.Key == "as" && attr.Val == "style" {
|
|
||||||
asset.Type = AssetTypes.CSS
|
|
||||||
}
|
|
||||||
if attr.Key == "as" && attr.Val == "script" {
|
|
||||||
asset.Type = AssetTypes.JS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err := asset.Load(a.basedirectory)
|
||||||
// Ensure we don't include duplicates
|
if err != nil {
|
||||||
if !paths.Contains(asset.Path) {
|
return err
|
||||||
err := asset.Load(a.basedirectory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.assets = append(a.assets, asset)
|
|
||||||
paths.Add(asset.Path)
|
|
||||||
}
|
}
|
||||||
|
a.assets = append(a.assets, asset)
|
||||||
}
|
}
|
||||||
if "script" == token.Data {
|
if "script" == token.Data {
|
||||||
|
|
||||||
tokenType = tokenizer.Next()
|
tokenType = tokenizer.Next()
|
||||||
//just make sure it's actually a text token
|
//just make sure it's actually a text token
|
||||||
asset := &Asset{Type: AssetTypes.JS}
|
asset := &Asset{Type: AssetTypes.JS}
|
||||||
@@ -134,14 +122,11 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !paths.Contains(asset.Path) {
|
err := asset.Load(a.basedirectory)
|
||||||
err := asset.Load(a.basedirectory)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.assets = append(a.assets, asset)
|
|
||||||
paths.Add(asset.Path)
|
|
||||||
}
|
}
|
||||||
|
a.assets = append(a.assets, asset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +141,7 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
var cdata strings.Builder
|
var cdata strings.Builder
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
header := `// assets.h
|
header := `// assets.c
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||||
// This file was auto-generated. DO NOT MODIFY.
|
// This file was auto-generated. DO NOT MODIFY.
|
||||||
|
|
||||||
@@ -168,10 +153,6 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
assetVariables := slicer.String()
|
assetVariables := slicer.String()
|
||||||
var variableName string
|
var variableName string
|
||||||
for index, asset := range a.assets {
|
for index, asset := range a.assets {
|
||||||
// For desktop we ignore the favicon
|
|
||||||
if asset.Type == AssetTypes.FAVICON {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
||||||
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
||||||
cdata.WriteString(assetCdata)
|
cdata.WriteString(assetCdata)
|
||||||
@@ -179,13 +160,13 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if assetVariables.Length() > 0 {
|
if assetVariables.Length() > 0 {
|
||||||
cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
cdata.WriteString(fmt.Sprintf("\nconst char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||||
} else {
|
} else {
|
||||||
cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };")
|
cdata.WriteString("\nconst char *assets[] = { 0x00 };")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
assetsFile := filepath.Join(targetDir, "assets.h")
|
assetsFile := filepath.Join(targetDir, "assets.c")
|
||||||
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -196,17 +177,16 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
||||||
// the items in the AssetBundle
|
// the items in the AssetBundle
|
||||||
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
||||||
theassetdb := assetdb.NewAssetDB()
|
assetdb := assetdb.NewAssetDB()
|
||||||
|
|
||||||
// Loop over the Assets
|
// Loop over the Assets
|
||||||
for _, asset := range a.assets {
|
for _, asset := range a.assets {
|
||||||
theassetdb.AddAsset(asset.Path, []byte(asset.Data))
|
assetdb.AddAsset(asset.Path, []byte(asset.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return theassetdb, nil
|
return assetdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump will output the assets to the terminal
|
|
||||||
func (a *AssetBundle) Dump() {
|
func (a *AssetBundle) Dump() {
|
||||||
println("Assets:")
|
println("Assets:")
|
||||||
for _, asset := range a.assets {
|
for _, asset := range a.assets {
|
||||||
|
|||||||
@@ -2,30 +2,31 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CustomLogger defines what a user can do with a logger
|
// CustomLogger defines what a user can do with a logger
|
||||||
type CustomLogger interface {
|
type CustomLogger interface {
|
||||||
// Writeln writes directly to the output with no log level plus line ending
|
// Writeln writes directly to the output with no log level plus line ending
|
||||||
Writeln(message string)
|
Writeln(message string) error
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
Write(message string)
|
Write(message string) error
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
Trace(format string, args ...interface{})
|
Trace(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
Debug(format string, args ...interface{})
|
Debug(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
Info(format string, args ...interface{})
|
Info(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
Warning(format string, args ...interface{})
|
Warning(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
Error(format string, args ...interface{})
|
Error(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
Fatal(format string, args ...interface{})
|
Fatal(format string, args ...interface{})
|
||||||
@@ -49,48 +50,49 @@ func newcustomLogger(logger *Logger, name string) *customLogger {
|
|||||||
|
|
||||||
// Writeln writes directly to the output with no log level
|
// Writeln writes directly to the output with no log level
|
||||||
// Appends a carriage return to the message
|
// Appends a carriage return to the message
|
||||||
func (l *customLogger) Writeln(message string) {
|
func (l *customLogger) Writeln(message string) error {
|
||||||
l.logger.Writeln(message)
|
return l.logger.Writeln(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
func (l *customLogger) Write(message string) {
|
func (l *customLogger) Write(message string) error {
|
||||||
l.logger.Write(message)
|
return l.logger.Write(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Trace(format string, args ...interface{}) {
|
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Trace(format, args...)
|
return l.logger.processLogMessage(TRACE, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Debug(format string, args ...interface{}) {
|
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Debug(format, args...)
|
return l.logger.processLogMessage(DEBUG, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Info(format string, args ...interface{}) {
|
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Info(format, args...)
|
return l.logger.processLogMessage(INFO, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Warning(format string, args ...interface{}) {
|
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Warning(format, args...)
|
return l.logger.processLogMessage(WARNING, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Error(format string, args ...interface{}) {
|
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Error(format, args...)
|
return l.logger.processLogMessage(ERROR, format, args...)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Fatal(format, args...)
|
l.logger.processLogMessage(FATAL, format, args...)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,32 +2,37 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel is an alias for the public LogLevel
|
|
||||||
type LogLevel = logger.LogLevel
|
|
||||||
|
|
||||||
// Logger is a utlility to log messages to a number of destinations
|
// Logger is a utlility to log messages to a number of destinations
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
output logger.Logger
|
writers []io.Writer
|
||||||
logLevel LogLevel
|
logLevel uint8
|
||||||
showLevelInLog bool
|
showLevelInLog bool
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
||||||
// are the targets for the logs
|
// are the targets for the logs
|
||||||
func New(output logger.Logger) *Logger {
|
func New(writers ...io.Writer) *Logger {
|
||||||
result := &Logger{
|
result := &Logger{
|
||||||
logLevel: logger.INFO,
|
logLevel: INFO,
|
||||||
showLevelInLog: true,
|
showLevelInLog: true,
|
||||||
output: output,
|
}
|
||||||
|
for _, writer := range writers {
|
||||||
|
result.AddOutput(writer)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writers gets the log writers
|
||||||
|
func (l *Logger) Writers() []io.Writer {
|
||||||
|
return l.writers
|
||||||
|
}
|
||||||
|
|
||||||
// CustomLogger creates a new custom logger that prints out a name/id
|
// CustomLogger creates a new custom logger that prints out a name/id
|
||||||
// before the messages
|
// before the messages
|
||||||
func (l *Logger) CustomLogger(name string) CustomLogger {
|
func (l *Logger) CustomLogger(name string) CustomLogger {
|
||||||
@@ -40,66 +45,99 @@ func (l *Logger) HideLogLevel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetLogLevel sets the minimum level of logs that will be output
|
// SetLogLevel sets the minimum level of logs that will be output
|
||||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
func (l *Logger) SetLogLevel(level uint8) {
|
||||||
l.logLevel = level
|
l.logLevel = level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddOutput adds the given `io.Writer` to the list of destinations
|
||||||
|
// that get logged to
|
||||||
|
func (l *Logger) AddOutput(writer io.Writer) {
|
||||||
|
l.writers = append(l.writers, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) write(loglevel uint8, message string) error {
|
||||||
|
|
||||||
|
// Don't print logs lower than the current log level
|
||||||
|
if loglevel < l.logLevel {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show log level text if enabled
|
||||||
|
if l.showLevelInLog {
|
||||||
|
message = mapLogLevel[loglevel] + message
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out the logs
|
||||||
|
l.lock.Lock()
|
||||||
|
for _, writer := range l.writers {
|
||||||
|
_, err := writer.Write([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
l.lock.Unlock() // Because defer is slow
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.lock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeln appends a newline character to the message before writing
|
||||||
|
func (l *Logger) writeln(loglevel uint8, message string) error {
|
||||||
|
return l.write(loglevel, message+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
// Writeln writes directly to the output with no log level
|
// Writeln writes directly to the output with no log level
|
||||||
// Appends a carriage return to the message
|
// Appends a carriage return to the message
|
||||||
func (l *Logger) Writeln(message string) {
|
func (l *Logger) Writeln(message string) error {
|
||||||
l.output.Print(message)
|
return l.write(BYPASS, message+"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
func (l *Logger) Write(message string) {
|
func (l *Logger) Write(message string) error {
|
||||||
l.output.Print(message)
|
return l.write(BYPASS, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print writes directly to the output with no log level
|
// processLogMessage formats the given message before writing it out
|
||||||
// Appends a carriage return to the message
|
func (l *Logger) processLogMessage(loglevel uint8, format string, args ...interface{}) error {
|
||||||
func (l *Logger) Print(message string) {
|
message := fmt.Sprintf(format, args...)
|
||||||
l.Write(message)
|
return l.writeln(loglevel, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
func (l *Logger) Trace(format string, args ...interface{}) {
|
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.TRACE {
|
return l.processLogMessage(TRACE, format, args...)
|
||||||
l.output.Trace(fmt.Sprintf(format, args...))
|
}
|
||||||
|
|
||||||
|
// CustomTrace returns a custom Logging function that will insert the given name before the message
|
||||||
|
func (l *Logger) CustomTrace(name string) func(format string, args ...interface{}) {
|
||||||
|
return func(format string, args ...interface{}) {
|
||||||
|
format = name + " | " + format
|
||||||
|
l.processLogMessage(TRACE, format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.DEBUG {
|
return l.processLogMessage(DEBUG, format, args...)
|
||||||
l.output.Debug(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
func (l *Logger) Info(format string, args ...interface{}) {
|
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.INFO {
|
return l.processLogMessage(INFO, format, args...)
|
||||||
l.output.Info(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
func (l *Logger) Warning(format string, args ...interface{}) {
|
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.WARNING {
|
return l.processLogMessage(WARNING, format, args...)
|
||||||
l.output.Warning(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
func (l *Logger) Error(format string, args ...interface{}) {
|
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.ERROR {
|
return l.processLogMessage(ERROR, format, args...)
|
||||||
l.output.Error(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||||
l.output.Fatal(fmt.Sprintf(format, args...))
|
l.processLogMessage(FATAL, format, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
34
v2/internal/logger/logger.go
Normal file
34
v2/internal/logger/logger.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TRACE level
|
||||||
|
TRACE uint8 = 0
|
||||||
|
|
||||||
|
// DEBUG level logging
|
||||||
|
DEBUG uint8 = 1
|
||||||
|
|
||||||
|
// INFO level logging
|
||||||
|
INFO uint8 = 2
|
||||||
|
|
||||||
|
// WARNING level logging
|
||||||
|
WARNING uint8 = 4
|
||||||
|
|
||||||
|
// ERROR level logging
|
||||||
|
ERROR uint8 = 8
|
||||||
|
|
||||||
|
// FATAL level logging
|
||||||
|
FATAL uint8 = 16
|
||||||
|
|
||||||
|
// BYPASS level logging - does not use a log level
|
||||||
|
BYPASS uint8 = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapLogLevel = map[uint8]string{
|
||||||
|
TRACE: "TRACE | ",
|
||||||
|
DEBUG: "DEBUG | ",
|
||||||
|
INFO: "INFO | ",
|
||||||
|
WARNING: "WARN | ",
|
||||||
|
ERROR: "ERROR | ",
|
||||||
|
FATAL: "FATAL | ",
|
||||||
|
BYPASS: "",
|
||||||
|
}
|
||||||
202
v2/internal/logger/logger_test.go
Normal file
202
v2/internal/logger/logger_test.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestByteBufferLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(TRACE)
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "TRACE | I am a message!\n",
|
||||||
|
DEBUG: "DEBUG | I am a message!\n",
|
||||||
|
WARNING: "WARN | I am a message!\n",
|
||||||
|
INFO: "INFO | I am a message!\n",
|
||||||
|
ERROR: "ERROR | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: myLogger.Trace,
|
||||||
|
DEBUG: myLogger.Debug,
|
||||||
|
WARNING: myLogger.Warning,
|
||||||
|
INFO: myLogger.Info,
|
||||||
|
ERROR: myLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level, expected := range tests {
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestCustomLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(TRACE)
|
||||||
|
customLogger := myLogger.CustomLogger("Test")
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "TRACE | Test | I am a message!\n",
|
||||||
|
DEBUG: "DEBUG | Test | I am a message!\n",
|
||||||
|
WARNING: "WARN | Test | I am a message!\n",
|
||||||
|
INFO: "INFO | Test | I am a message!\n",
|
||||||
|
ERROR: "ERROR | Test | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: customLogger.Trace,
|
||||||
|
DEBUG: customLogger.Debug,
|
||||||
|
WARNING: customLogger.Warning,
|
||||||
|
INFO: customLogger.Info,
|
||||||
|
ERROR: customLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level, expected := range tests {
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestWriteln(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(DEBUG)
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := myLogger.Writeln("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "I am a message!\n")
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err = myLogger.Write("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual = buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "I am a message!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogLevel(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(ERROR)
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "",
|
||||||
|
DEBUG: "",
|
||||||
|
WARNING: "",
|
||||||
|
INFO: "",
|
||||||
|
ERROR: "ERROR | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: myLogger.Trace,
|
||||||
|
DEBUG: myLogger.Debug,
|
||||||
|
WARNING: myLogger.Warning,
|
||||||
|
INFO: myLogger.Info,
|
||||||
|
ERROR: myLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level := range tests {
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "ERROR | I am a message!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
file, err := ioutil.TempFile(".", "wailsv2test")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
|
myLogger := New(file)
|
||||||
|
myLogger.SetLogLevel(DEBUG)
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err = myLogger.Info("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual, err := ioutil.ReadFile(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
is.Equal(string(actual), "INFO | I am a message!\n")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,38 +6,19 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client defines what a frontend client can do
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Quit()
|
Quit()
|
||||||
NotifyEvent(message string)
|
NotifyEvent(message string)
|
||||||
CallResult(message string)
|
CallResult(message string)
|
||||||
OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string)
|
SaveFileDialog(title string) string
|
||||||
SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string)
|
OpenFileDialog(title string) string
|
||||||
MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string)
|
OpenDirectoryDialog(title string) string
|
||||||
WindowSetTitle(title string)
|
WindowSetTitle(title string)
|
||||||
WindowShow()
|
|
||||||
WindowHide()
|
|
||||||
WindowCenter()
|
|
||||||
WindowMaximise()
|
|
||||||
WindowUnmaximise()
|
|
||||||
WindowMinimise()
|
|
||||||
WindowUnminimise()
|
|
||||||
WindowPosition(x int, y int)
|
|
||||||
WindowSize(width int, height int)
|
|
||||||
WindowSetMinSize(width int, height int)
|
|
||||||
WindowSetMaxSize(width int, height int)
|
|
||||||
WindowFullscreen()
|
WindowFullscreen()
|
||||||
WindowUnFullscreen()
|
WindowUnFullscreen()
|
||||||
WindowSetColour(colour int)
|
WindowSetColour(colour string) bool
|
||||||
DarkModeEnabled(callbackID string)
|
|
||||||
SetApplicationMenu(menuJSON string)
|
|
||||||
SetTrayMenu(trayMenuJSON string)
|
|
||||||
UpdateTrayMenuLabel(JSON string)
|
|
||||||
UpdateContextMenu(contextMenuJSON string)
|
|
||||||
DeleteTrayMenuByID(id string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DispatchClient is what the frontends use to interface with the
|
// DispatchClient is what the frontends use to interface with the
|
||||||
@@ -72,7 +53,7 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
|||||||
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
||||||
parsedMessage, err := message.Parse(incomingMessage)
|
parsedMessage, err := message.Parse(incomingMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err.Error())
|
d.logger.Trace("Error: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,6 +62,14 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
|||||||
|
|
||||||
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
||||||
|
|
||||||
|
// Check error
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Trace("Error: " + err.Error())
|
||||||
|
// Hrm... what do we do with this?
|
||||||
|
d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Publish the parsed message
|
// Publish the parsed message
|
||||||
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CallMessage struct {
|
type CallMessage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Args []json.RawMessage `json:"args"`
|
Args []interface{} `json:"args"`
|
||||||
CallbackID string `json:"callbackID,omitempty"`
|
CallbackID string `json:"callbackID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// callMessageParser does what it says on the tin!
|
// callMessageParser does what it says on the tin!
|
||||||
@@ -22,7 +22,6 @@ func callMessageParser(message string) (*parsedMessage, error) {
|
|||||||
callMessage := new(CallMessage)
|
callMessage := new(CallMessage)
|
||||||
|
|
||||||
m := message[1:]
|
m := message[1:]
|
||||||
|
|
||||||
err := json.Unmarshal([]byte(m), callMessage)
|
err := json.Unmarshal([]byte(m), callMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
import "encoding/json"
|
||||||
)
|
|
||||||
|
|
||||||
type EventMessage struct {
|
type EventMessage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -13,7 +12,6 @@ type EventMessage struct {
|
|||||||
type OnEventMessage struct {
|
type OnEventMessage struct {
|
||||||
Name string
|
Name string
|
||||||
Callback func(optionalData ...interface{})
|
Callback func(optionalData ...interface{})
|
||||||
Counter int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eventMessageParser does what it says on the tin!
|
// eventMessageParser does what it says on the tin!
|
||||||
@@ -29,6 +27,8 @@ func eventMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// Switch the event type (with or without data)
|
// Switch the event type (with or without data)
|
||||||
switch message[0] {
|
switch message[0] {
|
||||||
|
case 'e':
|
||||||
|
eventMessage.Name = message[2:]
|
||||||
case 'E':
|
case 'E':
|
||||||
m := message[2:]
|
m := message[2:]
|
||||||
err := json.Unmarshal([]byte(m), eventMessage)
|
err := json.Unmarshal([]byte(m), eventMessage)
|
||||||
|
|||||||
@@ -3,19 +3,16 @@ package message
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var logMessageMap = map[byte]string{
|
var logMessageMap = map[byte]string{
|
||||||
'P': "log:print",
|
|
||||||
'T': "log:trace",
|
|
||||||
'D': "log:debug",
|
'D': "log:debug",
|
||||||
'I': "log:info",
|
'I': "log:info",
|
||||||
'W': "log:warning",
|
'W': "log:warning",
|
||||||
'E': "log:error",
|
'E': "log:error",
|
||||||
'F': "log:fatal",
|
'F': "log:fatal",
|
||||||
'S': "log:setlevel",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// logMessageParser does what it says on the tin!
|
// logMessageParser does what it says on the tin!
|
||||||
func logMessageParser(message string) (*parsedMessage, error) {
|
func logMessageParser(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
// Sanity check: Log messages must be at least 2 bytes
|
// Sanity check: Log messages must be at least 2 bytes
|
||||||
if len(message) < 2 {
|
if len(message) < 2 {
|
||||||
return nil, fmt.Errorf("log message was an invalid length")
|
return nil, fmt.Errorf("log message was an invalid length")
|
||||||
@@ -26,7 +23,7 @@ func logMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// If the type is invalid, raise error
|
// If the type is invalid, raise error
|
||||||
if messageTopic == "" {
|
if messageTopic == "" {
|
||||||
return nil, fmt.Errorf("log message type '%c' invalid", message[1])
|
return nil, fmt.Errorf("log message type '%b' invalid", message[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new parsed message struct
|
// Create a new parsed message struct
|
||||||
|
|||||||
@@ -14,26 +14,17 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
|||||||
'L': logMessageParser,
|
'L': logMessageParser,
|
||||||
'R': runtimeMessageParser,
|
'R': runtimeMessageParser,
|
||||||
'E': eventMessageParser,
|
'E': eventMessageParser,
|
||||||
|
'e': eventMessageParser,
|
||||||
'C': callMessageParser,
|
'C': callMessageParser,
|
||||||
'W': windowMessageParser,
|
'W': windowMessageParser,
|
||||||
'D': dialogMessageParser,
|
|
||||||
'S': systemMessageParser,
|
|
||||||
'M': menuMessageParser,
|
|
||||||
'T': trayMessageParser,
|
|
||||||
'X': contextMenusMessageParser,
|
|
||||||
'U': urlMessageParser,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse will attempt to parse the given message
|
// Parse will attempt to parse the given message
|
||||||
func Parse(message string) (*parsedMessage, error) {
|
func Parse(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
if len(message) == 0 {
|
|
||||||
return nil, fmt.Errorf("MessageParser received blank message")
|
|
||||||
}
|
|
||||||
|
|
||||||
parseMethod := messageParsers[message[0]]
|
parseMethod := messageParsers[message[0]]
|
||||||
if parseMethod == nil {
|
if parseMethod == nil {
|
||||||
return nil, fmt.Errorf("message type '%c' invalid", message[0])
|
return nil, fmt.Errorf("message type '%b' invalid", message[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseMethod(message)
|
return parseMethod(message)
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ func runtimeMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// processBrowserMessage expects messages of the following format:
|
// processBrowserMessage expects messages of the following format:
|
||||||
// RB<METHOD><DATA>
|
// RB<METHOD><DATA>
|
||||||
// O = Open
|
|
||||||
func processBrowserMessage(message string) (*parsedMessage, error) {
|
func processBrowserMessage(message string) (*parsedMessage, error) {
|
||||||
method := message[2]
|
method := message[2]
|
||||||
switch method {
|
switch method {
|
||||||
case 'O':
|
case 'U':
|
||||||
// Open URL
|
// Open URL
|
||||||
target := message[3:]
|
url := message[3:]
|
||||||
return &parsedMessage{Topic: "runtime:browser:open", Data: target}, nil
|
return &parsedMessage{Topic: "runtime:browser:openurl", Data: url}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unknown browser message: %s", message)
|
return nil, fmt.Errorf("unknown browser message: %s", message)
|
||||||
|
|||||||
@@ -22,66 +22,6 @@ func windowMessageParser(message string) (*parsedMessage, error) {
|
|||||||
parsedMessage.Topic = "quit"
|
parsedMessage.Topic = "quit"
|
||||||
parsedMessage.Data = "Window Closed"
|
parsedMessage.Data = "Window Closed"
|
||||||
|
|
||||||
// Center window
|
|
||||||
case 'c':
|
|
||||||
parsedMessage.Topic = "window:center"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Hide window
|
|
||||||
case 'H':
|
|
||||||
parsedMessage.Topic = "window:hide"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Show window
|
|
||||||
case 'S':
|
|
||||||
parsedMessage.Topic = "window:show"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Position window
|
|
||||||
case 'p':
|
|
||||||
parsedMessage.Topic = "window:position:" + message[3:]
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Set window size
|
|
||||||
case 's':
|
|
||||||
parsedMessage.Topic = "window:size:" + message[3:]
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Maximise window
|
|
||||||
case 'M':
|
|
||||||
parsedMessage.Topic = "window:maximise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Unmaximise window
|
|
||||||
case 'U':
|
|
||||||
parsedMessage.Topic = "window:unmaximise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Minimise window
|
|
||||||
case 'm':
|
|
||||||
parsedMessage.Topic = "window:minimise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Unminimise window
|
|
||||||
case 'u':
|
|
||||||
parsedMessage.Topic = "window:unminimise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Fullscreen window
|
|
||||||
case 'F':
|
|
||||||
parsedMessage.Topic = "window:fullscreen"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// UnFullscreen window
|
|
||||||
case 'f':
|
|
||||||
parsedMessage.Topic = "window:unfullscreen"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Set Title
|
|
||||||
case 'T':
|
|
||||||
parsedMessage.Topic = "window:settitle"
|
|
||||||
parsedMessage.Data = message[2:]
|
|
||||||
|
|
||||||
// Unknown event type
|
// Unknown event type
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown message: %s", message)
|
return nil, fmt.Errorf("unknown message: %s", message)
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
package messagedispatcher
|
package messagedispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
@@ -23,8 +19,7 @@ type Dispatcher struct {
|
|||||||
eventChannel <-chan *servicebus.Message
|
eventChannel <-chan *servicebus.Message
|
||||||
windowChannel <-chan *servicebus.Message
|
windowChannel <-chan *servicebus.Message
|
||||||
dialogChannel <-chan *servicebus.Message
|
dialogChannel <-chan *servicebus.Message
|
||||||
systemChannel <-chan *servicebus.Message
|
running bool
|
||||||
menuChannel <-chan *servicebus.Message
|
|
||||||
|
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -32,13 +27,6 @@ type Dispatcher struct {
|
|||||||
// Clients
|
// Clients
|
||||||
clients map[string]*DispatchClient
|
clients map[string]*DispatchClient
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
// Context for cancellation
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
// internal wait group
|
|
||||||
wg sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New dispatcher. Needs a service bus to send to.
|
// New dispatcher. Needs a service bus to send to.
|
||||||
@@ -73,19 +61,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemChannel, err := servicebus.Subscribe("system:")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
menuChannel, err := servicebus.Subscribe("menufrontend:")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create context
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
result := &Dispatcher{
|
result := &Dispatcher{
|
||||||
servicebus: servicebus,
|
servicebus: servicebus,
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
@@ -95,10 +70,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
quitChannel: quitChannel,
|
quitChannel: quitChannel,
|
||||||
windowChannel: windowChannel,
|
windowChannel: windowChannel,
|
||||||
dialogChannel: dialogChannel,
|
dialogChannel: dialogChannel,
|
||||||
systemChannel: systemChannel,
|
|
||||||
menuChannel: menuChannel,
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -109,18 +80,15 @@ func (d *Dispatcher) Start() error {
|
|||||||
|
|
||||||
d.logger.Trace("Starting")
|
d.logger.Trace("Starting")
|
||||||
|
|
||||||
d.wg.Add(1)
|
d.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer d.logger.Trace("Shutdown")
|
for d.running {
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
|
||||||
d.wg.Done()
|
|
||||||
return
|
|
||||||
case <-d.quitChannel:
|
case <-d.quitChannel:
|
||||||
d.processQuit()
|
d.processQuit()
|
||||||
|
d.running = false
|
||||||
case resultMessage := <-d.resultChannel:
|
case resultMessage := <-d.resultChannel:
|
||||||
d.processCallResult(resultMessage)
|
d.processCallResult(resultMessage)
|
||||||
case eventMessage := <-d.eventChannel:
|
case eventMessage := <-d.eventChannel:
|
||||||
@@ -129,12 +97,11 @@ func (d *Dispatcher) Start() error {
|
|||||||
d.processWindowMessage(windowMessage)
|
d.processWindowMessage(windowMessage)
|
||||||
case dialogMessage := <-d.dialogChannel:
|
case dialogMessage := <-d.dialogChannel:
|
||||||
d.processDialogMessage(dialogMessage)
|
d.processDialogMessage(dialogMessage)
|
||||||
case systemMessage := <-d.systemChannel:
|
|
||||||
d.processSystemMessage(systemMessage)
|
|
||||||
case menuMessage := <-d.menuChannel:
|
|
||||||
d.processMenuMessage(menuMessage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call shutdown
|
||||||
|
d.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -148,6 +115,10 @@ func (d *Dispatcher) processQuit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) shutdown() {
|
||||||
|
d.logger.Trace("Shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterClient will register the given callback with the dispatcher
|
// RegisterClient will register the given callback with the dispatcher
|
||||||
// and return a DispatchClient that the caller can use to send messages
|
// and return a DispatchClient that the caller can use to send messages
|
||||||
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
||||||
@@ -195,35 +166,12 @@ func (d *Dispatcher) processCallResult(result *servicebus.Message) {
|
|||||||
if client == nil {
|
if client == nil {
|
||||||
// This is fatal - unknown target!
|
// This is fatal - unknown target!
|
||||||
d.logger.Fatal("Unknown target for call result: %+v", result)
|
d.logger.Fatal("Unknown target for call result: %+v", result)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string))
|
d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string))
|
||||||
client.frontend.CallResult(result.Data().(string))
|
client.frontend.CallResult(result.Data().(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
// processSystem
|
|
||||||
func (d *Dispatcher) processSystemMessage(result *servicebus.Message) {
|
|
||||||
|
|
||||||
d.logger.Trace("Got system in message dispatcher: %+v", result)
|
|
||||||
|
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
|
||||||
command := splitTopic[1]
|
|
||||||
callbackID := splitTopic[2]
|
|
||||||
switch command {
|
|
||||||
case "isdarkmode":
|
|
||||||
d.lock.RLock()
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.DarkModeEnabled(callbackID)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d.lock.RUnlock()
|
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown system command: %s", command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processEvent will
|
// processEvent will
|
||||||
func (d *Dispatcher) processEvent(result *servicebus.Message) {
|
func (d *Dispatcher) processEvent(result *servicebus.Message) {
|
||||||
|
|
||||||
@@ -282,7 +230,7 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
client.frontend.WindowUnFullscreen()
|
client.frontend.WindowUnFullscreen()
|
||||||
}
|
}
|
||||||
case "setcolour":
|
case "setcolour":
|
||||||
colour, ok := result.Data().(int)
|
colour, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
|
d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
@@ -291,105 +239,6 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.WindowSetColour(colour)
|
client.frontend.WindowSetColour(colour)
|
||||||
}
|
}
|
||||||
case "show":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowShow()
|
|
||||||
}
|
|
||||||
case "hide":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowHide()
|
|
||||||
}
|
|
||||||
case "center":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowCenter()
|
|
||||||
}
|
|
||||||
case "maximise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowMaximise()
|
|
||||||
}
|
|
||||||
case "unmaximise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowUnmaximise()
|
|
||||||
}
|
|
||||||
case "minimise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowMinimise()
|
|
||||||
}
|
|
||||||
case "unminimise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowUnminimise()
|
|
||||||
}
|
|
||||||
case "position":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:position' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x, err1 := strconv.Atoi(splitTopic[2])
|
|
||||||
y, err2 := strconv.Atoi(splitTopic[3])
|
|
||||||
if err1 != nil || err2 != nil {
|
|
||||||
d.logger.Error("Invalid integer parameters for 'window:position' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowPosition(x, y)
|
|
||||||
}
|
|
||||||
case "size":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:size' : %#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:size' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notifh clients
|
|
||||||
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:
|
default:
|
||||||
d.logger.Error("Unknown window command: %s", command)
|
d.logger.Error("Unknown window command: %s", command)
|
||||||
}
|
}
|
||||||
@@ -400,6 +249,7 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
// processDialogMessage processes dialog messages
|
// processDialogMessage processes dialog messages
|
||||||
func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
splitTopic := strings.Split(result.Topic(), ":")
|
||||||
|
|
||||||
if len(splitTopic) < 4 {
|
if len(splitTopic) < 4 {
|
||||||
d.logger.Error("Invalid dialog message : %#v", result.Data())
|
d.logger.Error("Invalid dialog message : %#v", result.Data())
|
||||||
return
|
return
|
||||||
@@ -409,142 +259,61 @@ func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
|||||||
switch command {
|
switch command {
|
||||||
case "select":
|
case "select":
|
||||||
dialogType := splitTopic[2]
|
dialogType := splitTopic[2]
|
||||||
|
title := splitTopic[3]
|
||||||
switch dialogType {
|
switch dialogType {
|
||||||
case "open":
|
case "file":
|
||||||
dialogOptions, ok := result.Data().(*dialog.OpenDialog)
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:open' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:file' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening File dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.OpenDialog(dialogOptions, callbackID)
|
result = client.frontend.OpenFileDialog(title)
|
||||||
}
|
}
|
||||||
case "save":
|
|
||||||
dialogOptions, ok := result.Data().(*dialog.SaveDialog)
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
case "filesave":
|
||||||
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:save' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:filesave' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening Save File dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.SaveDialog(dialogOptions, callbackID)
|
result = client.frontend.SaveFileDialog(title)
|
||||||
}
|
}
|
||||||
case "message":
|
|
||||||
dialogOptions, ok := result.Data().(*dialog.MessageDialog)
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
case "directory":
|
||||||
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:message' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:directory' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening Directory dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.MessageDialog(dialogOptions, callbackID)
|
result = client.frontend.OpenDirectoryDialog(title)
|
||||||
}
|
}
|
||||||
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown dialog type: %s", dialogType)
|
d.logger.Error("Unknown dialog command: %s", command)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown dialog command: %s", command)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
|
||||||
if len(splitTopic) < 2 {
|
|
||||||
d.logger.Error("Invalid menu message : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
command := splitTopic[1]
|
|
||||||
switch command {
|
|
||||||
case "updateappmenu":
|
|
||||||
|
|
||||||
updatedMenu, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updateappmenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.SetApplicationMenu(updatedMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "settraymenu":
|
|
||||||
trayMenuJSON, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:settraymenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.SetTrayMenu(trayMenuJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "updatecontextmenu":
|
|
||||||
updatedContextMenu, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updatecontextmenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.UpdateContextMenu(updatedContextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "updatetraymenulabel":
|
|
||||||
updatedTrayMenuLabel, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel)
|
|
||||||
}
|
|
||||||
case "deletetraymenu":
|
|
||||||
traymenuid, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.DeleteTrayMenuByID(traymenuid)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) Close() {
|
|
||||||
d.cancel()
|
|
||||||
d.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var internalMethods = slicer.String([]string{"WailsInit", "Wails Shutdown"})
|
||||||
|
|
||||||
var structCache = make(map[string]*ParsedStruct)
|
var structCache = make(map[string]*ParsedStruct)
|
||||||
var boundStructs = make(map[string]*ParsedStruct)
|
var boundStructs = make(map[string]*ParsedStruct)
|
||||||
var boundMethods = []string{}
|
var boundMethods = []string{}
|
||||||
@@ -47,7 +49,7 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo}
|
cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo}
|
||||||
pkgs, err := packages.Load(cfg, projectPath)
|
pkgs, err := packages.Load(cfg, projectPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "load: %v\n", err)
|
fmt.Fprintf(os.Stderr, "load: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if packages.PrintErrors(pkgs) > 0 {
|
if packages.PrintErrors(pkgs) > 0 {
|
||||||
@@ -63,6 +65,7 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
var wailsPkgVar = ""
|
var wailsPkgVar = ""
|
||||||
|
|
||||||
ast.Inspect(file, func(n ast.Node) bool {
|
ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
var s string
|
||||||
switch x := n.(type) {
|
switch x := n.(type) {
|
||||||
// Parse import declarations
|
// Parse import declarations
|
||||||
case *ast.ImportSpec:
|
case *ast.ImportSpec:
|
||||||
@@ -201,6 +204,10 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
// This is a struct pointer method
|
// This is a struct pointer method
|
||||||
i, ok := se.X.(*ast.Ident)
|
i, ok := se.X.(*ast.Ident)
|
||||||
if ok {
|
if ok {
|
||||||
|
// We want to ignore Internal functions
|
||||||
|
if internalMethods.Contains(x.Name.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// If we haven't already found this struct,
|
// If we haven't already found this struct,
|
||||||
// Create a placeholder in the cache
|
// Create a placeholder in the cache
|
||||||
parsedStruct := structCache[i.Name]
|
parsedStruct := structCache[i.Name]
|
||||||
@@ -431,6 +438,4 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
println()
|
println()
|
||||||
println("}")
|
println("}")
|
||||||
println()
|
println()
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process defines a process that can be executed
|
// Process defines a process that can be executed
|
||||||
type Process struct {
|
type Process struct {
|
||||||
logger *clilogger.CLILogger
|
logger *logger.Logger
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
exitChannel chan bool
|
exitChannel chan bool
|
||||||
Running bool
|
Running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcess creates a new process struct
|
// NewProcess creates a new process struct
|
||||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
func NewProcess(logger *logger.Logger, cmd string, args ...string) *Process {
|
||||||
result := &Process{
|
return &Process{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
cmd: exec.Command(cmd, args...),
|
cmd: exec.Command(cmd, args...),
|
||||||
exitChannel: make(chan bool, 1),
|
exitChannel: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
result.cmd.Stdout = os.Stdout
|
|
||||||
result.cmd.Stderr = os.Stderr
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the process
|
// Start the process
|
||||||
@@ -37,15 +33,10 @@ func (p *Process) Start() error {
|
|||||||
|
|
||||||
p.Running = true
|
p.Running = true
|
||||||
|
|
||||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
go func(cmd *exec.Cmd, running *bool, logger *logger.Logger, exitChannel chan bool) {
|
||||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
logger.Info("Starting process (PID: %d)", cmd.Process.Pid)
|
||||||
err := cmd.Wait()
|
cmd.Wait()
|
||||||
if err != nil {
|
logger.Info("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||||
if err.Error() != "signal: killed" {
|
|
||||||
logger.Fatal("Fatal error from app: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
|
||||||
*running = false
|
*running = false
|
||||||
exitChannel <- true
|
exitChannel <- true
|
||||||
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ type Project struct {
|
|||||||
// The path to the project directory
|
// The path to the project directory
|
||||||
Path string
|
Path string
|
||||||
|
|
||||||
// Assets directory
|
|
||||||
AssetsDir string `json:"assetsdir"`
|
|
||||||
|
|
||||||
// The output filename
|
// The output filename
|
||||||
OutputFilename string `json:"outputfilename"`
|
OutputFilename string `json:"outputfilename"`
|
||||||
|
|
||||||
@@ -36,15 +33,6 @@ type Project struct {
|
|||||||
|
|
||||||
// The platform to target
|
// The platform to target
|
||||||
Platform string
|
Platform string
|
||||||
|
|
||||||
// The application author
|
|
||||||
Author Author
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author stores details about the application author
|
|
||||||
type Author struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the project from the current working directory
|
// Load the project from the current working directory
|
||||||
@@ -75,11 +63,6 @@ func Load(projectPath string) (*Project, error) {
|
|||||||
result.Name = "wailsapp"
|
result.Name = "wailsapp"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default assets directory if none given
|
|
||||||
if result.AssetsDir == "" {
|
|
||||||
result.AssetsDir = filepath.Join(result.Path, "assets")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix up OutputFilename
|
// Fix up OutputFilename
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
|
|||||||
36
v2/internal/runtime/goruntime/browser.go
Normal file
36
v2/internal/runtime/goruntime/browser.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Browser defines all browser related operations
|
||||||
|
type Browser interface {
|
||||||
|
Open(url string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type browser struct{}
|
||||||
|
|
||||||
|
// Open a url / file using the system default application
|
||||||
|
// Credit: https://gist.github.com/hyg/9c4afcd91fe24316cbf0
|
||||||
|
func (b *browser) Open(url string) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
err = exec.Command("xdg-open", url).Start()
|
||||||
|
case "windows":
|
||||||
|
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||||
|
case "darwin":
|
||||||
|
err = exec.Command("open", url).Start()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported platform")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBrowser() *browser {
|
||||||
|
return &browser{}
|
||||||
|
}
|
||||||
16
v2/internal/runtime/goruntime/browser_test.go
Normal file
16
v2/internal/runtime/goruntime/browser_test.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrowserOpen(t *testing.T) {
|
||||||
|
mylogger := logger.New(os.Stdout)
|
||||||
|
myServiceBus := servicebus.New(mylogger)
|
||||||
|
myRuntime := New(myServiceBus)
|
||||||
|
myRuntime.Browser.Open("http://www.google.com")
|
||||||
|
}
|
||||||
105
v2/internal/runtime/goruntime/dialog.go
Normal file
105
v2/internal/runtime/goruntime/dialog.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dialog defines all Dialog related operations
|
||||||
|
type Dialog interface {
|
||||||
|
SaveFile(title string) string
|
||||||
|
SelectFile(title string) string
|
||||||
|
SelectDirectory(title string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialog exposes the Dialog interface
|
||||||
|
type dialog struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDialogs creates a new Dialogs struct
|
||||||
|
func newDialog(bus *servicebus.ServiceBus) Dialog {
|
||||||
|
return &dialog{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectFile prompts the user to select a file
|
||||||
|
func (r *dialog) SelectFile(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:fileselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:file:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
result := <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveFile prompts the user to select a file to save to
|
||||||
|
func (r *dialog) SaveFile(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:filesaveselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:filesave:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
result := <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDirectory prompts the user to select a file
|
||||||
|
func (r *dialog) SelectDirectory(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:directoryselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:directory:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
var result *servicebus.Message = <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
43
v2/internal/runtime/goruntime/events.go
Normal file
43
v2/internal/runtime/goruntime/events.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Events defines all events related operations
|
||||||
|
type Events interface {
|
||||||
|
On(eventName string, callback func(optionalData ...interface{}))
|
||||||
|
Emit(eventName string, optionalData ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// event exposes the events interface
|
||||||
|
type event struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEvents creates a new Events struct
|
||||||
|
func newEvents(bus *servicebus.ServiceBus) Events {
|
||||||
|
return &event{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On pass through
|
||||||
|
func (r *event) On(eventName string, callback func(optionalData ...interface{})) {
|
||||||
|
eventMessage := &message.OnEventMessage{
|
||||||
|
Name: eventName,
|
||||||
|
Callback: callback,
|
||||||
|
}
|
||||||
|
r.bus.Publish("event:on", eventMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit pass through
|
||||||
|
func (r *event) Emit(eventName string, optionalData ...interface{}) {
|
||||||
|
eventMessage := &message.EventMessage{
|
||||||
|
Name: eventName,
|
||||||
|
Data: optionalData,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.bus.Publish("event:emit:from:g", eventMessage)
|
||||||
|
}
|
||||||
28
v2/internal/runtime/goruntime/runtime.go
Normal file
28
v2/internal/runtime/goruntime/runtime.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
|
||||||
|
// Runtime is a means for the user to interact with the application at runtime
|
||||||
|
type Runtime struct {
|
||||||
|
Browser Browser
|
||||||
|
Events Events
|
||||||
|
Window Window
|
||||||
|
Dialog Dialog
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new runtime
|
||||||
|
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||||
|
return &Runtime{
|
||||||
|
Browser: newBrowser(),
|
||||||
|
Events: newEvents(serviceBus),
|
||||||
|
Window: newWindow(serviceBus),
|
||||||
|
Dialog: newDialog(serviceBus),
|
||||||
|
bus: serviceBus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit the application
|
||||||
|
func (r *Runtime) Quit() {
|
||||||
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
|
}
|
||||||
54
v2/internal/runtime/goruntime/window.go
Normal file
54
v2/internal/runtime/goruntime/window.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Window defines all Window related operations
|
||||||
|
type Window interface {
|
||||||
|
Close()
|
||||||
|
SetTitle(title string)
|
||||||
|
Fullscreen()
|
||||||
|
UnFullscreen()
|
||||||
|
SetColour(colour string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window exposes the Windows interface
|
||||||
|
type window struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newWindow creates a new window struct
|
||||||
|
func newWindow(bus *servicebus.ServiceBus) Window {
|
||||||
|
return &window{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the Window
|
||||||
|
// DISCUSSION:
|
||||||
|
// Should we even be doing this now we have a server build?
|
||||||
|
// Runtime.Quit() makes more sense than closing a window...
|
||||||
|
func (w *window) Close() {
|
||||||
|
w.bus.Publish("quit", "runtime.Close()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle sets the title of the window
|
||||||
|
func (w *window) SetTitle(title string) {
|
||||||
|
w.bus.Publish("window:settitle", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fullscreen makes the window fullscreen
|
||||||
|
func (w *window) Fullscreen() {
|
||||||
|
w.bus.Publish("window:fullscreen", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnFullscreen makes the window UnFullscreen
|
||||||
|
func (w *window) UnFullscreen() {
|
||||||
|
w.bus.Publish("window:unfullscreen", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColour sets the window colour to the given string
|
||||||
|
func (w *window) SetColour(colour string) {
|
||||||
|
w.bus.Publish("window:setcolour", colour)
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@ export function SetBindings(bindingsMap) {
|
|||||||
window.backend[packageName][structName][methodName] = function () {
|
window.backend[packageName][structName][methodName] = function () {
|
||||||
|
|
||||||
// No timeout by default
|
// No timeout by default
|
||||||
let timeout = 0;
|
var timeout = 0;
|
||||||
|
|
||||||
// Actual function
|
// Actual function
|
||||||
function dynamic() {
|
function dynamic() {
|
||||||
@@ -89,3 +89,19 @@ 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;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,23 @@ The lightweight framework for web-like apps
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the given URL / filename in the system browser
|
* Opens the given URL in the system browser
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} target
|
* @param {string} url
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function Open(target) {
|
export function OpenURL(url) {
|
||||||
return SendMessage('RBO' + target);
|
return SendMessage('RBU' + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function OpenFile(filename) {
|
||||||
|
return SendMessage('runtime:browser:openfile', filename);
|
||||||
|
}
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ export function Callback(incomingMessage) {
|
|||||||
* @param {any[]=} data
|
* @param {any[]=} data
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function SystemCall(method) {
|
export function SystemCall(method, data) {
|
||||||
var data = [].slice.apply(arguments).slice(1);
|
|
||||||
return Call('.wails.' + method, data);
|
return Call('.wails.' + method, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,16 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import { SetBindings } from './bindings';
|
import { SetBindings } from './bindings';
|
||||||
import { Init } from './main';
|
import { Init } from './main';
|
||||||
import {RaiseError} from '../desktop/darwin';
|
|
||||||
|
|
||||||
// Setup global error handler
|
// Setup global error handler
|
||||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
window.onerror = function (/*msg, url, lineNo, columnNo, error*/) {
|
||||||
const errorMessage = {
|
// window.wails.Log.Error('**** Caught Unhandled Error ****');
|
||||||
message: msg,
|
// window.wails.Log.Error('Message: ' + msg);
|
||||||
url: url,
|
// window.wails.Log.Error('URL: ' + url);
|
||||||
line: lineNo,
|
// window.wails.Log.Error('Line No: ' + lineNo);
|
||||||
column: columnNo,
|
// window.wails.Log.Error('Column No: ' + columnNo);
|
||||||
error: JSON.stringify(error),
|
// window.wails.Log.Error('error: ' + error);
|
||||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
(function () { window.wails.Log.Error(new Error().stack); })();
|
||||||
};
|
|
||||||
RaiseError(errorMessage);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialise the Runtime
|
// Initialise the Runtime
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { Error } from './log';
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
// Defines a single listener with a maximum number of times to callback
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Listener class defines a listener! :-)
|
* The Listener class defines a listener! :-)
|
||||||
*
|
*
|
||||||
@@ -44,7 +43,7 @@ class Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let eventListeners = {};
|
var eventListeners = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
@@ -72,17 +71,6 @@ export function On(eventName, callback) {
|
|||||||
OnMultiple(eventName, callback);
|
OnMultiple(eventName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers listeners for when the system theme changes from light/dark. A bool is
|
|
||||||
* sent to the listener, true if it is dark mode.
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {function} callback
|
|
||||||
*/
|
|
||||||
export function OnThemeChange(callback) {
|
|
||||||
On('wails:system:themechange', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked once then destroyed
|
* Registers an event listener that will be invoked once then destroyed
|
||||||
*
|
*
|
||||||
@@ -94,43 +82,11 @@ export function Once(eventName, callback) {
|
|||||||
OnMultiple(eventName, callback, 1);
|
OnMultiple(eventName, callback, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyListeners(eventData) {
|
|
||||||
|
|
||||||
// Get the event name
|
|
||||||
let eventName = eventData.name;
|
|
||||||
|
|
||||||
// Check if we have any listeners for this event
|
|
||||||
if (eventListeners[eventName]) {
|
|
||||||
|
|
||||||
// Keep a list of listener indexes to destroy
|
|
||||||
const newEventListenerList = eventListeners[eventName].slice();
|
|
||||||
|
|
||||||
// Iterate listeners
|
|
||||||
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
|
||||||
|
|
||||||
// Get next listener
|
|
||||||
const listener = eventListeners[eventName][count];
|
|
||||||
|
|
||||||
let data = eventData.data;
|
|
||||||
|
|
||||||
// Do the callback
|
|
||||||
const destroy = listener.Callback(data);
|
|
||||||
if (destroy) {
|
|
||||||
// if the listener indicated to destroy itself, add it to the destroy list
|
|
||||||
newEventListenerList.splice(count, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update callbacks with new list of listeners
|
|
||||||
eventListeners[eventName] = newEventListenerList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify informs frontend listeners that an event was emitted with the given data
|
* Notify informs frontend listeners that an event was emitted with the given data
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} notifyMessage - encoded notification message
|
* @param {string} encoded notification message
|
||||||
|
|
||||||
*/
|
*/
|
||||||
export function Notify(notifyMessage) {
|
export function Notify(notifyMessage) {
|
||||||
@@ -144,7 +100,33 @@ export function Notify(notifyMessage) {
|
|||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners(message);
|
var eventName = message.name;
|
||||||
|
|
||||||
|
// Check if we have any listeners for this event
|
||||||
|
if (eventListeners[eventName]) {
|
||||||
|
|
||||||
|
// Keep a list of listener indexes to destroy
|
||||||
|
const newEventListenerList = eventListeners[eventName].slice();
|
||||||
|
|
||||||
|
// Iterate listeners
|
||||||
|
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
||||||
|
|
||||||
|
// Get next listener
|
||||||
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
|
var data = message.data;
|
||||||
|
|
||||||
|
// Do the callback
|
||||||
|
const destroy = listener.Callback(data);
|
||||||
|
if (destroy) {
|
||||||
|
// if the listener indicated to destroy itself, add it to the destroy list
|
||||||
|
newEventListenerList.splice(count, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update callbacks with new list of listners
|
||||||
|
eventListeners[eventName] = newEventListenerList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,15 +137,66 @@ export function Notify(notifyMessage) {
|
|||||||
*/
|
*/
|
||||||
export function Emit(eventName) {
|
export function Emit(eventName) {
|
||||||
|
|
||||||
const payload = {
|
// Calculate the data
|
||||||
name: eventName,
|
if (arguments.length > 1) {
|
||||||
data: [].slice.apply(arguments).slice(1),
|
// Notify backend
|
||||||
};
|
const payload = {
|
||||||
|
name: eventName,
|
||||||
|
data: [].slice.apply(arguments).slice(1),
|
||||||
|
};
|
||||||
|
SendMessage('Ej' + JSON.stringify(payload));
|
||||||
|
} else {
|
||||||
|
SendMessage('ej' + eventName);
|
||||||
|
}
|
||||||
|
|
||||||
// Notify JS listeners
|
}
|
||||||
notifyListeners(payload);
|
|
||||||
|
|
||||||
// Notify Go listeners
|
// Callbacks for the heartbeat calls
|
||||||
SendMessage('Ej' + JSON.stringify(payload));
|
const heartbeatCallbacks = {};
|
||||||
|
|
||||||
}
|
/**
|
||||||
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
|
|
||||||
|
// Declare interval variable
|
||||||
|
let interval = null;
|
||||||
|
|
||||||
|
// Setup callback
|
||||||
|
function dynamicCallback() {
|
||||||
|
// Kill interval
|
||||||
|
clearInterval(interval);
|
||||||
|
// Callback
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register callback
|
||||||
|
heartbeatCallbacks[eventName] = dynamicCallback;
|
||||||
|
|
||||||
|
// Start emitting the event
|
||||||
|
interval = setInterval(function () {
|
||||||
|
Emit(eventName);
|
||||||
|
}, timeInMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
export function Acknowledge(eventName) {
|
||||||
|
// If we are waiting for acknowledgement for this event type
|
||||||
|
if (heartbeatCallbacks[eventName]) {
|
||||||
|
// Acknowledge!
|
||||||
|
heartbeatCallbacks[eventName]();
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot acknowledge unknown heartbeat '${eventName}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,26 +25,6 @@ function sendLogMessage(level, message) {
|
|||||||
SendMessage('L' + level + message);
|
SendMessage('L' + level + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given trace message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function Trace(message) {
|
|
||||||
sendLogMessage('T', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function Print(message) {
|
|
||||||
sendLogMessage('P', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log the given debug message with the backend
|
* Log the given debug message with the backend
|
||||||
*
|
*
|
||||||
@@ -94,22 +74,3 @@ export function Error(message) {
|
|||||||
export function Fatal(message) {
|
export function Fatal(message) {
|
||||||
sendLogMessage('F', message);
|
sendLogMessage('F', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Log level to the given log level
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {number} loglevel
|
|
||||||
*/
|
|
||||||
export function SetLogLevel(loglevel) {
|
|
||||||
sendLogMessage('S', loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log levels
|
|
||||||
export const Level = {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -10,15 +10,11 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import * as Log from './log';
|
import * as Log from './log';
|
||||||
import * as Browser from './browser';
|
import * as Browser from './browser';
|
||||||
import * as Window from './window';
|
import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
|
||||||
import * as Dialog from './dialog';
|
import { Callback } from './calls';
|
||||||
import { On, Once, OnMultiple, Emit, Notify } from './events';
|
import { AddScript, InjectCSS } from './utils';
|
||||||
import { Callback, SystemCall } from './calls';
|
|
||||||
import { AddScript, InjectCSS, DisableDefaultContextMenu } from './utils';
|
|
||||||
import { AddIPCListener } from 'ipc';
|
import { AddIPCListener } from 'ipc';
|
||||||
import * as Platform from 'platform';
|
import * as Platform from 'platform';
|
||||||
import * as Store from './store';
|
|
||||||
import * as Tray from './tray';
|
|
||||||
|
|
||||||
export function Init() {
|
export function Init() {
|
||||||
// Backend is where the Go struct wrappers get bound to
|
// Backend is where the Go struct wrappers get bound to
|
||||||
@@ -29,37 +25,23 @@ export function Init() {
|
|||||||
System: Platform.System,
|
System: Platform.System,
|
||||||
Log,
|
Log,
|
||||||
Browser,
|
Browser,
|
||||||
Window,
|
|
||||||
Tray,
|
|
||||||
Dialog,
|
|
||||||
Events: {
|
Events: {
|
||||||
On,
|
On,
|
||||||
Once,
|
|
||||||
OnMultiple,
|
OnMultiple,
|
||||||
Emit,
|
Emit,
|
||||||
|
Heartbeat,
|
||||||
|
Acknowledge,
|
||||||
},
|
},
|
||||||
_: {
|
_: {
|
||||||
Callback,
|
Callback,
|
||||||
Notify,
|
Notify,
|
||||||
AddScript,
|
AddScript,
|
||||||
InjectCSS,
|
InjectCSS,
|
||||||
DisableDefaultContextMenu,
|
Init,
|
||||||
// Init,
|
AddIPCListener
|
||||||
AddIPCListener,
|
}
|
||||||
SystemCall,
|
|
||||||
},
|
|
||||||
Store,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup system. Store uses window.wails so needs to be setup after that
|
|
||||||
window.wails.System = {
|
|
||||||
IsDarkMode: Store.New('wails:isdarkmode'),
|
|
||||||
LogLevel: Store.New('wails:loglevel'),
|
|
||||||
AppConfig: Store.New('wails:appconfig'),
|
|
||||||
};
|
|
||||||
// Copy platform specific information into it
|
|
||||||
Object.assign(window.wails.System, Platform.System);
|
|
||||||
|
|
||||||
// Do platform specific Init
|
// Do platform specific Init
|
||||||
Platform.Init();
|
Platform.Init();
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,3 @@ export function InjectCSS(css) {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DisableDefaultContextMenu() {
|
|
||||||
window.disableWailsDefaultContextMenu = true;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const System = {
|
export const System = {
|
||||||
...common,
|
Platform: "linux",
|
||||||
Platform: () => "linux",
|
AppType: "desktop"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SendMessage(message) {
|
export function SendMessage(message) {
|
||||||
@@ -27,7 +27,7 @@ export function Init() {
|
|||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||||
window.addEventListener('mousedown', function (e) {
|
window.addEventListener('mousedown', function (e) {
|
||||||
let currentElement = e.target;
|
var currentElement = e.target;
|
||||||
while (currentElement != null) {
|
while (currentElement != null) {
|
||||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
6
v2/internal/runtime/js/package-lock.json
generated
6
v2/internal/runtime/js/package-lock.json
generated
@@ -1923,9 +1923,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001171",
|
"version": "1.0.30001040",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001171.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001040.tgz",
|
||||||
"integrity": "sha512-5Alrh8TTYPG9IH4UkRqEBZoEToWRLvPbSQokvzSz0lii8/FOWKG4keO1HoYfPWs8IF/NH/dyNPg1cmJGvV3Zlg==",
|
"integrity": "sha512-Ep0tEPeI5wCvmJNrXjE3etgfI+lkl1fTDU6Y3ZH1mhrjkPlVI9W4pcKbMo+BQLpEWKVYYp2EmYaRsqpPC3k7lQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"webpack": "^4.41.5",
|
"webpack": "^4.41.5",
|
||||||
"webpack-cli": "^3.3.10"
|
"webpack-cli": "^3.3.10"
|
||||||
},
|
}
|
||||||
"dependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
7a2c438e79cf603ba763055e515650be
|
77a32d4461a2cad598edfd551fe64dcd
|
||||||
@@ -1 +1 @@
|
|||||||
src
|
bridge.js
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Wails Runtime
|
# Wails Runtime
|
||||||
|
|
||||||
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) V2 project, not a standalone module.
|
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) project, not a standalone module.
|
||||||
|
|||||||
@@ -10,12 +10,28 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the given URL or Filename in the system browser
|
* Opens the given URL in the system browser
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} target
|
* @param {string} url
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function Open(target) {
|
function OpenURL(url) {
|
||||||
return window.wails.Browser.Open(target);
|
return window.wails.Browser.OpenURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function OpenFile(filename) {
|
||||||
|
return window.wails.Browser.OpenFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OpenURL: OpenURL,
|
||||||
|
OpenFile: OpenFile
|
||||||
|
};
|
||||||
@@ -56,14 +56,28 @@ function Emit(eventName) {
|
|||||||
return window.wails.Events.Emit.apply(null, args);
|
return window.wails.Events.Emit.apply(null, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers listeners for when the system theme changes
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function OnThemeChange(callback) {
|
function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
On('wails:system:themechange', callback);
|
window.wails.Events.Heartbeat(eventName, timeInMilliseconds, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
function Acknowledge(eventName) {
|
||||||
|
return window.wails.Events.Acknowledge(eventName);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -71,5 +85,6 @@ module.exports = {
|
|||||||
On: On,
|
On: On,
|
||||||
Once: Once,
|
Once: Once,
|
||||||
Emit: Emit,
|
Emit: Emit,
|
||||||
OnThemeChange: OnThemeChange,
|
Heartbeat: Heartbeat,
|
||||||
|
Acknowledge: Acknowledge
|
||||||
};
|
};
|
||||||
@@ -9,25 +9,13 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
import bridge from './bridge';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ready will execute the callback when Wails has loaded
|
* Initialises the Wails runtime
|
||||||
* and initialised.
|
|
||||||
*
|
*
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function ready(callback) {
|
function Init(callback) {
|
||||||
|
window.wails._.Init(callback);
|
||||||
// If window.wails exists, we are ready
|
|
||||||
if( window.wails ) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not we need to setup the bridge
|
|
||||||
bridge.InitBridge(callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = Init;
|
||||||
ready: ready,
|
|
||||||
};
|
|
||||||
@@ -11,26 +11,6 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
function Print(message) {
|
|
||||||
window.wails.Log.Print(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given trace message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
function Trace(message) {
|
|
||||||
window.wails.Log.Trace(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log the given debug message with the backend
|
* Log the given debug message with the backend
|
||||||
*
|
*
|
||||||
@@ -74,40 +54,17 @@ function Error(message) {
|
|||||||
/**
|
/**
|
||||||
* Log the given fatal message with the backend
|
* Log the given fatal message with the backend
|
||||||
*
|
*
|
||||||
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
function Fatal(message) {
|
function Fatal(message) {
|
||||||
window.wails.Log.Fatal(message);
|
window.wails.Log.Fatal(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Log level to the given log level
|
|
||||||
*
|
|
||||||
* @param {number} loglevel
|
|
||||||
*/
|
|
||||||
function SetLogLevel(loglevel) {
|
|
||||||
window.wails.Log.SetLogLevel(loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log levels
|
|
||||||
const Level = {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Print: Print,
|
|
||||||
Trace: Trace,
|
|
||||||
Debug: Debug,
|
Debug: Debug,
|
||||||
Info: Info,
|
Info: Info,
|
||||||
Warning: Warning,
|
Warning: Warning,
|
||||||
Error: Error,
|
Error: Error,
|
||||||
Fatal: Fatal,
|
Fatal: Fatal
|
||||||
SetLogLevel: SetLogLevel,
|
|
||||||
Level: Level,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,22 +11,12 @@ The lightweight framework for web-like apps
|
|||||||
|
|
||||||
const Log = require('./log');
|
const Log = require('./log');
|
||||||
const Browser = require('./browser');
|
const Browser = require('./browser');
|
||||||
const Dialog = require('./dialog');
|
|
||||||
const Events = require('./events');
|
const Events = require('./events');
|
||||||
const Init = require('./init');
|
const Init = require('./init');
|
||||||
const System = require('./system');
|
|
||||||
const Store = require('./store');
|
|
||||||
const Window = require('./window');
|
|
||||||
const Tray = require('./tray');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Browser: Browser,
|
|
||||||
Dialog: Dialog,
|
|
||||||
Events: Events,
|
|
||||||
ready: Init.ready,
|
|
||||||
Log: Log,
|
Log: Log,
|
||||||
System: System,
|
Browser: Browser,
|
||||||
Store: Store,
|
Events: Events,
|
||||||
Window: Window,
|
Init: Init
|
||||||
Tray: Tray,
|
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wailsapp/runtime",
|
||||||
"version": "1.3.12",
|
"version": "1.0.10",
|
||||||
"description": "Wails V2 Javascript runtime library",
|
"description": "Wails Javascript runtime library",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"types": "runtime.d.ts",
|
"types": "runtime.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
129
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
129
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
@@ -1,137 +1,26 @@
|
|||||||
export = wailsapp__runtime;
|
export = wailsapp__runtime;
|
||||||
|
|
||||||
interface Store {
|
|
||||||
get(): any;
|
|
||||||
set(value: any): void;
|
|
||||||
subscribe(callback: (newvalue: any) => void): void;
|
|
||||||
update(callback: (currentvalue: any) => any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MacTitleBar {
|
|
||||||
TitleBarAppearsTransparent: boolean; // NSWindow.titleBarAppearsTransparent
|
|
||||||
HideTitle: boolean; // NSWindow.hideTitle
|
|
||||||
HideTitleBar: boolean; // NSWindow.hideTitleBar
|
|
||||||
FullSizeContent: boolean; // Makes the webview portion of the window the full size of the window, even over the titlebar
|
|
||||||
UseToolbar: boolean; // Set true to add a blank toolbar to the window (makes the title bar larger)
|
|
||||||
HideToolbarSeparator: boolean; // Set true to remove the separator between the toolbar and the main content area
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MacAppConfig {
|
|
||||||
TitleBar: MacTitleBar;
|
|
||||||
}
|
|
||||||
interface LinuxAppConfig {
|
|
||||||
}
|
|
||||||
interface WindowsAppConfig {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppConfig {
|
|
||||||
Title: string; // Application Title
|
|
||||||
Width: number; // Window Width
|
|
||||||
Height: number; // Window Height
|
|
||||||
DisableResize: boolean; // True if resize is disabled
|
|
||||||
Fullscreen: boolean; // App started in fullscreen
|
|
||||||
MinWidth: number; // Window Minimum Width
|
|
||||||
MinHeight: number; // Window Minimum Height
|
|
||||||
MaxWidth: number; // Window Maximum Width
|
|
||||||
MaxHeight: number; // Window Maximum Height
|
|
||||||
StartHidden: boolean; // Start with window hidden
|
|
||||||
DevTools: boolean; // Enables the window devtools
|
|
||||||
RBGA: number; // The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
|
||||||
Mac?: MacAppConfig; // - Configuration when running on Mac
|
|
||||||
Linux?: LinuxAppConfig; // - Configuration when running on Linux
|
|
||||||
Windows?: WindowsAppConfig; // - Configuration when running on Windows
|
|
||||||
Appearance: string; // The default application appearance. Use the values listed here: https://developer.apple.com/documentation/appkit/nsappearance?language=objc
|
|
||||||
WebviewIsTransparent: number; // Makes the background of the webview content transparent. Use this with the Alpha part of the window colour to make parts of your application transparent.
|
|
||||||
WindowBackgroundIsTranslucent: number; // Makes the transparent parts of the application window translucent. Example: https://en.wikipedia.org/wiki/MacOS_Big_Sur#/media/File:MacOS_Big_Sur_-_Safari_Extensions_category_in_App_Store.jpg
|
|
||||||
LogLevel: number; // The initial log level (lower is more verbose)
|
|
||||||
}
|
|
||||||
interface Level {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OpenDialogOptions {
|
|
||||||
DefaultDirectory: string;
|
|
||||||
DefaultFilename: string;
|
|
||||||
Title: string;
|
|
||||||
Filters: string;
|
|
||||||
AllowFiles: boolean;
|
|
||||||
AllowDirectories: boolean;
|
|
||||||
AllowMultiple: boolean;
|
|
||||||
ShowHiddenFiles: boolean;
|
|
||||||
CanCreateDirectories: boolean;
|
|
||||||
ResolvesAliases: boolean;
|
|
||||||
TreatPackagesAsDirectories: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SaveDialogOptions {
|
|
||||||
DefaultDirectory: string;
|
|
||||||
DefaultFilename: string;
|
|
||||||
Title: string;
|
|
||||||
Filters: string;
|
|
||||||
ShowHiddenFiles: boolean;
|
|
||||||
CanCreateDirectories: boolean;
|
|
||||||
TreatPackagesAsDirectories: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DialogType {
|
|
||||||
InfoDialog: 'info',
|
|
||||||
WarningDialog: 'warning',
|
|
||||||
ErrorDialog: 'error',
|
|
||||||
QuestionDialog: 'question',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessageDialogOptions {
|
|
||||||
Type: DialogType;
|
|
||||||
Title: string;
|
|
||||||
Message: string;
|
|
||||||
Buttons: string[];
|
|
||||||
DefaultButton: string;
|
|
||||||
CancelButton: string;
|
|
||||||
Icon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const wailsapp__runtime: {
|
declare const wailsapp__runtime: {
|
||||||
Browser: {
|
Browser: {
|
||||||
Open(target: string): Promise<any>;
|
OpenFile(filename: string): Promise<any>;
|
||||||
|
OpenURL(url: string): Promise<any>;
|
||||||
};
|
};
|
||||||
Events: {
|
Events: {
|
||||||
Emit(eventName: string, data?: any): void;
|
Acknowledge(eventName: string): void;
|
||||||
On(eventName: string, callback: (data?: any) => void): void;
|
Emit(eventName: string): void;
|
||||||
OnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void;
|
Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
|
||||||
Once(eventName: string, callback: (data?: any) => void): void;
|
On(eventName: string, callback: () => void): void;
|
||||||
|
OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
|
||||||
|
Once(eventName: string, callback: () => void): void;
|
||||||
};
|
};
|
||||||
// Init(callback: () => void): void;
|
Init(callback: () => void): void;
|
||||||
Log: {
|
Log: {
|
||||||
Debug(message: string): void;
|
Debug(message: string): void;
|
||||||
Error(message: string): void;
|
Error(message: string): void;
|
||||||
Fatal(message: string): void;
|
Fatal(message: string): void;
|
||||||
Info(message: string): void;
|
Info(message: string): void;
|
||||||
Warning(message: string): void;
|
Warning(message: string): void;
|
||||||
Level: Level;
|
|
||||||
};
|
};
|
||||||
System: {
|
|
||||||
DarkModeEnabled(): Promise<boolean>;
|
|
||||||
OnThemeChange(callback: (darkModeEnabled: boolean) => void): void;
|
|
||||||
LogLevel(): Store;
|
|
||||||
Platform(): string;
|
|
||||||
AppType(): string;
|
|
||||||
AppConfig(): AppConfig;
|
|
||||||
};
|
|
||||||
Store: {
|
|
||||||
New(name: string, defaultValue?: any): Store;
|
|
||||||
};
|
|
||||||
Dialog: {
|
|
||||||
Open(options: OpenDialogOptions): Promise<Array<string>>;
|
|
||||||
Save(options: SaveDialogOptions): Promise<string>;
|
|
||||||
Message(options: MessageDialogOptions): Promise<string>;
|
|
||||||
};
|
|
||||||
Tray: {
|
|
||||||
SetIcon(trayIconID: string): void;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
mode: 'production',
|
mode: 'production',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, '..', 'assets'),
|
path: path.resolve(__dirname, '..', 'assets'),
|
||||||
filename: 'desktop_'+platform+'.js',
|
filename: 'desktop.js',
|
||||||
library: 'Wails'
|
library: 'Wails'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package servicebus
|
package servicebus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -13,26 +12,23 @@ import (
|
|||||||
type ServiceBus struct {
|
type ServiceBus struct {
|
||||||
listeners map[string][]chan *Message
|
listeners map[string][]chan *Message
|
||||||
messageQueue chan *Message
|
messageQueue chan *Message
|
||||||
|
quitChannel chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
closed bool
|
closed bool
|
||||||
debug bool
|
debug bool
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ServiceBus
|
// New creates a new ServiceBus
|
||||||
// The internal message queue is set to 100 messages
|
// The internal message queue is set to 100 messages
|
||||||
// Listener queues are set to 10
|
// Listener queues are set to 10
|
||||||
func New(logger *logger.Logger) *ServiceBus {
|
func New(logger *logger.Logger) *ServiceBus {
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
return &ServiceBus{
|
return &ServiceBus{
|
||||||
listeners: make(map[string][]chan *Message),
|
listeners: make(map[string][]chan *Message),
|
||||||
messageQueue: make(chan *Message, 100),
|
messageQueue: make(chan *Message, 100),
|
||||||
|
quitChannel: make(chan struct{}, 1),
|
||||||
logger: logger.CustomLogger("Service Bus"),
|
logger: logger.CustomLogger("Service Bus"),
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,22 +63,23 @@ func (s *ServiceBus) Debug() {
|
|||||||
// Start the service bus
|
// Start the service bus
|
||||||
func (s *ServiceBus) Start() error {
|
func (s *ServiceBus) Start() error {
|
||||||
|
|
||||||
|
s.logger.Trace("Starting")
|
||||||
|
|
||||||
// Prevent starting when closed
|
// Prevent starting when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return fmt.Errorf("cannot call start on closed servicebus")
|
return fmt.Errorf("cannot call start on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Trace("Starting")
|
// We run in a different thread
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer s.logger.Trace("Stopped")
|
|
||||||
|
quit := false
|
||||||
|
s.wg.Add(1)
|
||||||
|
|
||||||
// Loop until we get a quit message
|
// Loop until we get a quit message
|
||||||
for {
|
for !quit {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
|
||||||
return
|
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
case message := <-s.messageQueue:
|
case message := <-s.messageQueue:
|
||||||
@@ -93,9 +90,16 @@ func (s *ServiceBus) Start() error {
|
|||||||
}
|
}
|
||||||
// Dispatch message
|
// Dispatch message
|
||||||
s.dispatchMessage(message)
|
s.dispatchMessage(message)
|
||||||
|
|
||||||
|
// Listen for quit messages
|
||||||
|
case <-s.quitChannel:
|
||||||
|
quit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicate we have shut down
|
||||||
|
s.wg.Done()
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -112,7 +116,10 @@ func (s *ServiceBus) Stop() error {
|
|||||||
s.closed = true
|
s.closed = true
|
||||||
|
|
||||||
// Send quit message
|
// Send quit message
|
||||||
s.cancel()
|
s.quitChannel <- struct{}{}
|
||||||
|
|
||||||
|
// Wait for dispatcher to stop
|
||||||
|
s.wg.Wait()
|
||||||
|
|
||||||
// Close down subscriber channels
|
// Close down subscriber channels
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
@@ -127,6 +134,7 @@ func (s *ServiceBus) Stop() error {
|
|||||||
// Close message queue
|
// Close message queue
|
||||||
close(s.messageQueue)
|
close(s.messageQueue)
|
||||||
|
|
||||||
|
s.logger.Trace("Stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,22 +168,25 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish sends the given message on the service bus
|
// Publish sends the given message on the service bus
|
||||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
func (s *ServiceBus) Publish(topic string, data interface{}) error {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return
|
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := NewMessage(topic, data)
|
message := NewMessage(topic, data)
|
||||||
s.messageQueue <- message
|
s.messageQueue <- message
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublishForTarget sends the given message on the service bus for the given target
|
// PublishForTarget sends the given message on the service bus for the given target
|
||||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) error {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return
|
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := NewMessageForTarget(topic, data, target)
|
message := NewMessageForTarget(topic, data, target)
|
||||||
s.messageQueue <- message
|
s.messageQueue <- message
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package signal
|
package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -22,24 +20,24 @@ type Manager struct {
|
|||||||
// signalChannel
|
// signalChannel
|
||||||
signalchannel chan os.Signal
|
signalchannel chan os.Signal
|
||||||
|
|
||||||
// ctx
|
// Quit channel
|
||||||
ctx context.Context
|
quitChannel <-chan *servicebus.Message
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
// Parent waitgroup
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new signal manager
|
// NewManager creates a new signal manager
|
||||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||||
|
|
||||||
|
// Register quit channel
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
result := &Manager{
|
result := &Manager{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
logger: logger.CustomLogger("Event Manager"),
|
logger: logger.CustomLogger("Event Manager"),
|
||||||
signalchannel: make(chan os.Signal, 2),
|
signalchannel: make(chan os.Signal, 2),
|
||||||
ctx: ctx,
|
quitChannel: quitChannel,
|
||||||
cancel: cancel,
|
|
||||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -51,23 +49,20 @@ func (m *Manager) Start() {
|
|||||||
// Hook into interrupts
|
// Hook into interrupts
|
||||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
m.wg.Add(1)
|
// Spin off signal listener
|
||||||
|
|
||||||
// Spin off signal listener and wait for either a cancellation
|
|
||||||
// or signal
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
running := true
|
||||||
case <-m.signalchannel:
|
for running {
|
||||||
println()
|
select {
|
||||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
case <-m.signalchannel:
|
||||||
m.bus.Publish("quit", "ctrl-c pressed")
|
println()
|
||||||
|
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||||
// Start shutdown of Wails
|
m.bus.Publish("quit", "ctrl-c pressed")
|
||||||
m.cancel()
|
case <-m.quitChannel:
|
||||||
|
running = false
|
||||||
case <-m.ctx.Done():
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.logger.Trace("Shutdown")
|
m.logger.Trace("Shutdown")
|
||||||
m.wg.Done()
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package subsystem
|
|||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime/goruntime"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Binding is the Binding subsystem. It manages all service bus messages
|
// Binding is the Binding subsystem. It manages all service bus messages
|
||||||
// starting with "binding".
|
// starting with "binding".
|
||||||
type Binding struct {
|
type Binding struct {
|
||||||
|
quitChannel <-chan *servicebus.Message
|
||||||
bindingChannel <-chan *servicebus.Message
|
bindingChannel <-chan *servicebus.Message
|
||||||
|
running bool
|
||||||
running bool
|
|
||||||
|
|
||||||
// Binding db
|
// Binding db
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
@@ -21,11 +21,17 @@ type Binding struct {
|
|||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// runtime
|
// runtime
|
||||||
runtime *runtime.Runtime
|
runtime *goruntime.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
|
||||||
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *goruntime.Runtime) (*Binding, error) {
|
||||||
|
|
||||||
|
// Register quit channel
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
bindingChannel, err := bus.Subscribe("binding")
|
bindingChannel, err := bus.Subscribe("binding")
|
||||||
@@ -34,12 +40,21 @@ func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Binding{
|
result := &Binding{
|
||||||
|
quitChannel: quitChannel,
|
||||||
bindingChannel: bindingChannel,
|
bindingChannel: bindingChannel,
|
||||||
logger: logger.CustomLogger("Binding Subsystem"),
|
logger: logger.CustomLogger("Binding Subsystem"),
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
runtime: runtime,
|
runtime: runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call WailsInit methods once the frontend is loaded
|
||||||
|
// TODO: Double check that this is actually being emitted
|
||||||
|
// when we want it to be
|
||||||
|
runtime.Events.On("wails:loaded", func(...interface{}) {
|
||||||
|
result.logger.Trace("Calling WailsInit() methods")
|
||||||
|
result.CallWailsInit()
|
||||||
|
})
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,16 +69,45 @@ func (b *Binding) Start() error {
|
|||||||
go func() {
|
go func() {
|
||||||
for b.running {
|
for b.running {
|
||||||
select {
|
select {
|
||||||
|
case <-b.quitChannel:
|
||||||
|
b.running = false
|
||||||
case bindingMessage := <-b.bindingChannel:
|
case bindingMessage := <-b.bindingChannel:
|
||||||
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
b.logger.Trace("Got binding message: %+v", bindingMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.logger.Trace("Shutdown")
|
|
||||||
|
// Call shutdown
|
||||||
|
b.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binding) Close() {
|
// CallWailsInit will callback to the registered WailsInit
|
||||||
b.running = false
|
// methods with the runtime object
|
||||||
|
func (b *Binding) CallWailsInit() error {
|
||||||
|
for _, wailsinit := range b.bindings.DB().WailsInitMethods() {
|
||||||
|
_, err := wailsinit.Call([]interface{}{b.runtime})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallWailsShutdown will callback to the registered WailsShutdown
|
||||||
|
// methods with the runtime object
|
||||||
|
func (b *Binding) CallWailsShutdown() error {
|
||||||
|
for _, wailsshutdown := range b.bindings.DB().WailsShutdownMethods() {
|
||||||
|
_, err := wailsshutdown.Call([]interface{}{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Binding) shutdown() {
|
||||||
|
b.CallWailsShutdown()
|
||||||
|
b.logger.Trace("Shutdown")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,21 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call is the Call subsystem. It manages all service bus messages
|
// Call is the Call subsystem. It manages all service bus messages
|
||||||
// starting with "call".
|
// starting with "call".
|
||||||
type Call struct {
|
type Call struct {
|
||||||
|
quitChannel <-chan *servicebus.Message
|
||||||
callChannel <-chan *servicebus.Message
|
callChannel <-chan *servicebus.Message
|
||||||
|
running bool
|
||||||
// quit flag
|
|
||||||
shouldQuit bool
|
|
||||||
|
|
||||||
// bindings DB
|
// bindings DB
|
||||||
DB *binding.DB
|
DB *binding.DB
|
||||||
@@ -32,19 +25,16 @@ type Call struct {
|
|||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// runtime
|
|
||||||
runtime *runtime.Runtime
|
|
||||||
|
|
||||||
// context
|
|
||||||
ctx context.Context
|
|
||||||
|
|
||||||
// parent waitgroup
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCall creates a new call subsystem
|
// NewCall creates a new log subsystem
|
||||||
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB) (*Call, error) {
|
||||||
|
|
||||||
|
// Register quit channel
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
callChannel, err := bus.Subscribe("call:invoke")
|
callChannel, err := bus.Subscribe("call:invoke")
|
||||||
@@ -53,13 +43,11 @@ func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Log
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Call{
|
result := &Call{
|
||||||
|
quitChannel: quitChannel,
|
||||||
callChannel: callChannel,
|
callChannel: callChannel,
|
||||||
logger: logger.CustomLogger("Call Subsystem"),
|
logger: logger.CustomLogger("Call Subsystem"),
|
||||||
DB: DB,
|
DB: DB,
|
||||||
bus: bus,
|
bus: bus,
|
||||||
runtime: runtime,
|
|
||||||
ctx: ctx,
|
|
||||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -68,21 +56,22 @@ func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Log
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (c *Call) Start() error {
|
func (c *Call) Start() error {
|
||||||
|
|
||||||
c.wg.Add(1)
|
c.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer c.logger.Trace("Shutdown")
|
for c.running {
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case <-c.ctx.Done():
|
case <-c.quitChannel:
|
||||||
c.wg.Done()
|
c.running = false
|
||||||
return
|
|
||||||
case callMessage := <-c.callChannel:
|
case callMessage := <-c.callChannel:
|
||||||
|
|
||||||
c.processCall(callMessage)
|
c.processCall(callMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call shutdown
|
||||||
|
c.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -98,12 +87,6 @@ func (c *Call) processCall(callMessage *servicebus.Message) {
|
|||||||
// Lookup method
|
// Lookup method
|
||||||
registeredMethod := c.DB.GetMethod(payload.Name)
|
registeredMethod := c.DB.GetMethod(payload.Name)
|
||||||
|
|
||||||
// Check if it's a system call
|
|
||||||
if strings.HasPrefix(payload.Name, ".wails.") {
|
|
||||||
c.processSystemCall(payload, callMessage.Target())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we have it
|
// Check we have it
|
||||||
if registeredMethod == nil {
|
if registeredMethod == nil {
|
||||||
c.sendError(fmt.Errorf("Method not registered"), payload, callMessage.Target())
|
c.sendError(fmt.Errorf("Method not registered"), payload, callMessage.Target())
|
||||||
@@ -111,12 +94,7 @@ func (c *Call) processCall(callMessage *servicebus.Message) {
|
|||||||
}
|
}
|
||||||
c.logger.Trace("Got registered method: %+v", registeredMethod)
|
c.logger.Trace("Got registered method: %+v", registeredMethod)
|
||||||
|
|
||||||
args, err := registeredMethod.ParseArgs(payload.Args)
|
result, err := registeredMethod.Call(payload.Args)
|
||||||
if err != nil {
|
|
||||||
c.sendError(fmt.Errorf("Error parsing arguments: %s", err.Error()), payload, callMessage.Target())
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := registeredMethod.Call(args)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.sendError(err, payload, callMessage.Target())
|
c.sendError(err, payload, callMessage.Target())
|
||||||
return
|
return
|
||||||
@@ -127,50 +105,14 @@ func (c *Call) processCall(callMessage *servicebus.Message) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Call) processSystemCall(payload *message.CallMessage, clientID string) {
|
|
||||||
c.logger.Trace("Got internal System call: %+v", payload)
|
|
||||||
callName := strings.TrimPrefix(payload.Name, ".wails.")
|
|
||||||
switch callName {
|
|
||||||
case "IsDarkMode":
|
|
||||||
darkModeEnabled := c.runtime.System.IsDarkMode()
|
|
||||||
c.sendResult(darkModeEnabled, payload, clientID)
|
|
||||||
case "Dialog.Open":
|
|
||||||
dialogOptions := new(dialog.OpenDialog)
|
|
||||||
err := json.Unmarshal(payload.Args[0], dialogOptions)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("Error decoding: %s", err)
|
|
||||||
}
|
|
||||||
result := c.runtime.Dialog.Open(dialogOptions)
|
|
||||||
c.sendResult(result, payload, clientID)
|
|
||||||
case "Dialog.Save":
|
|
||||||
dialogOptions := new(dialog.SaveDialog)
|
|
||||||
err := json.Unmarshal(payload.Args[0], dialogOptions)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("Error decoding: %s", err)
|
|
||||||
}
|
|
||||||
result := c.runtime.Dialog.Save(dialogOptions)
|
|
||||||
c.sendResult(result, payload, clientID)
|
|
||||||
case "Dialog.Message":
|
|
||||||
dialogOptions := new(dialog.MessageDialog)
|
|
||||||
err := json.Unmarshal(payload.Args[0], dialogOptions)
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("Error decoding: %s", err)
|
|
||||||
}
|
|
||||||
result := c.runtime.Dialog.Message(dialogOptions)
|
|
||||||
c.sendResult(result, payload, clientID)
|
|
||||||
default:
|
|
||||||
c.logger.Error("Unknown system call: %+v", callName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Call) sendResult(result interface{}, payload *message.CallMessage, clientID string) {
|
func (c *Call) sendResult(result interface{}, payload *message.CallMessage, clientID string) {
|
||||||
c.logger.Trace("Sending success result with CallbackID '%s' : %+v\n", payload.CallbackID, result)
|
c.logger.Trace("Sending success result with CallbackID '%s' : %+v\n", payload.CallbackID, result)
|
||||||
incomingMessage := &CallbackMessage{
|
message := &CallbackMessage{
|
||||||
Result: result,
|
Result: result,
|
||||||
CallbackID: payload.CallbackID,
|
CallbackID: payload.CallbackID,
|
||||||
}
|
}
|
||||||
messageData, err := json.Marshal(incomingMessage)
|
messageData, err := json.Marshal(message)
|
||||||
c.logger.Trace("json incomingMessage data: %+v\n", string(messageData))
|
c.logger.Trace("json message data: %+v\n", string(messageData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// what now?
|
// what now?
|
||||||
c.logger.Fatal(err.Error())
|
c.logger.Fatal(err.Error())
|
||||||
@@ -180,13 +122,13 @@ func (c *Call) sendResult(result interface{}, payload *message.CallMessage, clie
|
|||||||
|
|
||||||
func (c *Call) sendError(err error, payload *message.CallMessage, clientID string) {
|
func (c *Call) sendError(err error, payload *message.CallMessage, clientID string) {
|
||||||
c.logger.Trace("Sending error result with CallbackID '%s' : %+v\n", payload.CallbackID, err.Error())
|
c.logger.Trace("Sending error result with CallbackID '%s' : %+v\n", payload.CallbackID, err.Error())
|
||||||
incomingMessage := &CallbackMessage{
|
message := &CallbackMessage{
|
||||||
Err: err.Error(),
|
Err: err.Error(),
|
||||||
CallbackID: payload.CallbackID,
|
CallbackID: payload.CallbackID,
|
||||||
}
|
}
|
||||||
|
|
||||||
messageData, err := json.Marshal(incomingMessage)
|
messageData, err := json.Marshal(message)
|
||||||
c.logger.Trace("json incomingMessage data: %+v\n", string(messageData))
|
c.logger.Trace("json message data: %+v\n", string(messageData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// what now?
|
// what now?
|
||||||
c.logger.Fatal(err.Error())
|
c.logger.Fatal(err.Error())
|
||||||
@@ -194,6 +136,10 @@ func (c *Call) sendError(err error, payload *message.CallMessage, clientID strin
|
|||||||
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
c.bus.PublishForTarget("call:result", string(messageData), clientID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Call) shutdown() {
|
||||||
|
c.logger.Trace("Shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
// CallbackMessage defines a message that contains the result of a call
|
// CallbackMessage defines a message that contains the result of a call
|
||||||
type CallbackMessage struct {
|
type CallbackMessage struct {
|
||||||
Result interface{} `json:"result"`
|
Result interface{} `json:"result"`
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -16,14 +15,16 @@ import (
|
|||||||
// means it does not expire (default).
|
// means it does not expire (default).
|
||||||
type eventListener struct {
|
type eventListener struct {
|
||||||
callback func(...interface{}) // Function to call with emitted event data
|
callback func(...interface{}) // Function to call with emitted event data
|
||||||
counter int // The number of times this callback may be called. -1 = infinite
|
counter int64 // The number of times this callback may be called. -1 = infinite
|
||||||
delete bool // Flag to indicate that this listener should be deleted
|
delete bool // Flag to indicate that this listener should be deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event is the Eventing subsystem. It manages all service bus messages
|
// Event is the Eventing subsystem. It manages all service bus messages
|
||||||
// starting with "event".
|
// starting with "event".
|
||||||
type Event struct {
|
type Event struct {
|
||||||
|
quitChannel <-chan *servicebus.Message
|
||||||
eventChannel <-chan *servicebus.Message
|
eventChannel <-chan *servicebus.Message
|
||||||
|
running bool
|
||||||
|
|
||||||
// Event listeners
|
// Event listeners
|
||||||
listeners map[string][]*eventListener
|
listeners map[string][]*eventListener
|
||||||
@@ -31,16 +32,16 @@ type Event struct {
|
|||||||
|
|
||||||
// logger
|
// logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// ctx
|
|
||||||
ctx context.Context
|
|
||||||
|
|
||||||
// parent waitgroup
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent creates a new log subsystem
|
// NewEvent creates a new log subsystem
|
||||||
func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error) {
|
||||||
|
|
||||||
|
// Register quit channel
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Subscribe to event messages
|
// Subscribe to event messages
|
||||||
eventChannel, err := bus.Subscribe("event")
|
eventChannel, err := bus.Subscribe("event")
|
||||||
@@ -49,23 +50,22 @@ func NewEvent(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Lo
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := &Event{
|
result := &Event{
|
||||||
|
quitChannel: quitChannel,
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
logger: logger.CustomLogger("Event Subsystem"),
|
logger: logger.CustomLogger("Event Subsystem"),
|
||||||
listeners: make(map[string][]*eventListener),
|
listeners: make(map[string][]*eventListener),
|
||||||
ctx: ctx,
|
|
||||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterListener provides a means of subscribing to events of type "eventName"
|
// RegisterListener provides a means of subscribing to events of type "eventName"
|
||||||
func (e *Event) RegisterListener(eventName string, callback func(...interface{}), counter int) {
|
func (e *Event) RegisterListener(eventName string, callback func(...interface{})) {
|
||||||
|
|
||||||
// Create new eventListener
|
// Create new eventListener
|
||||||
thisListener := &eventListener{
|
thisListener := &eventListener{
|
||||||
callback: callback,
|
callback: callback,
|
||||||
counter: counter,
|
counter: 0,
|
||||||
delete: false,
|
delete: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +80,15 @@ func (e *Event) Start() error {
|
|||||||
|
|
||||||
e.logger.Trace("Starting")
|
e.logger.Trace("Starting")
|
||||||
|
|
||||||
e.wg.Add(1)
|
e.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer e.logger.Trace("Shutdown")
|
for e.running {
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case <-e.ctx.Done():
|
case <-e.quitChannel:
|
||||||
e.wg.Done()
|
e.running = false
|
||||||
return
|
break
|
||||||
case eventMessage := <-e.eventChannel:
|
case eventMessage := <-e.eventChannel:
|
||||||
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
splitTopic := strings.Split(eventMessage.Topic(), ":")
|
||||||
eventType := splitTopic[1]
|
eventType := splitTopic[1]
|
||||||
@@ -121,7 +120,7 @@ func (e *Event) Start() error {
|
|||||||
var message *message.OnEventMessage = eventMessage.Data().(*message.OnEventMessage)
|
var message *message.OnEventMessage = eventMessage.Data().(*message.OnEventMessage)
|
||||||
eventName := message.Name
|
eventName := message.Name
|
||||||
callback := message.Callback
|
callback := message.Callback
|
||||||
e.RegisterListener(eventName, callback, message.Counter)
|
e.RegisterListener(eventName, callback)
|
||||||
e.logger.Trace("Registered listener for event '%s' with callback %p", eventName, callback)
|
e.logger.Trace("Registered listener for event '%s' with callback %p", eventName, callback)
|
||||||
default:
|
default:
|
||||||
e.logger.Error("unknown event message: %+v", eventMessage)
|
e.logger.Error("unknown event message: %+v", eventMessage)
|
||||||
@@ -129,6 +128,8 @@ func (e *Event) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call shutdown
|
||||||
|
e.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -140,7 +141,7 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
|||||||
// Get list of event listeners
|
// Get list of event listeners
|
||||||
listeners := e.listeners[eventName]
|
listeners := e.listeners[eventName]
|
||||||
if listeners == nil {
|
if listeners == nil {
|
||||||
e.logger.Trace("No listeners for event '%s'", eventName)
|
println("no listeners for", eventName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,14 +179,15 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save new listeners or remove entry
|
// Save new listeners
|
||||||
if len(newListeners) > 0 {
|
e.listeners[eventName] = newListeners
|
||||||
e.listeners[eventName] = newListeners
|
|
||||||
} else {
|
|
||||||
delete(e.listeners, eventName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock
|
// Unlock
|
||||||
e.notifyLock.Unlock()
|
e.notifyLock.Unlock()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Event) shutdown() {
|
||||||
|
e.logger.Trace("Shutdown")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,25 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Log is the Logging subsystem. It handles messages with topics starting
|
// Log is the Logging subsystem. It handles messages with topics starting
|
||||||
// with "log:"
|
// with "log:"
|
||||||
type Log struct {
|
type Log struct {
|
||||||
logChannel <-chan *servicebus.Message
|
logChannel <-chan *servicebus.Message
|
||||||
|
quitChannel <-chan *servicebus.Message
|
||||||
// quit flag
|
running bool
|
||||||
shouldQuit bool
|
|
||||||
|
|
||||||
// Logger!
|
// Logger!
|
||||||
logger *logger.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
|
// NewLog creates a new log subsystem
|
||||||
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *runtime.Store) (*Log, error) {
|
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) {
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Subscribe to log messages
|
||||||
logChannel, err := bus.Subscribe("log")
|
logChannel, err := bus.Subscribe("log")
|
||||||
@@ -42,14 +27,16 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
// Subscribe to quit messages
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
result := &Log{
|
result := &Log{
|
||||||
logChannel: logChannel,
|
logChannel: logChannel,
|
||||||
logger: logger,
|
quitChannel: quitChannel,
|
||||||
logLevelStore: logLevelStore,
|
logger: logger,
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -58,24 +45,18 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (l *Log) Start() error {
|
func (l *Log) Start() error {
|
||||||
|
|
||||||
l.wg.Add(1)
|
l.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer l.logger.Trace("Logger Shutdown")
|
for l.running {
|
||||||
|
|
||||||
for l.shouldQuit == false {
|
|
||||||
select {
|
select {
|
||||||
case <-l.ctx.Done():
|
case <-l.quitChannel:
|
||||||
l.wg.Done()
|
l.running = false
|
||||||
return
|
break
|
||||||
case logMessage := <-l.logChannel:
|
case logMessage := <-l.logChannel:
|
||||||
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
||||||
switch logType {
|
switch logType {
|
||||||
case "print":
|
|
||||||
l.logger.Print(logMessage.Data().(string))
|
|
||||||
case "trace":
|
|
||||||
l.logger.Trace(logMessage.Data().(string))
|
|
||||||
case "debug":
|
case "debug":
|
||||||
l.logger.Debug(logMessage.Data().(string))
|
l.logger.Debug(logMessage.Data().(string))
|
||||||
case "info":
|
case "info":
|
||||||
@@ -86,33 +67,13 @@ func (l *Log) Start() error {
|
|||||||
l.logger.Error(logMessage.Data().(string))
|
l.logger.Error(logMessage.Data().(string))
|
||||||
case "fatal":
|
case "fatal":
|
||||||
l.logger.Fatal(logMessage.Data().(string))
|
l.logger.Fatal(logMessage.Data().(string))
|
||||||
case "setlevel":
|
|
||||||
switch inLevel := logMessage.Data().(type) {
|
|
||||||
case logger.LogLevel:
|
|
||||||
l.logger.SetLogLevel(inLevel)
|
|
||||||
l.logLevelStore.Set(inLevel)
|
|
||||||
case string:
|
|
||||||
uint64level, err := strconv.ParseUint(inLevel, 10, 8)
|
|
||||||
if err != nil {
|
|
||||||
l.logger.Error("Error parsing log level: %+v", inLevel)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
level := logger.LogLevel(uint64level)
|
|
||||||
l.logLevelStore.Set(level)
|
|
||||||
l.logger.SetLogLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
l.logger.Error("unknown log message: %+v", logMessage)
|
l.logger.Error("unknown log message: %+v", logMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
l.logger.Trace("Logger Shutdown")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Log) Close() {
|
|
||||||
l.cancel()
|
|
||||||
l.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,67 +1,47 @@
|
|||||||
package subsystem
|
package subsystem
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
"github.com/wailsapp/wails/v2/internal/runtime/goruntime"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
// Runtime is the Runtime subsystem. It handles messages with topics starting
|
||||||
// with "runtime:"
|
// with "runtime:"
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
|
quitChannel <-chan *servicebus.Message
|
||||||
runtimeChannel <-chan *servicebus.Message
|
runtimeChannel <-chan *servicebus.Message
|
||||||
|
running bool
|
||||||
// The hooks channel allows us to hook into frontend startup
|
|
||||||
hooksChannel <-chan *servicebus.Message
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
|
|
||||||
// quit flag
|
|
||||||
shouldQuit bool
|
|
||||||
|
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
|
|
||||||
// Runtime library
|
// Runtime library
|
||||||
runtime *runtime.Runtime
|
runtime *goruntime.Runtime
|
||||||
|
|
||||||
//ctx
|
|
||||||
ctx context.Context
|
|
||||||
|
|
||||||
// Startup Hook
|
|
||||||
startupOnce sync.Once
|
|
||||||
|
|
||||||
// Service bus
|
|
||||||
bus *servicebus.ServiceBus
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime creates a new runtime subsystem
|
// NewRuntime creates a new runtime subsystem
|
||||||
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime)) (*Runtime, error) {
|
func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger) (*Runtime, error) {
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Register quit channel
|
||||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to log messages
|
// Subscribe to log messages
|
||||||
hooksChannel, err := bus.Subscribe("hooks:")
|
runtimeChannel, err := bus.Subscribe("runtime")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
runtimeChannel: runtimeChannel,
|
quitChannel: quitChannel,
|
||||||
hooksChannel: hooksChannel,
|
runtimeChannel: runtimeChannel,
|
||||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||||
runtime: runtime.New(bus),
|
runtime: goruntime.New(bus),
|
||||||
startupCallback: startupCallback,
|
|
||||||
ctx: ctx,
|
|
||||||
bus: bus,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -70,36 +50,15 @@ func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.
|
|||||||
// Start the subsystem
|
// Start the subsystem
|
||||||
func (r *Runtime) Start() error {
|
func (r *Runtime) Start() error {
|
||||||
|
|
||||||
|
r.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer r.logger.Trace("Shutdown")
|
for r.running {
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case hooksMessage := <-r.hooksChannel:
|
case <-r.quitChannel:
|
||||||
r.logger.Trace(fmt.Sprintf("Received hooksmessage: %+v", hooksMessage))
|
r.running = false
|
||||||
messageSlice := strings.Split(hooksMessage.Topic(), ":")
|
break
|
||||||
hook := messageSlice[1]
|
|
||||||
switch hook {
|
|
||||||
case "startup":
|
|
||||||
if r.startupCallback != nil {
|
|
||||||
r.startupOnce.Do(func() {
|
|
||||||
go func() {
|
|
||||||
r.startupCallback(r.runtime)
|
|
||||||
|
|
||||||
// If we got a url, publish it now startup completed
|
|
||||||
url, ok := hooksMessage.Data().(string)
|
|
||||||
if ok && len(url) > 0 {
|
|
||||||
r.bus.Publish("url:handler", url)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
r.logger.Warning("no startup callback registered!")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
r.logger.Error("unknown hook message: %+v", hooksMessage)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case runtimeMessage := <-r.runtimeChannel:
|
case runtimeMessage := <-r.runtimeChannel:
|
||||||
r.logger.Trace(fmt.Sprintf("Received message: %+v", runtimeMessage))
|
r.logger.Trace(fmt.Sprintf("Received message: %+v", runtimeMessage))
|
||||||
// Topics have the format: "runtime:category:call"
|
// Topics have the format: "runtime:category:call"
|
||||||
@@ -116,41 +75,40 @@ func (r *Runtime) Start() error {
|
|||||||
case "browser":
|
case "browser":
|
||||||
err = r.processBrowserMessage(method, runtimeMessage.Data())
|
err = r.processBrowserMessage(method, runtimeMessage.Data())
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown runtime message: %+v",
|
fmt.Errorf("unknown log message: %+v", runtimeMessage)
|
||||||
runtimeMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we had an error, log it
|
// If we had an error, log it
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error(err.Error())
|
r.logger.Error(err.Error())
|
||||||
}
|
}
|
||||||
case <-r.ctx.Done():
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call shutdown
|
||||||
|
r.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoRuntime returns the Go Runtime object
|
// GoRuntime returns the Go Runtime object
|
||||||
func (r *Runtime) GoRuntime() *runtime.Runtime {
|
func (r *Runtime) GoRuntime() *goruntime.Runtime {
|
||||||
return r.runtime
|
return r.runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) shutdown() {
|
||||||
|
r.logger.Trace("Shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
||||||
switch method {
|
switch method {
|
||||||
case "open":
|
case "openurl":
|
||||||
target, ok := data.(string)
|
url, ok := data.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("expected 1 string parameter for runtime:browser:open")
|
return fmt.Errorf("expected 1 string parameter for runtime:browser:openurl")
|
||||||
}
|
}
|
||||||
go func() {
|
go r.runtime.Browser.Open(url)
|
||||||
err := r.runtime.Browser.Open(target)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error(err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown method runtime:browser:%s", method)
|
return fmt.Errorf("unknown method runtime:browser:%s", method)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,24 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PackageManager is a common interface across all package managers
|
||||||
|
type PackageManager interface {
|
||||||
|
Name() string
|
||||||
|
Packages() packagemap
|
||||||
|
PackageInstalled(*Package) (bool, error)
|
||||||
|
PackageAvailable(*Package) (bool, error)
|
||||||
|
InstallCommand(*Package) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package contains information about a system package
|
||||||
|
type Package struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
InstallCommand map[string]string
|
||||||
|
SystemPackage bool
|
||||||
|
Library bool
|
||||||
|
Optional bool
|
||||||
|
}
|
||||||
|
|
||||||
// A list of package manager commands
|
// A list of package manager commands
|
||||||
var pmcommands = []string{
|
var pmcommands = []string{
|
||||||
@@ -20,6 +38,8 @@ var pmcommands = []string{
|
|||||||
"zypper",
|
"zypper",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type packagemap = map[string][]*Package
|
||||||
|
|
||||||
// Find will attempt to find the system package manager
|
// Find will attempt to find the system package manager
|
||||||
func Find(osid string) PackageManager {
|
func Find(osid string) PackageManager {
|
||||||
|
|
||||||
@@ -50,6 +70,58 @@ func newPackageManager(pmname string, osid string) PackageManager {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dependancy represents a system package that we require
|
||||||
|
type Dependancy struct {
|
||||||
|
Name string
|
||||||
|
PackageName string
|
||||||
|
Installed bool
|
||||||
|
InstallCommand string
|
||||||
|
Version string
|
||||||
|
Optional bool
|
||||||
|
External bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DependencyList is a list of Dependency instances
|
||||||
|
type DependencyList []*Dependancy
|
||||||
|
|
||||||
|
// InstallAllRequiredCommand returns the command you need to use to install all required dependencies
|
||||||
|
func (d DependencyList) InstallAllRequiredCommand() string {
|
||||||
|
|
||||||
|
result := ""
|
||||||
|
for _, dependency := range d {
|
||||||
|
if dependency.PackageName != "" {
|
||||||
|
if !dependency.Installed && !dependency.Optional {
|
||||||
|
if result == "" {
|
||||||
|
result = dependency.InstallCommand
|
||||||
|
} else {
|
||||||
|
result += " " + dependency.PackageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstallAllOptionalCommand returns the command you need to use to install all optional dependencies
|
||||||
|
func (d DependencyList) InstallAllOptionalCommand() string {
|
||||||
|
|
||||||
|
result := ""
|
||||||
|
for _, dependency := range d {
|
||||||
|
if dependency.PackageName != "" {
|
||||||
|
if !dependency.Installed && dependency.Optional {
|
||||||
|
if result == "" {
|
||||||
|
result = dependency.InstallCommand
|
||||||
|
} else {
|
||||||
|
result += " " + dependency.PackageName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Dependancies scans the system for required dependancies
|
// Dependancies scans the system for required dependancies
|
||||||
// Returns a list of dependancies search for, whether they were found
|
// Returns a list of dependancies search for, whether they were found
|
||||||
// and whether they were installed
|
// and whether they were installed
|
||||||
|
|||||||
@@ -2,20 +2,14 @@
|
|||||||
|
|
||||||
package system
|
package system
|
||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
func (i *Info) discover() error {
|
func (i *Info) discover() {
|
||||||
|
dll := syscall.MustLoadDLL("kernel32.dll")
|
||||||
var err error
|
p := dll.MustFindProc("GetVersion")
|
||||||
osinfo, err := operatingsystem.Info()
|
v, _, _ := p.Call()
|
||||||
if err != nil {
|
fmt.Printf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16))
|
||||||
return err
|
|
||||||
}
|
|
||||||
i.OS = osinfo
|
|
||||||
|
|
||||||
// dll := syscall.MustLoadDLL("kernel32.dll")
|
|
||||||
// p := dll.MustFindProc("GetVersion")
|
|
||||||
// v, _, _ := p.Call()
|
|
||||||
// fmt.Printf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16))
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/leaanthony/gosod"
|
"github.com/leaanthony/gosod"
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cahce for the templates
|
// Cahce for the templates
|
||||||
@@ -22,29 +21,21 @@ var templateCache []Template = nil
|
|||||||
|
|
||||||
// Data contains the data we wish to embed during template installation
|
// Data contains the data we wish to embed during template installation
|
||||||
type Data struct {
|
type Data struct {
|
||||||
ProjectName string
|
ProjectName string
|
||||||
BinaryName string
|
BinaryName string
|
||||||
WailsVersion string
|
WailsVersion string
|
||||||
NPMProjectName string
|
NPMProjectName string
|
||||||
AuthorName string
|
Author string
|
||||||
AuthorEmail string
|
WailsDirectory string
|
||||||
AuthorNameAndEmail string
|
|
||||||
WailsDirectory string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options for installing a template
|
// Options for installing a template
|
||||||
type Options struct {
|
type Options struct {
|
||||||
ProjectName string
|
ProjectName string
|
||||||
TemplateName string
|
TemplateName string
|
||||||
BinaryName string
|
BinaryName string
|
||||||
TargetDir string
|
TargetDir string
|
||||||
Logger *clilogger.CLILogger
|
Logger *logger.Logger
|
||||||
GenerateVSCode bool
|
|
||||||
PathToDesktopBinary string
|
|
||||||
PathToServerBinary string
|
|
||||||
InitGit bool
|
|
||||||
AuthorName string
|
|
||||||
AuthorEmail string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template holds data relating to a template
|
// Template holds data relating to a template
|
||||||
@@ -171,24 +162,9 @@ func Install(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Did the user want to install in current directory?
|
// Did the user want to install in current directory?
|
||||||
if options.TargetDir == "" {
|
if options.TargetDir == "." {
|
||||||
|
// Yes - use cwd
|
||||||
// If the current directory is empty, use it
|
options.TargetDir = cwd
|
||||||
isEmpty, err := fs.DirIsEmpty(cwd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isEmpty {
|
|
||||||
// Yes - use cwd
|
|
||||||
options.TargetDir = cwd
|
|
||||||
} else {
|
|
||||||
options.TargetDir = filepath.Join(cwd, options.ProjectName)
|
|
||||||
if fs.DirExists(options.TargetDir) {
|
|
||||||
return fmt.Errorf("cannot create project directory. Dir exists: %s", options.TargetDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Get the absolute path of the given directory
|
// Get the absolute path of the given directory
|
||||||
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
||||||
@@ -229,93 +205,43 @@ func Install(options *Options) error {
|
|||||||
BinaryName: filepath.Base(options.TargetDir),
|
BinaryName: filepath.Base(options.TargetDir),
|
||||||
NPMProjectName: NPMProjectName,
|
NPMProjectName: NPMProjectName,
|
||||||
WailsDirectory: localWailsDirectory,
|
WailsDirectory: localWailsDirectory,
|
||||||
AuthorEmail: options.AuthorEmail,
|
|
||||||
AuthorName: options.AuthorName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a formatted name and email combo.
|
|
||||||
if options.AuthorName != "" {
|
|
||||||
templateData.AuthorNameAndEmail = options.AuthorName + " "
|
|
||||||
}
|
|
||||||
if options.AuthorEmail != "" {
|
|
||||||
templateData.AuthorNameAndEmail += "<" + options.AuthorEmail + ">"
|
|
||||||
}
|
|
||||||
templateData.AuthorNameAndEmail = strings.TrimSpace(templateData.AuthorNameAndEmail)
|
|
||||||
|
|
||||||
// Extract the template
|
// Extract the template
|
||||||
err = installer.Extract(options.TargetDir, templateData)
|
err = installer.Extract(options.TargetDir, templateData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = generateIDEFiles(options)
|
// Calculate the directory name
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputList prints the list of available tempaltes to the given logger
|
// OutputList prints the list of available tempaltes to the given logger
|
||||||
func OutputList(logger *clilogger.CLILogger) error {
|
func OutputList(logger *logger.Logger) error {
|
||||||
templates, err := List()
|
templates, err := List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
table := tablewriter.NewWriter(logger.Writer)
|
for _, writer := range logger.Writers() {
|
||||||
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
table := tablewriter.NewWriter(writer)
|
||||||
table.SetAutoWrapText(false)
|
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
||||||
table.SetAutoFormatHeaders(true)
|
table.SetAutoWrapText(false)
|
||||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
table.SetAutoFormatHeaders(true)
|
||||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||||
table.SetCenterSeparator("")
|
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||||
table.SetColumnSeparator("")
|
table.SetCenterSeparator("")
|
||||||
table.SetRowSeparator("")
|
table.SetColumnSeparator("")
|
||||||
table.SetHeaderLine(false)
|
table.SetRowSeparator("")
|
||||||
table.SetBorder(false)
|
table.SetHeaderLine(false)
|
||||||
table.SetTablePadding("\t") // pad with tabs
|
table.SetBorder(false)
|
||||||
table.SetNoWhiteSpace(true)
|
table.SetTablePadding("\t") // pad with tabs
|
||||||
for _, template := range templates {
|
table.SetNoWhiteSpace(true)
|
||||||
table.Append([]string{template.Name, template.ShortName, template.Description})
|
for _, template := range templates {
|
||||||
|
table.Append([]string{template.Name, template.ShortName, template.Description})
|
||||||
|
}
|
||||||
|
table.Render()
|
||||||
}
|
}
|
||||||
table.Render()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateIDEFiles(options *Options) error {
|
|
||||||
|
|
||||||
if options.GenerateVSCode {
|
|
||||||
return generateVSCodeFiles(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateVSCodeFiles(options *Options) error {
|
|
||||||
|
|
||||||
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
|
||||||
sourceDir := fs.RelativePath(filepath.Join("./ides/vscode"))
|
|
||||||
|
|
||||||
// Use Gosod to install the template
|
|
||||||
installer, err := gosod.TemplateDir(sourceDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
binaryName := filepath.Base(options.TargetDir)
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// yay windows
|
|
||||||
binaryName += ".exe"
|
|
||||||
}
|
|
||||||
|
|
||||||
options.PathToDesktopBinary = filepath.Join("build", runtime.GOOS, "desktop", binaryName)
|
|
||||||
options.PathToServerBinary = filepath.Join("build", runtime.GOOS, "server", binaryName)
|
|
||||||
|
|
||||||
err = installer.Extract(targetDir, options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic application struct
|
// Basic application struct
|
||||||
|
|||||||
@@ -2,21 +2,21 @@
|
|||||||
"name": "svelte-app",
|
"name": "svelte-app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npx rollup -c",
|
"build": "rollup -c",
|
||||||
"dev": "npx rollup -c -w",
|
"dev": "rollup -c -w",
|
||||||
"start": "npx sirv public",
|
"start": "sirv public",
|
||||||
"start:dev": "npx sirv public --single --host 0.0.0.0 --dev"
|
"start:dev": "sirv public --single --host 0.0.0.0 --dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^11.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
"@rollup/plugin-node-resolve": "^7.0.0",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.0.2",
|
||||||
"rollup": "^2.35.1",
|
"rollup": "^1.20.0",
|
||||||
"rollup-plugin-livereload": "^2.0.0",
|
"rollup-plugin-livereload": "^1.0.0",
|
||||||
"rollup-plugin-svelte": "^7.0.0",
|
"rollup-plugin-svelte": "^5.0.3",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^5.1.2",
|
||||||
"svelte": "^3.31.1",
|
"svelte": "^3.0.0",
|
||||||
"svelte-mui": "^0.3.3-5"
|
"svelte-mui": "^0.3.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sirv-cli": "^0.4.4"
|
"sirv-cli": "^0.4.4"
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
app, err := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Bind(newBasic())
|
app.Bind(newBasic())
|
||||||
|
|
||||||
err = app.Run()
|
app.Run()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic application struct
|
// Basic application struct
|
||||||
@@ -12,19 +12,20 @@ type Basic struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newBasic creates a new Basic application struct
|
// newBasic creates a new Basic application struct
|
||||||
func NewBasic() *Basic {
|
func newBasic() *Basic {
|
||||||
return &Basic{}
|
return &Basic{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// startup is called at application startup
|
// WailsInit is called at application startup
|
||||||
func (b *Basic) startup(runtime *wails.Runtime) {
|
func (b *Basic) WailsInit(runtime *wails.Runtime) error {
|
||||||
// Perform your setup here
|
// Perform your setup here
|
||||||
b.runtime = runtime
|
b.runtime = runtime
|
||||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdown is called at application termination
|
// WailsShutdown is called at application termination
|
||||||
func (b *Basic) shutdown() {
|
func (b *Basic) WailsShutdown() {
|
||||||
// Perform your teardown here
|
// Perform your teardown here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<link rel="stylesheet" href="/main.css">
|
<link rel="stylesheet" href="/main.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-wails-drag>
|
<body>
|
||||||
<div id="logo"></div>
|
<div id="logo"></div>
|
||||||
<div id="input">
|
<div id="input">
|
||||||
<input id="name" type="text"></input>
|
<input id="name" type="text"></input>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,39 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
app := NewBasic()
|
app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
||||||
|
|
||||||
err := wails.Run(&options.App{
|
app.Bind(newBasic())
|
||||||
Title: "{{.ProjectName}}",
|
|
||||||
Width: 800,
|
app.Run()
|
||||||
Height: 600,
|
|
||||||
DisableResize: true,
|
|
||||||
Mac: &mac.Options{
|
|
||||||
WebviewIsTransparent: true,
|
|
||||||
WindowBackgroundIsTranslucent: true,
|
|
||||||
TitleBar: mac.TitleBarHiddenInset(),
|
|
||||||
Menu: menu.DefaultMacMenu(),
|
|
||||||
},
|
|
||||||
LogLevel: logger.DEBUG,
|
|
||||||
Startup: app.startup,
|
|
||||||
Shutdown: app.shutdown,
|
|
||||||
Bind: []interface{}{
|
|
||||||
app,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "{{.ProjectName}}",
|
"name": "{{.ProjectName}}",
|
||||||
"outputfilename": "{{.BinaryName}}",
|
"outputfilename": "{{.BinaryName}}",
|
||||||
"html": "frontend/index.html",
|
"html": "frontend/index.html"
|
||||||
"author": {
|
|
||||||
"name": "{{.AuthorName}}",
|
|
||||||
"email": "{{.AuthorEmail}}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Basic application struct
|
// Basic application struct
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "vuetify2",
|
||||||
|
"author": "Travis McLane<tmclane@gmail.com>",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^3.6.1",
|
||||||
|
"regenerator-runtime": "^0.13.3",
|
||||||
|
"vue": "^2.5.22",
|
||||||
|
"vuetify": "^2.2.17"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@mdi/font": "^4.3.95",
|
||||||
|
"@vue/cli-plugin-babel": "^3.4.0",
|
||||||
|
"@vue/cli-plugin-eslint": "^3.4.0",
|
||||||
|
"@vue/cli-service": "^3.4.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
|
"eslint": "^5.8.0",
|
||||||
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
|
"eventsource-polyfill": "^0.9.6",
|
||||||
|
"vue-template-compiler": "^2.5.21",
|
||||||
|
"webpack-hot-middleware": "^2.24.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"rules": {},
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "babel-eslint"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postcss": {
|
||||||
|
"plugins": {
|
||||||
|
"autoprefixer": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 8"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2"
|
wails "github.com/wailsapp/wails/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -5,9 +5,5 @@
|
|||||||
"js": "frontend/dist/app.js",
|
"js": "frontend/dist/app.js",
|
||||||
"css": "frontend/dist/app.css",
|
"css": "frontend/dist/app.css",
|
||||||
"frontend:build": "npm run build",
|
"frontend:build": "npm run build",
|
||||||
"frontend:install": "npm install",
|
"frontend:install": "npm install"
|
||||||
"author": {
|
|
||||||
"name": "{{.AuthorName}}",
|
|
||||||
"email": "{{.AuthorEmail}}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package webserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -20,94 +18,6 @@ type WebClient struct {
|
|||||||
running bool
|
running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wc *WebClient) SetTrayMenu(trayMenuJSON string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) SetApplicationMenu(menuJSON string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateTrayMenu(trayMenuJSON string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateContextMenu(contextMenuJSON string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowShow() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowHide() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowCenter() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowMaximise() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowUnmaximise() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowMinimise() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowUnminimise() {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowPosition(x int, y int) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) WindowSize(width int, height int) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) DarkModeEnabled(callbackID string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateMenu(menu *menu.Menu) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateTray(menu *menu.Menu) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateTrayLabel(label string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wc *WebClient) UpdateTrayIcon(name string) {
|
|
||||||
wc.logger.Info("Not implemented in server build")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quit terminates the webclient session
|
// Quit terminates the webclient session
|
||||||
func (wc *WebClient) Quit() {
|
func (wc *WebClient) Quit() {
|
||||||
wc.running = false
|
wc.running = false
|
||||||
@@ -124,6 +34,21 @@ func (wc *WebClient) CallResult(message string) {
|
|||||||
wc.SendMessage("R" + message)
|
wc.SendMessage("R" + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveFileDialog is a noop in the webclient
|
||||||
|
func (wc *WebClient) SaveFileDialog(title string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFileDialog is a noop in the webclient
|
||||||
|
func (wc *WebClient) OpenFileDialog(title string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenDirectoryDialog is a noop in the webclient
|
||||||
|
func (wc *WebClient) OpenDirectoryDialog(title string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// WindowSetTitle is a noop in the webclient
|
// WindowSetTitle is a noop in the webclient
|
||||||
func (wc *WebClient) WindowSetTitle(title string) {}
|
func (wc *WebClient) WindowSetTitle(title string) {}
|
||||||
|
|
||||||
@@ -134,7 +59,8 @@ func (wc *WebClient) WindowFullscreen() {}
|
|||||||
func (wc *WebClient) WindowUnFullscreen() {}
|
func (wc *WebClient) WindowUnFullscreen() {}
|
||||||
|
|
||||||
// WindowSetColour is a noop in the webclient
|
// WindowSetColour is a noop in the webclient
|
||||||
func (wc *WebClient) WindowSetColour(colour int) {
|
func (wc *WebClient) WindowSetColour(colour string) bool {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run processes messages from the remote webclient
|
// Run processes messages from the remote webclient
|
||||||
@@ -165,11 +91,7 @@ func (wc *WebClient) Run(w *WebServer) {
|
|||||||
dispatcher.DispatchMessage(v.(string))
|
dispatcher.DispatchMessage(v.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := wc.conn.Close(ws.StatusNormalClosure, "Goodbye")
|
wc.conn.Close(ws.StatusNormalClosure, "Goodbye")
|
||||||
if err != nil {
|
|
||||||
w.logger.Error("Error encountered on socket: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.logger.Debug("Connection closed: %v", wc.identifier)
|
w.logger.Debug("Connection closed: %v", wc.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,10 +102,7 @@ func (wc *WebClient) SendMessage(message string) {
|
|||||||
wc.logger.Debug("WebClient.SendMessage() - %s", message)
|
wc.logger.Debug("WebClient.SendMessage() - %s", message)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
err := wc.conn.Write(ctx, ws.MessageText, []byte(message))
|
wc.conn.Write(ctx, ws.MessageText, []byte(message))
|
||||||
if err != nil {
|
|
||||||
wc.logger.Error("Error encountered writing to webclient: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unregisterClient is called automatically by a WebClient session during termination
|
// unregisterClient is called automatically by a WebClient session during termination
|
||||||
|
|||||||
@@ -14,23 +14,20 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/internal/html"
|
"github.com/wailsapp/wails/v2/internal/html"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/project"
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseBuilder is the common builder struct
|
// BaseBuilder is the common builder struct
|
||||||
type BaseBuilder struct {
|
type BaseBuilder struct {
|
||||||
filesToDelete slicer.StringSlicer
|
filesToDelete slicer.StringSlicer
|
||||||
projectData *project.Project
|
projectData *project.Project
|
||||||
options *Options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBaseBuilder creates a new BaseBuilder
|
// NewBaseBuilder creates a new BaseBuilder
|
||||||
func NewBaseBuilder(options *Options) *BaseBuilder {
|
func NewBaseBuilder() *BaseBuilder {
|
||||||
result := &BaseBuilder{
|
result := &BaseBuilder{}
|
||||||
options: options,
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,9 +37,7 @@ func (b *BaseBuilder) SetProjectData(projectData *project.Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseBuilder) addFileToDelete(filename string) {
|
func (b *BaseBuilder) addFileToDelete(filename string) {
|
||||||
if !b.options.KeepAssets {
|
b.filesToDelete.Add(filename)
|
||||||
b.filesToDelete.Add(filename)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseBuilder) fileExists(path string) bool {
|
func (b *BaseBuilder) fileExists(path string) bool {
|
||||||
@@ -54,43 +49,39 @@ func (b *BaseBuilder) fileExists(path string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildCustomAssets will iterate through the projects static directory and add all files
|
// buildStaticAssets will iterate through the projects static directory and add all files
|
||||||
// to the application wide asset database.
|
// to the application wide asset database.
|
||||||
func (b *BaseBuilder) buildCustomAssets(projectData *project.Project) error {
|
func (b *BaseBuilder) buildStaticAssets(projectData *project.Project) error {
|
||||||
|
|
||||||
// Add trailing slash to Asset directory
|
// Add trailing slash to Asset directory
|
||||||
customAssetsDir := filepath.Join(projectData.Path, "assets", "custom") + "/"
|
assetsDir := filepath.Join(projectData.Path, "assets") + "/"
|
||||||
if !b.fileExists(customAssetsDir) {
|
|
||||||
err := fs.MkDirs(customAssetsDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assets := assetdb.NewAssetDB()
|
assets := assetdb.NewAssetDB()
|
||||||
err := filepath.Walk(customAssetsDir, func(path string, info os.FileInfo, err error) error {
|
if b.fileExists(assetsDir) {
|
||||||
|
err := filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
normalisedPath := filepath.ToSlash(path)
|
||||||
|
localPath := strings.TrimPrefix(normalisedPath, assetsDir)
|
||||||
|
if len(localPath) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if data, err := ioutil.ReadFile(filepath.Join(assetsDir, localPath)); err == nil {
|
||||||
|
assets.AddAsset(localPath, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
normalisedPath := filepath.ToSlash(path)
|
|
||||||
localPath := strings.TrimPrefix(normalisedPath, customAssetsDir)
|
|
||||||
if len(localPath) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if data, err := ioutil.ReadFile(filepath.Join(customAssetsDir, localPath)); err == nil {
|
|
||||||
assets.AddAsset(localPath, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write assetdb out to root directory
|
// Write assetdb out to root directory
|
||||||
assetsDbFilename := fs.RelativePath("../../../assetsdb.go")
|
assetsDbFilename := fs.RelativePath("../../../assetsdb.go")
|
||||||
b.addFileToDelete(assetsDbFilename)
|
b.addFileToDelete(assetsDbFilename)
|
||||||
err = ioutil.WriteFile(assetsDbFilename, []byte(assets.Serialize("assets", "wails")), 0644)
|
err := ioutil.WriteFile(assetsDbFilename, []byte(assets.Serialize("assets", "wails")), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -137,7 +128,7 @@ func (b *BaseBuilder) CleanUp() {
|
|||||||
|
|
||||||
// Delete file. We ignore errors because these files will be overwritten
|
// Delete file. We ignore errors because these files will be overwritten
|
||||||
// by the next build anyway.
|
// by the next build anyway.
|
||||||
_ = os.Remove(filename)
|
os.Remove(filename)
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -145,39 +136,12 @@ func (b *BaseBuilder) CleanUp() {
|
|||||||
// CompileProject compiles the project
|
// CompileProject compiles the project
|
||||||
func (b *BaseBuilder) CompileProject(options *Options) error {
|
func (b *BaseBuilder) CompileProject(options *Options) error {
|
||||||
|
|
||||||
// Run go mod tidy first
|
|
||||||
cmd := exec.Command(options.Compiler, "mod", "tidy")
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default go build command
|
// Default go build command
|
||||||
commands := slicer.String([]string{"build"})
|
commands := slicer.String([]string{"build"})
|
||||||
|
|
||||||
// Add better debugging flags
|
|
||||||
if options.Mode == Debug {
|
|
||||||
commands.Add("-gcflags")
|
|
||||||
commands.Add(`"all=-N -l"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out if we can make this more efficient
|
|
||||||
// We need to do a full build as CGO doesn't detect updates
|
|
||||||
// to .h files, and we package assets into .h file. We could
|
|
||||||
// potentially try and see if the assets have changed but will
|
|
||||||
// this take as much time as a `-a` build?
|
|
||||||
commands.Add("-a")
|
|
||||||
|
|
||||||
var tags slicer.StringSlicer
|
|
||||||
tags.Add(options.OutputType)
|
|
||||||
|
|
||||||
if options.Mode == Debug {
|
|
||||||
tags.Add("debug")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the output type build tag
|
// Add the output type build tag
|
||||||
commands.Add("-tags")
|
commands.Add("-tags")
|
||||||
commands.Add(tags.Join(","))
|
commands.Add(options.OutputType)
|
||||||
|
|
||||||
// Strip binary in Production mode
|
// Strip binary in Production mode
|
||||||
if options.Mode == Production {
|
if options.Mode == Production {
|
||||||
@@ -194,8 +158,7 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get application build directory
|
// Get application build directory
|
||||||
appDir := options.BuildDirectory
|
appDir, err := getApplicationBuildDirectory(options, options.Platform)
|
||||||
err = cleanBuildDirectory(options)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -206,40 +169,20 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up output filename
|
// Set up output filename
|
||||||
outputFile := options.OutputFile
|
outputFilePath := filepath.Join(appDir, b.projectData.OutputFilename)
|
||||||
if outputFile == "" {
|
|
||||||
outputFile = b.projectData.OutputFilename
|
|
||||||
}
|
|
||||||
compiledBinary := filepath.Join(appDir, outputFile)
|
|
||||||
commands.Add("-o")
|
commands.Add("-o")
|
||||||
commands.Add(compiledBinary)
|
commands.Add(outputFilePath)
|
||||||
|
|
||||||
b.projectData.OutputFilename = strings.TrimPrefix(compiledBinary, options.ProjectData.Path)
|
b.projectData.OutputFilename = strings.TrimPrefix(outputFilePath, options.ProjectData.Path)
|
||||||
options.CompiledBinary = compiledBinary
|
|
||||||
|
|
||||||
// Create the command
|
// Create the command
|
||||||
cmd = exec.Command(options.Compiler, commands.AsSlice()...)
|
cmd := exec.Command(options.Compiler, commands.AsSlice()...)
|
||||||
|
|
||||||
// Set the directory
|
// Set the directory
|
||||||
cmd.Dir = b.projectData.Path
|
cmd.Dir = b.projectData.Path
|
||||||
|
|
||||||
// Add CGO flags
|
// Set GO111MODULE environment variable
|
||||||
// We use the project/build dir as a temporary place for our generated c headers
|
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
||||||
buildBaseDir, err := fs.RelativeToCwd("build")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.Env = os.Environ() // inherit env
|
|
||||||
|
|
||||||
// Use upsertEnv so we don't overwrite user's CGO_CFLAGS
|
|
||||||
cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
|
|
||||||
if v != "" {
|
|
||||||
v += " "
|
|
||||||
}
|
|
||||||
v += "-I" + buildBaseDir
|
|
||||||
return v
|
|
||||||
})
|
|
||||||
|
|
||||||
// Setup buffers
|
// Setup buffers
|
||||||
var stdo, stde bytes.Buffer
|
var stdo, stde bytes.Buffer
|
||||||
@@ -351,9 +294,7 @@ func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verb
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildFrontend executes the `npm build` command for the frontend directory
|
// BuildFrontend executes the `npm build` command for the frontend directory
|
||||||
func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
|
||||||
|
|
||||||
// TODO: Fix this up from the CLI
|
|
||||||
verbose := false
|
verbose := false
|
||||||
|
|
||||||
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
||||||
@@ -361,10 +302,10 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
|||||||
// Check there is an 'InstallCommand' provided in wails.json
|
// Check there is an 'InstallCommand' provided in wails.json
|
||||||
if b.projectData.InstallCommand == "" {
|
if b.projectData.InstallCommand == "" {
|
||||||
// No - don't install
|
// No - don't install
|
||||||
outputLogger.Println(" - No Install command. Skipping.")
|
outputLogger.Writeln(" - No Install command. Skipping.")
|
||||||
} else {
|
} else {
|
||||||
// Do install if needed
|
// Do install if needed
|
||||||
outputLogger.Println(" - Installing dependencies...")
|
outputLogger.Writeln(" - Installing dependencies...")
|
||||||
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
|
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -372,12 +313,12 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
|||||||
|
|
||||||
// Check if there is a build command
|
// Check if there is a build command
|
||||||
if b.projectData.BuildCommand == "" {
|
if b.projectData.BuildCommand == "" {
|
||||||
outputLogger.Println(" - No Build command. Skipping.")
|
outputLogger.Writeln(" - No Build command. Skipping.")
|
||||||
// No - ignore
|
// No - ignore
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
outputLogger.Println(" - Compiling Frontend Project")
|
outputLogger.Writeln(" - Compiling Frontend Project")
|
||||||
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
||||||
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
||||||
if verbose || err != nil {
|
if verbose || err != nil {
|
||||||
@@ -397,22 +338,3 @@ func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) {
|
|||||||
// Read in html
|
// Read in html
|
||||||
return html.NewAssetBundle(b.projectData.HTML)
|
return html.NewAssetBundle(b.projectData.HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
func upsertEnv(env []string, key string, update func(v string) string) []string {
|
|
||||||
newEnv := make([]string, len(env), len(env)+1)
|
|
||||||
found := false
|
|
||||||
for i := range env {
|
|
||||||
if strings.HasPrefix(env[i], key+"=") {
|
|
||||||
eqIndex := strings.Index(env[i], "=")
|
|
||||||
val := env[i][eqIndex+1:]
|
|
||||||
newEnv[i] = fmt.Sprintf("%s=%v", key, update(val))
|
|
||||||
found = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newEnv[i] = env[i]
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update("")))
|
|
||||||
}
|
|
||||||
return newEnv
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,12 +3,11 @@ package build
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/project"
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mode is the type used to indicate the build modes
|
// Mode is the type used to indicate the build modes
|
||||||
@@ -25,20 +24,14 @@ var modeMap = []string{"Debug", "Production"}
|
|||||||
|
|
||||||
// Options contains all the build options as well as the project data
|
// Options contains all the build options as well as the project data
|
||||||
type Options struct {
|
type Options struct {
|
||||||
LDFlags string // Optional flags to pass to linker
|
LDFlags string // Optional flags to pass to linker
|
||||||
Logger *clilogger.CLILogger // All output to the logger
|
Logger *logger.Logger // All output to the logger
|
||||||
OutputType string // EG: desktop, server....
|
OutputType string // EG: desktop, server....
|
||||||
Mode Mode // release or debug
|
Mode Mode // release or debug
|
||||||
ProjectData *project.Project // The project data
|
ProjectData *project.Project // The project data
|
||||||
Pack bool // Create a package for the app after building
|
Pack bool // Create a package for the app after building
|
||||||
Platform string // The platform to build for
|
Platform string // The platform to build for
|
||||||
Compiler string // The compiler command to use
|
Compiler string // The compiler command to use
|
||||||
IgnoreFrontend bool // Indicates if the frontend does not need building
|
|
||||||
OutputFile string // Override the output filename
|
|
||||||
BuildDirectory string // Directory to use for building the application
|
|
||||||
CompiledBinary string // Fully qualified path to the compiled binary
|
|
||||||
KeepAssets bool // /Keep the generated assets/files
|
|
||||||
AppleIdentity string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetModeAsString returns the current mode as a string
|
// GetModeAsString returns the current mode as a string
|
||||||
@@ -52,6 +45,11 @@ func Build(options *Options) (string, error) {
|
|||||||
// Extract logger
|
// Extract logger
|
||||||
outputLogger := options.Logger
|
outputLogger := options.Logger
|
||||||
|
|
||||||
|
// Create a default logger if it doesn't exist
|
||||||
|
if outputLogger == nil {
|
||||||
|
outputLogger = logger.New()
|
||||||
|
}
|
||||||
|
|
||||||
// Get working directory
|
// Get working directory
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,7 +57,7 @@ func Build(options *Options) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check platform
|
// Check platform
|
||||||
validPlatforms := slicer.String([]string{"linux", "darwin"})
|
validPlatforms := slicer.String([]string{"linux"})
|
||||||
if !validPlatforms.Contains(options.Platform) {
|
if !validPlatforms.Contains(options.Platform) {
|
||||||
return "", fmt.Errorf("platform %s not supported", options.Platform)
|
return "", fmt.Errorf("platform %s not supported", options.Platform)
|
||||||
}
|
}
|
||||||
@@ -71,9 +69,6 @@ func Build(options *Options) (string, error) {
|
|||||||
}
|
}
|
||||||
options.ProjectData = projectData
|
options.ProjectData = projectData
|
||||||
|
|
||||||
// Calculate build dir
|
|
||||||
options.BuildDirectory = filepath.Join(options.ProjectData.Path, "build", options.Platform, options.OutputType)
|
|
||||||
|
|
||||||
// Save the project type
|
// Save the project type
|
||||||
projectData.OutputType = options.OutputType
|
projectData.OutputType = options.OutputType
|
||||||
|
|
||||||
@@ -82,13 +77,11 @@ func Build(options *Options) (string, error) {
|
|||||||
|
|
||||||
switch projectData.OutputType {
|
switch projectData.OutputType {
|
||||||
case "desktop":
|
case "desktop":
|
||||||
builder = newDesktopBuilder(options)
|
builder = newDesktopBuilder()
|
||||||
case "hybrid":
|
case "hybrid":
|
||||||
builder = newHybridBuilder(options)
|
builder = newHybridBuilder()
|
||||||
case "server":
|
case "server":
|
||||||
builder = newServerBuilder(options)
|
builder = newServerBuilder()
|
||||||
case "dev":
|
|
||||||
builder = newDesktopBuilder(options)
|
|
||||||
default:
|
default:
|
||||||
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
return "", fmt.Errorf("cannot build assets for output type %s", projectData.OutputType)
|
||||||
}
|
}
|
||||||
@@ -99,40 +92,34 @@ func Build(options *Options) (string, error) {
|
|||||||
// Initialise Builder
|
// Initialise Builder
|
||||||
builder.SetProjectData(projectData)
|
builder.SetProjectData(projectData)
|
||||||
|
|
||||||
// Generate Frontend JS Package
|
outputLogger.Writeln(" - Building Wails Frontend")
|
||||||
// outputLogger.Println(" - Generating Backend JS Package")
|
err = builder.BuildFrontend(outputLogger)
|
||||||
// // Ignore the parser report coming back
|
if err != nil {
|
||||||
// _, err = parser.GenerateWailsFrontendPackage()
|
return "", err
|
||||||
// if err != nil {
|
|
||||||
// return "", err
|
|
||||||
// }
|
|
||||||
if !options.IgnoreFrontend {
|
|
||||||
outputLogger.Println(" - Building Project Frontend")
|
|
||||||
err = builder.BuildFrontend(outputLogger)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the base assets
|
// Build the base assets
|
||||||
outputLogger.Println(" - Compiling Assets")
|
outputLogger.Writeln(" - Compiling Assets")
|
||||||
|
err = builder.BuildRuntime(options)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
err = builder.BuildAssets(options)
|
err = builder.BuildAssets(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the application
|
// Compile the application
|
||||||
outputLogger.Print(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
outputLogger.Write(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
||||||
err = builder.CompileProject(options)
|
err = builder.CompileProject(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
outputLogger.Println("done.")
|
outputLogger.Writeln("done.")
|
||||||
|
|
||||||
// Do we need to pack the app?
|
// Do we need to pack the app?
|
||||||
if options.Pack {
|
if options.Pack {
|
||||||
|
|
||||||
outputLogger.Println(" - Packaging Application")
|
outputLogger.Writeln(" - Packaging Application")
|
||||||
|
|
||||||
// TODO: Allow cross platform build
|
// TODO: Allow cross platform build
|
||||||
err = packageProject(options, runtime.GOOS)
|
err = packageProject(options, runtime.GOOS)
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package build
|
package build
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/project"
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder defines a builder that can build Wails applications
|
// Builder defines a builder that can build Wails applications
|
||||||
type Builder interface {
|
type Builder interface {
|
||||||
SetProjectData(projectData *project.Project)
|
SetProjectData(projectData *project.Project)
|
||||||
BuildAssets(*Options) error
|
BuildAssets(*Options) error
|
||||||
BuildFrontend(*clilogger.CLILogger) error
|
BuildFrontend(*logger.Logger) error
|
||||||
BuildRuntime(*Options) error
|
BuildRuntime(*Options) error
|
||||||
CompileProject(*Options) error
|
CompileProject(*Options) error
|
||||||
CleanUp()
|
CleanUp()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
"github.com/wailsapp/wails/v2/internal/html"
|
"github.com/wailsapp/wails/v2/internal/html"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DesktopBuilder builds applications for the desktop
|
// DesktopBuilder builds applications for the desktop
|
||||||
@@ -14,9 +15,9 @@ type DesktopBuilder struct {
|
|||||||
*BaseBuilder
|
*BaseBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDesktopBuilder(options *Options) *DesktopBuilder {
|
func newDesktopBuilder() *DesktopBuilder {
|
||||||
return &DesktopBuilder{
|
return &DesktopBuilder{
|
||||||
BaseBuilder: NewBaseBuilder(options),
|
BaseBuilder: NewBaseBuilder(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,17 +25,6 @@ func newDesktopBuilder(options *Options) *DesktopBuilder {
|
|||||||
func (d *DesktopBuilder) BuildAssets(options *Options) error {
|
func (d *DesktopBuilder) BuildAssets(options *Options) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Check assets directory exists
|
|
||||||
if !fs.DirExists(options.ProjectData.AssetsDir) {
|
|
||||||
// Path to default assets
|
|
||||||
defaultAssets := fs.RelativePath("./internal/assets")
|
|
||||||
// Copy the default assets directory
|
|
||||||
err := fs.CopyDir(defaultAssets, options.ProjectData.AssetsDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a list of assets from the HTML
|
// Get a list of assets from the HTML
|
||||||
assets, err := d.BaseBuilder.ExtractAssets()
|
assets, err := d.BaseBuilder.ExtractAssets()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -42,7 +32,13 @@ func (d *DesktopBuilder) BuildAssets(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build base assets (HTML/JS/CSS/etc)
|
// Build base assets (HTML/JS/CSS/etc)
|
||||||
err = d.BuildBaseAssets(assets, options)
|
err = d.BuildBaseAssets(assets, options.Logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build static assets
|
||||||
|
err = d.buildStaticAssets(d.projectData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -51,25 +47,13 @@ func (d *DesktopBuilder) BuildAssets(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildBaseAssets builds the assets for the desktop application
|
// BuildBaseAssets builds the assets for the desktop application
|
||||||
func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Options) error {
|
func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, outputLogger *logger.Logger) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
outputLogger := options.Logger
|
outputLogger.Write(" - Embedding Assets...")
|
||||||
outputLogger.Print(" - Embedding Assets...")
|
|
||||||
|
|
||||||
// Get target asset directory
|
// Get target asset directory
|
||||||
assetDir, err := fs.RelativeToCwd("build")
|
assetDir := fs.RelativePath("../../../internal/ffenestri")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make dir if it doesn't exist
|
|
||||||
if !fs.DirExists(assetDir) {
|
|
||||||
err := fs.Mkdir(assetDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump assets as C
|
// Dump assets as C
|
||||||
assetsFile, err := assets.WriteToCFile(assetDir)
|
assetsFile, err := assets.WriteToCFile(assetDir)
|
||||||
@@ -79,34 +63,22 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti
|
|||||||
d.addFileToDelete(assetsFile)
|
d.addFileToDelete(assetsFile)
|
||||||
|
|
||||||
// Process Icon
|
// Process Icon
|
||||||
err = d.processApplicationIcon(assetDir)
|
err = d.processIcon(assetDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process Tray Icons
|
outputLogger.Writeln("done.")
|
||||||
err = d.processTrayIcons(assetDir, options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Dialog Icons
|
|
||||||
err = d.processDialogIcons(assetDir, options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
outputLogger.Println("done.")
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processApplicationIcon will copy a default icon if one doesn't exist, then, if
|
// processIcon will copy a default icon if one doesn't exist, then, if
|
||||||
// needed, will compile the icon
|
// needed, will compile the icon
|
||||||
func (d *DesktopBuilder) processApplicationIcon(assetDir string) error {
|
func (d *DesktopBuilder) processIcon(assetDir string) error {
|
||||||
|
|
||||||
// Copy default icon if one doesn't exist
|
// Copy default icon if one doesn't exist
|
||||||
iconFile := filepath.Join(d.projectData.AssetsDir, "appicon.png")
|
iconFile := filepath.Join(d.projectData.Path, "icon.png")
|
||||||
if !fs.FileExists(iconFile) {
|
if !fs.FileExists(iconFile) {
|
||||||
err := fs.CopyFile(defaultIconPath(), iconFile)
|
err := fs.CopyFile(defaultIconPath(), iconFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -129,7 +101,7 @@ func (d *DesktopBuilder) BuildRuntime(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
outputLogger.Print(" - Embedding Runtime...")
|
outputLogger.Write(" - Embedding Runtime...")
|
||||||
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
||||||
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
|
if err := d.NpmRunWithEnvironment(sourceDir, "build:desktop", false, envvars); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -140,7 +112,7 @@ func (d *DesktopBuilder) BuildRuntime(options *Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
outputLogger.Println("done.")
|
outputLogger.Writeln("done.")
|
||||||
|
|
||||||
// Convert to C structure
|
// Convert to C structure
|
||||||
runtimeC := `
|
runtimeC := `
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/v2/internal/project"
|
"github.com/wailsapp/wails/v2/internal/project"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HybridBuilder builds applications as a server
|
// HybridBuilder builds applications as a server
|
||||||
@@ -12,11 +11,11 @@ type HybridBuilder struct {
|
|||||||
server *ServerBuilder
|
server *ServerBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHybridBuilder(options *Options) Builder {
|
func newHybridBuilder() Builder {
|
||||||
result := &HybridBuilder{
|
result := &HybridBuilder{
|
||||||
BaseBuilder: NewBaseBuilder(options),
|
BaseBuilder: NewBaseBuilder(),
|
||||||
desktop: newDesktopBuilder(options),
|
desktop: newDesktopBuilder(),
|
||||||
server: newServerBuilder(options),
|
server: newServerBuilder(),
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -31,7 +30,7 @@ func (b *HybridBuilder) BuildAssets(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Build static assets
|
// Build static assets
|
||||||
err = b.buildCustomAssets(b.projectData)
|
err = b.buildStaticAssets(b.projectData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -39,12 +38,6 @@ func (b *HybridBuilder) BuildAssets(options *Options) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildFrontend builds the assets for the desktop application
|
|
||||||
func (b *HybridBuilder) BuildFrontend(_ *clilogger.CLILogger) error {
|
|
||||||
panic("To be implemented")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildAssets builds the assets for the desktop application
|
// BuildAssets builds the assets for the desktop application
|
||||||
func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
|
func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
|
||||||
|
|
||||||
@@ -53,7 +46,7 @@ func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = b.desktop.BuildBaseAssets(assets, options)
|
err = b.desktop.BuildBaseAssets(assets, options.Logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -64,13 +57,13 @@ func (b *HybridBuilder) BuildBaseAssets(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build desktop static assets
|
// Build desktop static assets
|
||||||
err = b.desktop.buildCustomAssets(b.projectData)
|
err = b.desktop.buildStaticAssets(b.projectData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build server static assets
|
// Build server static assets
|
||||||
err = b.server.buildCustomAssets(b.projectData)
|
err = b.server.buildStaticAssets(b.projectData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ func packageProject(options *Options, platform string) error {
|
|||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch platform {
|
switch platform {
|
||||||
case "linux", "darwin":
|
case "linux":
|
||||||
err = packageApplication(options)
|
err = packageLinuxApplication(options)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("packing not supported for %s yet", platform)
|
err = fmt.Errorf("packing not supported for %s yet", platform)
|
||||||
}
|
}
|
||||||
@@ -27,28 +27,29 @@ func packageProject(options *Options, platform string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanBuildDirectory will remove an existing build directory and recreate it
|
// Gets (and creates) the platform/target build directory
|
||||||
func cleanBuildDirectory(options *Options) error {
|
func getApplicationBuildDirectory(options *Options, platform string) (string, error) {
|
||||||
|
buildDirectory := filepath.Join(options.ProjectData.Path, "build", platform, options.OutputType)
|
||||||
buildDirectory := options.BuildDirectory
|
|
||||||
|
|
||||||
// Clear out old builds
|
// Clear out old builds
|
||||||
if fs.DirExists(buildDirectory) {
|
if fs.DirExists(buildDirectory) {
|
||||||
err := os.RemoveAll(buildDirectory)
|
err := os.RemoveAll(buildDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create clean directory
|
// Create clean directory
|
||||||
err := os.MkdirAll(buildDirectory, 0700)
|
err := os.MkdirAll(buildDirectory, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return buildDirectory, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyFileToBuildDirectory() {}
|
||||||
|
|
||||||
// Gets (and creates) the build base directory
|
// Gets (and creates) the build base directory
|
||||||
func getBuildBaseDirectory(options *Options) (string, error) {
|
func getBuildBaseDirectory(options *Options) (string, error) {
|
||||||
buildDirectory := filepath.Join(options.ProjectData.Path, "build")
|
buildDirectory := filepath.Join(options.ProjectData.Path, "build")
|
||||||
@@ -63,7 +64,7 @@ func getBuildBaseDirectory(options *Options) (string, error) {
|
|||||||
|
|
||||||
// Gets the path to the default icon
|
// Gets the path to the default icon
|
||||||
func defaultIconPath() string {
|
func defaultIconPath() string {
|
||||||
return fs.RelativePath("internal/packager/icon1024.png")
|
return fs.RelativePath("internal/packager/icon64.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets the platform dependent package assets directory
|
// Gets the platform dependent package assets directory
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func deleteLinuxPackFiles(appDirBase string) {
|
|||||||
os.RemoveAll(appDir)
|
os.RemoveAll(appDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func packageApplication(options *Options) error {
|
func packageLinuxApplication(options *Options) error {
|
||||||
|
|
||||||
// Check we have AppImage tools
|
// Check we have AppImage tools
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,15 @@ type ServerBuilder struct {
|
|||||||
*BaseBuilder
|
*BaseBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServerBuilder(options *Options) *ServerBuilder {
|
func newServerBuilder() *ServerBuilder {
|
||||||
result := &ServerBuilder{
|
result := &ServerBuilder{
|
||||||
BaseBuilder: NewBaseBuilder(options),
|
BaseBuilder: NewBaseBuilder(),
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildAssets builds the assets for the desktop application
|
// BuildAssets builds the assets for the desktop application
|
||||||
func (s *ServerBuilder) BuildAssets(_ *Options) error {
|
func (s *ServerBuilder) BuildAssets(options *Options) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
assets, err := s.BaseBuilder.ExtractAssets()
|
assets, err := s.BaseBuilder.ExtractAssets()
|
||||||
@@ -38,7 +38,7 @@ func (s *ServerBuilder) BuildAssets(_ *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build static assets
|
// Build static assets
|
||||||
err = s.buildCustomAssets(s.projectData)
|
err = s.buildStaticAssets(s.projectData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -56,11 +56,11 @@ func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
|
|||||||
// Fetch, update, and reinject index.html
|
// Fetch, update, and reinject index.html
|
||||||
index, err := db.Read("index.html")
|
index, err := db.Read("index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf(`failed to locate "index.html"`)
|
return fmt.Errorf(`Failed to locate "index.html"`)
|
||||||
}
|
}
|
||||||
splits := strings.Split(string(index), "</body>")
|
splits := strings.Split(string(index), "</body>")
|
||||||
if len(splits) != 2 {
|
if len(splits) != 2 {
|
||||||
return fmt.Errorf("unable to locate a </body> tag in your frontend/index.html")
|
return fmt.Errorf("Unable to locate a </body> tag in your frontend/index.html")
|
||||||
}
|
}
|
||||||
injectScript := `<script defer src="/wails.js"></script><script defer src="/bindings.js"></script>`
|
injectScript := `<script defer src="/wails.js"></script><script defer src="/bindings.js"></script>`
|
||||||
result := []string{}
|
result := []string{}
|
||||||
@@ -84,14 +84,11 @@ func (s *ServerBuilder) BuildBaseAssets(assets *html.AssetBundle) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
_, err = f.WriteString(db.Serialize("db", "webserver"))
|
f.WriteString(db.Serialize("db", "webserver"))
|
||||||
if err != nil {
|
|
||||||
// Ignore error - we already have one!
|
return nil
|
||||||
_ = f.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return f.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildRuntime builds the javascript runtime used by the HTML client to connect to the websocket
|
// BuildRuntime builds the javascript runtime used by the HTML client to connect to the websocket
|
||||||
@@ -102,13 +99,13 @@ func (s *ServerBuilder) BuildRuntime(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
options.Logger.Print(" - Embedding Runtime...")
|
options.Logger.Write(" - Embedding Runtime...")
|
||||||
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
envvars := []string{"WAILSPLATFORM=" + options.Platform}
|
||||||
var err error
|
var err error
|
||||||
if err = s.NpmRunWithEnvironment(sourceDir, "build:server", false, envvars); err != nil {
|
if err = s.NpmRunWithEnvironment(sourceDir, "build:server", false, envvars); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
options.Logger.Println("done.")
|
options.Logger.Writeln("done.")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user