mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 13:19:00 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71bfd29376 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -16,4 +16,13 @@ examples/**/example*
|
||||
cmd/wails/wails
|
||||
.DS_Store
|
||||
tmp
|
||||
node_modules/
|
||||
node_modules/
|
||||
package.json.md5
|
||||
v2/test/**/frontend/dist
|
||||
v2/test/**/build/
|
||||
v2/test/frameless/icon.png
|
||||
v2/test/hidden/icon.png
|
||||
v2/internal/ffenestri/runtime.c
|
||||
v2/internal/runtime/assets/desktop.js
|
||||
v2/test/kitchensink/frontend/public/bundle.*
|
||||
v2/pkg/parser/testproject/frontend/wails
|
||||
|
||||
2
go.mod
2
go.mod
@@ -22,7 +22,7 @@ require (
|
||||
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/text v0.3.0
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||
|
||||
2
go.sum
2
go.sum
@@ -81,6 +81,8 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||
|
||||
13
v2/.vscode/settings.json
vendored
Normal file
13
v2/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"ios": "c",
|
||||
"typeinfo": "c",
|
||||
"sstream": "c",
|
||||
"__functional_03": "c",
|
||||
"functional": "c",
|
||||
"__locale": "c",
|
||||
"locale": "c",
|
||||
"chrono": "c",
|
||||
"system_error": "c"
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,19 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
||||
func AddBuildSubcommand(app *clir.Cli) {
|
||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
outputType := "desktop"
|
||||
|
||||
@@ -56,11 +56,8 @@ func AddBuildSubcommand(app *clir.Cli) {
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := logger.New()
|
||||
|
||||
if !quiet {
|
||||
logger.AddOutput(os.Stdout)
|
||||
}
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Validate output type
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
@@ -72,8 +69,8 @@ func AddBuildSubcommand(app *clir.Cli) {
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Writeln(task)
|
||||
logger.Writeln(strings.Repeat("-", len(task)))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
@@ -108,9 +105,9 @@ func doBuild(buildOptions *build.Options) error {
|
||||
}
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
buildOptions.Logger.Writeln("")
|
||||
buildOptions.Logger.Writeln(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||
buildOptions.Logger.Writeln("")
|
||||
buildOptions.Logger.Println("")
|
||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||
buildOptions.Logger.Println("")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package dev
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
@@ -13,13 +14,13 @@ import (
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"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/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli) error {
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("dev", "Development mode")
|
||||
|
||||
@@ -51,8 +52,7 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
}
|
||||
|
||||
// Create logger
|
||||
logger := logger.New()
|
||||
logger.AddOutput(os.Stdout)
|
||||
logger := clilogger.New(w)
|
||||
app.PrintBanner()
|
||||
|
||||
// TODO: Check you are in a project directory
|
||||
@@ -74,11 +74,11 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
debounceQuit := make(chan bool, 1)
|
||||
|
||||
// Do initial build
|
||||
logger.Info("Building application for development...")
|
||||
logger.Println("Building application for development...")
|
||||
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||
|
||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||
// logger.Info("event: %+v", event)
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
@@ -86,7 +86,7 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
watcher.Add(event.Name)
|
||||
logger.Info("Watching directory: %s", event.Name)
|
||||
logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -95,7 +95,7 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
// logger.Info("modified file: %s", event.Name)
|
||||
// logger.Println("modified file: %s", event.Name)
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
@@ -112,14 +112,14 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
logger.Info("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||
logger.Println("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||
return
|
||||
}
|
||||
|
||||
if buildFrontend {
|
||||
logger.Info("Full rebuild triggered: %s updated", event.Name)
|
||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
||||
} else {
|
||||
logger.Info("Partial build triggered: %s updated", event.Name)
|
||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||
}
|
||||
|
||||
// Do a rebuild
|
||||
@@ -152,7 +152,7 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
logger.Info("Watching directory: %s", dir)
|
||||
logger.Println("Watching directory: %s", dir)
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
@@ -176,7 +176,7 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
debugBinaryProcess.Kill()
|
||||
}
|
||||
|
||||
logger.Info("Development mode exited")
|
||||
logger.Println("Development mode exited")
|
||||
|
||||
return nil
|
||||
})
|
||||
@@ -203,15 +203,15 @@ exit:
|
||||
}
|
||||
}
|
||||
|
||||
func restartApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||
|
||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
||||
println()
|
||||
if err != nil {
|
||||
logger.Error("Build Failed: %s", err.Error())
|
||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
logger.Info("Build new binary: %s", appBinary)
|
||||
logger.Println("Build new binary: %s", appBinary)
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
@@ -238,7 +238,7 @@ func restartApp(logger *logger.Logger, outputType string, ldflags string, compil
|
||||
return newProcess
|
||||
}
|
||||
|
||||
func buildApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
|
||||
@@ -2,37 +2,37 @@ package doctor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"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/packagemanager"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `doctor` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli) error {
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := logger.New()
|
||||
logger.AddOutput(os.Stdout)
|
||||
logger := clilogger.New(w)
|
||||
|
||||
app.PrintBanner()
|
||||
print("Scanning system - please wait...")
|
||||
logger.Print("Scanning system - please wait...")
|
||||
|
||||
// Get system info
|
||||
info, err := system.GetInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
println("Done.")
|
||||
logger.Println("Done.")
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
@@ -112,22 +112,22 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||
w.Flush()
|
||||
println()
|
||||
println("Diagnosis")
|
||||
println("---------\n")
|
||||
logger.Println("")
|
||||
logger.Println("Diagnosis")
|
||||
logger.Println("---------\n")
|
||||
|
||||
// Generate an appropriate diagnosis
|
||||
|
||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||
println("Your system is ready for Wails development!")
|
||||
logger.Println("Your system is ready for Wails development!")
|
||||
}
|
||||
|
||||
if dependenciesAvailableRequired != 0 {
|
||||
println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||
}
|
||||
|
||||
if dependenciesAvailableOptional != 0 {
|
||||
println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||
}
|
||||
|
||||
if len(externalPackages) > 0 {
|
||||
@@ -135,18 +135,18 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
if p.Optional {
|
||||
print("[Optional] ")
|
||||
}
|
||||
println("Install " + p.Name + ": " + p.InstallCommand)
|
||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
||||
}
|
||||
}
|
||||
|
||||
if len(dependenciesMissing) != 0 {
|
||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
||||
println("Fatal:")
|
||||
println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||
log.Println("Fatal:")
|
||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||
}
|
||||
|
||||
println()
|
||||
log.Println("")
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
91
v2/cmd/wails/internal/commands/generate/generate.go
Normal file
91
v2/cmd/wails/internal/commands/generate/generate.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
||||
|
||||
// Backend API
|
||||
backendAPI := command.NewSubCommand("module", "Generates a JS module for the frontend to interface with the backend")
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
backendAPI.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
app.PrintBanner()
|
||||
|
||||
logger.Print("Generating Javascript module for Go code...")
|
||||
|
||||
// Start Time
|
||||
start := time.Now()
|
||||
|
||||
p, err := parser.GenerateWailsFrontendPackage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Println("done.")
|
||||
logger.Println("")
|
||||
|
||||
elapsed := time.Since(start)
|
||||
packages := p.Packages
|
||||
|
||||
// Print report
|
||||
for _, pkg := range p.Packages {
|
||||
if pkg.ShouldGenerate() {
|
||||
logPackage(pkg, logger)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.Println("%d packages parsed in %s.", len(packages), elapsed)
|
||||
|
||||
return nil
|
||||
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
||||
|
||||
logger.Println("Processed Go package '" + pkg.Gopackage.Name + "' as '" + pkg.Name + "'")
|
||||
for _, strct := range pkg.Structs() {
|
||||
logger.Println("")
|
||||
logger.Println(" Processed struct '" + strct.Name + "'")
|
||||
if strct.IsBound {
|
||||
for _, method := range strct.Methods {
|
||||
logger.Println(" Bound method '" + method.Name + "'")
|
||||
}
|
||||
}
|
||||
if strct.IsUsedAsData {
|
||||
for _, field := range strct.Fields {
|
||||
if !field.Ignored {
|
||||
logger.Print(" Processed ")
|
||||
if field.IsOptional {
|
||||
logger.Print("optional ")
|
||||
}
|
||||
logger.Println("field '" + field.Name + "' as '" + field.JSName() + "'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.Println("")
|
||||
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
}
|
||||
@@ -2,17 +2,17 @@ package initialise
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `init` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli) error {
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Load the template shortnames
|
||||
validShortNames, err := templates.TemplateShortNames()
|
||||
@@ -32,13 +32,17 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
command.StringFlag("n", "Name of project", &projectName)
|
||||
|
||||
// Setup project directory
|
||||
projectDirectory := "."
|
||||
projectDirectory := ""
|
||||
command.StringFlag("d", "Project directory", &projectDirectory)
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
// VSCode project files
|
||||
vscode := false
|
||||
command.BoolFlag("vscode", "Generate VSCode project files", &vscode)
|
||||
|
||||
// List templates
|
||||
list := false
|
||||
command.BoolFlag("l", "List templates", &list)
|
||||
@@ -46,32 +50,29 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
logger := logger.New()
|
||||
|
||||
if !quiet {
|
||||
logger.AddOutput(os.Stdout)
|
||||
}
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
|
||||
// Are we listing templates?
|
||||
if list {
|
||||
app.PrintBanner()
|
||||
err := templates.OutputList(logger)
|
||||
logger.Writeln("")
|
||||
logger.Println("")
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate output type
|
||||
if !validShortNames.Contains(templateName) {
|
||||
logger.Write(fmt.Sprintf("ERROR: Template '%s' is not valid", templateName))
|
||||
logger.Writeln("")
|
||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
||||
logger.Println("")
|
||||
command.PrintHelp()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate name
|
||||
if len(projectName) == 0 {
|
||||
logger.Writeln("ERROR: Project name required")
|
||||
logger.Writeln("")
|
||||
logger.Println("ERROR: Project name required")
|
||||
logger.Println("")
|
||||
command.PrintHelp()
|
||||
return nil
|
||||
}
|
||||
@@ -81,15 +82,16 @@ func AddSubcommand(app *clir.Cli) error {
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
||||
logger.Writeln(task)
|
||||
logger.Writeln(strings.Repeat("-", len(task)))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Create Template Options
|
||||
options := &templates.Options{
|
||||
ProjectName: projectName,
|
||||
TargetDir: projectDirectory,
|
||||
TemplateName: templateName,
|
||||
Logger: logger,
|
||||
ProjectName: projectName,
|
||||
TargetDir: projectDirectory,
|
||||
TemplateName: templateName,
|
||||
Logger: logger,
|
||||
GenerateVSCode: vscode,
|
||||
}
|
||||
|
||||
return initProject(options)
|
||||
@@ -112,9 +114,17 @@ func initProject(options *templates.Options) error {
|
||||
|
||||
// Output stats
|
||||
elapsed := time.Since(start)
|
||||
options.Logger.Writeln("")
|
||||
options.Logger.Writeln(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
||||
options.Logger.Writeln("")
|
||||
options.Logger.Println("")
|
||||
options.Logger.Println("Project Name: " + options.ProjectName)
|
||||
options.Logger.Println("Project Directory: " + options.TargetDir)
|
||||
options.Logger.Println("Project Template: " + options.TemplateName)
|
||||
options.Logger.Println("")
|
||||
if options.GenerateVSCode {
|
||||
options.Logger.Println("VSCode config files generated.")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
||||
)
|
||||
|
||||
@@ -22,17 +23,22 @@ func main() {
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
build.AddBuildSubcommand(app)
|
||||
err = initialise.AddSubcommand(app)
|
||||
build.AddBuildSubcommand(app, os.Stdout)
|
||||
err = initialise.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
err = doctor.AddSubcommand(app)
|
||||
err = doctor.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = dev.AddSubcommand(app)
|
||||
err = dev.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
err = generate.AddSubcommand(app, os.Stdout)
|
||||
if err != nil {
|
||||
fatal(err.Error())
|
||||
}
|
||||
|
||||
10
v2/go.mod
10
v2/go.mod
@@ -3,15 +3,23 @@ module github.com/wailsapp/wails/v2
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/gosod v0.0.4
|
||||
github.com/leaanthony/slicer v1.4.1
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
nhooyr.io/websocket v1.8.6
|
||||
|
||||
20
v2/go.sum
20
v2/go.sum
@@ -1,6 +1,8 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/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=
|
||||
@@ -29,6 +31,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
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/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
||||
@@ -40,8 +44,8 @@ github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||
github.com/leaanthony/slicer v1.4.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w=
|
||||
github.com/leaanthony/slicer v1.4.1/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/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=
|
||||
@@ -58,12 +62,20 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
||||
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=
|
||||
@@ -89,6 +101,8 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -110,5 +124,7 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
|
||||
@@ -5,6 +5,5 @@ package app
|
||||
// Init initialises the application for a debug environment
|
||||
func (a *App) Init() error {
|
||||
a.debug = true
|
||||
println("Initialising debug options")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ package app
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/features"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
@@ -22,12 +22,10 @@ type App struct {
|
||||
|
||||
// Indicates if the app is running in debug mode
|
||||
debug bool
|
||||
|
||||
Features *features.Features
|
||||
}
|
||||
|
||||
// CreateApp returns a null application
|
||||
func CreateApp(options *Options) *App {
|
||||
func CreateApp(options *options.App) *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"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/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/internal/signal"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
@@ -21,6 +20,7 @@ type App struct {
|
||||
servicebus *servicebus.ServiceBus
|
||||
logger *logger.Logger
|
||||
signal *signal.Manager
|
||||
options *options.App
|
||||
|
||||
// Subsystems
|
||||
log *subsystem.Log
|
||||
@@ -36,46 +36,31 @@ type App struct {
|
||||
// This is our binding DB
|
||||
bindings *binding.Bindings
|
||||
|
||||
// Feature flags
|
||||
Features *features.Features
|
||||
// LogLevel Store
|
||||
loglevelStore *runtime.Store
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *Options) *App {
|
||||
func CreateApp(options *options.App) *App {
|
||||
|
||||
// Merge default options
|
||||
options.mergeDefaults()
|
||||
options.MergeDefaults()
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(os.Stdout)
|
||||
myLogger.SetLogLevel(logger.TRACE)
|
||||
myLogger := logger.New(options.Logger)
|
||||
myLogger.SetLogLevel(options.LogLevel)
|
||||
|
||||
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||
Title: options.Title,
|
||||
Width: options.Width,
|
||||
Height: options.Height,
|
||||
MinWidth: options.MinWidth,
|
||||
MinHeight: options.MinHeight,
|
||||
MaxWidth: options.MaxWidth,
|
||||
MaxHeight: options.MaxHeight,
|
||||
Frameless: options.Frameless,
|
||||
StartHidden: options.StartHidden,
|
||||
|
||||
// This should be controlled by the compile time flags...
|
||||
DevTools: true,
|
||||
|
||||
Resizable: !options.DisableResize,
|
||||
Fullscreen: options.Fullscreen,
|
||||
}, myLogger)
|
||||
window := ffenestri.NewApplicationWithConfig(options, myLogger)
|
||||
|
||||
result := &App{
|
||||
window: window,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
Features: features.New(),
|
||||
}
|
||||
|
||||
result.options = options
|
||||
|
||||
// Initialise the app
|
||||
result.Init()
|
||||
|
||||
@@ -106,6 +91,9 @@ func (a *App) Run() error {
|
||||
a.runtime = runtime
|
||||
a.runtime.Start()
|
||||
|
||||
// Application Stores
|
||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("loglevel", a.options.LogLevel)
|
||||
|
||||
// Start the binding subsystem
|
||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
@@ -115,7 +103,7 @@ func (a *App) Run() error {
|
||||
a.binding.Start()
|
||||
|
||||
// Start the logging subsystem
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -139,7 +127,7 @@ func (a *App) Run() error {
|
||||
a.event.Start()
|
||||
|
||||
// Start the call subsystem
|
||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -152,7 +140,7 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||
result := a.window.Run(dispatcher, bindingDump)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
a.servicebus.Stop()
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"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/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
@@ -44,9 +43,6 @@ type App struct {
|
||||
servicebus *servicebus.ServiceBus
|
||||
|
||||
debug bool
|
||||
|
||||
// Feature flags
|
||||
Features *features.Features
|
||||
}
|
||||
|
||||
// Create App
|
||||
@@ -67,7 +63,6 @@ func CreateApp(options *Options) *App {
|
||||
MinHeight: options.MinHeight,
|
||||
MaxWidth: options.MaxWidth,
|
||||
MaxHeight: options.MaxHeight,
|
||||
Frameless: options.Frameless,
|
||||
StartHidden: options.StartHidden,
|
||||
|
||||
// This should be controlled by the compile time flags...
|
||||
@@ -191,7 +186,7 @@ func (a *App) Run() error {
|
||||
}
|
||||
}()
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||
result := a.window.Run(dispatcher, bindingDump)
|
||||
a.servicebus.Stop()
|
||||
|
||||
return result
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
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
|
||||
StartHidden bool
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -34,7 +35,7 @@ func (b *BoundMethod) VerifyWailsInit() error {
|
||||
}
|
||||
|
||||
// Check input type
|
||||
if !b.Inputs[0].IsType("*goruntime.Runtime") {
|
||||
if !b.Inputs[0].IsType("*runtime.Runtime") {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
@@ -78,6 +79,26 @@ func (b *BoundMethod) OutputCount() int {
|
||||
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())
|
||||
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
|
||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||
// Check inputs
|
||||
@@ -94,17 +115,8 @@ func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||
|
||||
// Iterate over given arguments
|
||||
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
|
||||
callArgs[index] = value
|
||||
callArgs[index] = reflect.ValueOf(arg)
|
||||
}
|
||||
|
||||
// Do the call
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
@@ -67,6 +68,8 @@ func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
boundMethod.Inputs = inputs
|
||||
|
||||
// Iterate outputs
|
||||
// TODO: Determine what to do about limiting return types
|
||||
// especially around errors.
|
||||
outputParamCount := methodType.NumOut()
|
||||
var outputs []*Parameter
|
||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
||||
@@ -84,7 +87,7 @@ func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||
}
|
||||
|
||||
// convertArgToValue
|
||||
func convertArgToValue(input interface{}, target *Parameter) (result reflect.Value, err error) {
|
||||
func convertArgToValue(input json.RawMessage, target *Parameter) (result reflect.Value, err error) {
|
||||
|
||||
// Catch type conversion panics thrown by convert
|
||||
defer func() {
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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{},
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package features
|
||||
|
||||
// Linux holds linux specific feature flags
|
||||
type Linux struct {
|
||||
}
|
||||
62
v2/internal/ffenestri/README.md
Normal file
62
v2/internal/ffenestri/README.md
Normal file
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
// +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,9 +5,9 @@ import (
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/features"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
@@ -26,36 +29,9 @@ import "C"
|
||||
// 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
|
||||
StartHidden bool
|
||||
}
|
||||
|
||||
var defaultConfig = &Config{
|
||||
Title: "My Wails App",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
DevTools: true,
|
||||
Resizable: true,
|
||||
Fullscreen: false,
|
||||
Frameless: false,
|
||||
StartHidden: false,
|
||||
}
|
||||
|
||||
// Application is our main application object
|
||||
type Application struct {
|
||||
config *Config
|
||||
config *options.App
|
||||
memory []unsafe.Pointer
|
||||
|
||||
// This is the main app pointer
|
||||
@@ -80,7 +56,7 @@ func init() {
|
||||
}
|
||||
|
||||
// NewApplicationWithConfig creates a new application based on the given config
|
||||
func NewApplicationWithConfig(config *Config, logger *logger.Logger) *Application {
|
||||
func NewApplicationWithConfig(config *options.App, logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: config,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
@@ -90,7 +66,7 @@ func NewApplicationWithConfig(config *Config, logger *logger.Logger) *Applicatio
|
||||
// NewApplication creates a new Application with the default config
|
||||
func NewApplication(logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: defaultConfig,
|
||||
config: options.Default,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
@@ -123,16 +99,25 @@ type DispatchClient interface {
|
||||
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
|
||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, features *features.Features) error {
|
||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string) error {
|
||||
title := a.string2CString(a.config.Title)
|
||||
width := C.int(a.config.Width)
|
||||
height := C.int(a.config.Height)
|
||||
resizable := a.bool2Cint(a.config.Resizable)
|
||||
resizable := a.bool2Cint(!a.config.DisableResize)
|
||||
devtools := a.bool2Cint(a.config.DevTools)
|
||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden)
|
||||
logLevel := C.int(a.config.LogLevel)
|
||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel)
|
||||
|
||||
// Save app reference
|
||||
a.app = unsafe.Pointer(app)
|
||||
@@ -150,9 +135,14 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, featur
|
||||
// Set debug if needed
|
||||
C.SetDebug(app, a.bool2Cint(DEBUG))
|
||||
|
||||
// Set Frameless
|
||||
if a.config.Frameless {
|
||||
C.DisableFrame(a.app)
|
||||
// TODO: Move frameless to Linux options
|
||||
// if a.config.Frameless {
|
||||
// C.DisableFrame(a.app)
|
||||
// }
|
||||
|
||||
if a.config.RGBA != 0 {
|
||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
||||
C.SetColour(a.app, r, g, b, alpha)
|
||||
}
|
||||
|
||||
// Escape bindings so C doesn't freak out
|
||||
@@ -161,13 +151,13 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, featur
|
||||
// Set 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
|
||||
// can access it
|
||||
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
||||
|
||||
// Process platform settings
|
||||
a.processPlatformSettings()
|
||||
|
||||
// Check we could initialise the application
|
||||
if app != nil {
|
||||
// Yes - Save memory reference and run app, cleaning up afterwards
|
||||
@@ -189,11 +179,3 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, featur
|
||||
func messageFromWindowCallback(data *C.char) {
|
||||
dispatcher.DispatchMessage(C.GoString(data))
|
||||
}
|
||||
|
||||
func (a *Application) processFeatureFlags(features *features.Features) {
|
||||
|
||||
// Process generic features
|
||||
|
||||
// Process OS Specific flags
|
||||
a.processOSFeatureFlags(features)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden);
|
||||
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel);
|
||||
extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
|
||||
extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
|
||||
extern void Run(void *app, int argc, char **argv);
|
||||
@@ -16,18 +16,20 @@ extern void Show(void *app);
|
||||
extern void Center(void *app);
|
||||
extern void Maximise(void *app);
|
||||
extern void Unmaximise(void *app);
|
||||
extern void ToggleMaximise(void *app);
|
||||
extern void Minimise(void *app);
|
||||
extern void Unminimise(void *app);
|
||||
extern void ToggleMinimise(void *app);
|
||||
extern void SetColour(void *app, int red, int green, int blue, int alpha);
|
||||
extern void SetSize(void *app, int width, int height);
|
||||
extern void SetPosition(void *app, int x, int y);
|
||||
extern void Quit(void *app);
|
||||
extern void SetTitle(void *app, const char *title);
|
||||
extern void Fullscreen(void *app);
|
||||
extern void UnFullscreen(void *app);
|
||||
extern int SetColour(void *app, const char *colourString);
|
||||
extern void ToggleFullscreen(void *app);
|
||||
extern void DisableFrame(void *app);
|
||||
extern char *SaveFileDialog(void *appPointer, char *title, char *filter);
|
||||
extern char *OpenFileDialog(void *appPointer, char *title, char *filter);
|
||||
extern char *OpenDirectoryDialog(void *appPointer, char *title, char *filter);
|
||||
|
||||
extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolveAliases, int treatPackagesAsDirectories);
|
||||
extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
#endif
|
||||
|
||||
@@ -13,9 +13,9 @@ import "C"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client is our implentation of messageDispatcher.Client
|
||||
@@ -114,46 +114,43 @@ func (c *Client) WindowSize(width int, height int) {
|
||||
}
|
||||
|
||||
// WindowSetColour sets the window colour
|
||||
func (c *Client) WindowSetColour(colour string) bool {
|
||||
result := C.SetColour(c.app.app, c.app.string2CString(colour))
|
||||
return result == 1
|
||||
func (c *Client) WindowSetColour(colour int) {
|
||||
r, g, b, a := intToColour(colour)
|
||||
C.SetColour(c.app.app, r, g, b, a)
|
||||
}
|
||||
|
||||
// OpenFileDialog will open a file dialog with the given title
|
||||
func (c *Client) OpenFileDialog(title string, filter string) string {
|
||||
|
||||
cstring := C.OpenFileDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
|
||||
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
|
||||
// OpenDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDialog(dialogOptions *options.OpenDialog, callbackID string) {
|
||||
C.OpenDialog(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.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.ResolveAliases),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
// SaveFileDialog will open a save file dialog with the given title
|
||||
func (c *Client) SaveFileDialog(title string, filter string) string {
|
||||
|
||||
cstring := C.SaveFileDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
|
||||
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
|
||||
// SaveDialog will open a dialog with the given title and filter
|
||||
func (c *Client) SaveDialog(dialogOptions *options.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),
|
||||
)
|
||||
}
|
||||
|
||||
// OpenDirectoryDialog will open a directory dialog with the given title
|
||||
func (c *Client) OpenDirectoryDialog(title string, filter string) string {
|
||||
|
||||
cstring := C.OpenDirectoryDialog(c.app.app, c.app.string2CString(title), c.app.string2CString(filter))
|
||||
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
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
|
||||
1050
v2/internal/ffenestri/ffenestri_darwin.c
Normal file
1050
v2/internal/ffenestri/ffenestri_darwin.c
Normal file
File diff suppressed because it is too large
Load Diff
67
v2/internal/ffenestri/ffenestri_darwin.go
Normal file
67
v2/internal/ffenestri/ffenestri_darwin.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
extern void TitlebarAppearsTransparent(void *);
|
||||
extern void HideTitle(void *);
|
||||
extern void HideTitleBar(void *);
|
||||
extern void FullSizeContent(void *);
|
||||
extern void UseToolbar(void *);
|
||||
extern void HideToolbarSeparator(void *);
|
||||
extern void DisableFrame(void *);
|
||||
extern void SetAppearance(void *, const char *);
|
||||
extern void WebviewIsTransparent(void *);
|
||||
extern void SetWindowBackgroundIsTranslucent(void *);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() {
|
||||
|
||||
mac := a.config.Mac
|
||||
titlebar := mac.TitleBar
|
||||
|
||||
// HideTitle
|
||||
if titlebar.HideTitle {
|
||||
C.HideTitle(a.app)
|
||||
}
|
||||
|
||||
// HideTitleBar
|
||||
if titlebar.HideTitleBar {
|
||||
C.HideTitleBar(a.app)
|
||||
}
|
||||
|
||||
// Full Size Content
|
||||
if titlebar.FullSizeContent {
|
||||
C.FullSizeContent(a.app)
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
if titlebar.UseToolbar {
|
||||
C.UseToolbar(a.app)
|
||||
}
|
||||
|
||||
if titlebar.HideToolbarSeparator {
|
||||
C.HideToolbarSeparator(a.app)
|
||||
}
|
||||
|
||||
if titlebar.TitlebarAppearsTransparent {
|
||||
C.TitlebarAppearsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Process window Appearance
|
||||
if mac.Appearance != "" {
|
||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||
}
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Check if window should be translucent
|
||||
if mac.WindowBackgroundIsTranslucent {
|
||||
C.SetWindowBackgroundIsTranslucent(a.app)
|
||||
}
|
||||
}
|
||||
1383
v2/internal/ffenestri/json.c
Normal file
1383
v2/internal/ffenestri/json.c
Normal file
File diff suppressed because it is too large
Load Diff
119
v2/internal/ffenestri/json.h
Normal file
119
v2/internal/ffenestri/json.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
Source: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
||||
*/
|
||||
|
||||
#ifndef CCAN_JSON_H
|
||||
#define CCAN_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
JSON_NULL,
|
||||
JSON_BOOL,
|
||||
JSON_STRING,
|
||||
JSON_NUMBER,
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
} JsonTag;
|
||||
|
||||
typedef struct JsonNode JsonNode;
|
||||
|
||||
struct JsonNode
|
||||
{
|
||||
/* only if parent is an object or array (NULL otherwise) */
|
||||
JsonNode *parent;
|
||||
JsonNode *prev, *next;
|
||||
|
||||
/* only if parent is an object (NULL otherwise) */
|
||||
char *key; /* Must be valid UTF-8. */
|
||||
|
||||
JsonTag tag;
|
||||
union {
|
||||
/* JSON_BOOL */
|
||||
bool bool_;
|
||||
|
||||
/* JSON_STRING */
|
||||
char *string_; /* Must be valid UTF-8. */
|
||||
|
||||
/* JSON_NUMBER */
|
||||
double number_;
|
||||
|
||||
/* JSON_ARRAY */
|
||||
/* JSON_OBJECT */
|
||||
struct {
|
||||
JsonNode *head, *tail;
|
||||
} children;
|
||||
};
|
||||
};
|
||||
|
||||
/*** Encoding, decoding, and validation ***/
|
||||
|
||||
JsonNode *json_decode (const char *json);
|
||||
char *json_encode (const JsonNode *node);
|
||||
char *json_encode_string (const char *str);
|
||||
char *json_stringify (const JsonNode *node, const char *space);
|
||||
void json_delete (JsonNode *node);
|
||||
|
||||
bool json_validate (const char *json);
|
||||
|
||||
/*** Lookup and traversal ***/
|
||||
|
||||
JsonNode *json_find_element (JsonNode *array, int index);
|
||||
JsonNode *json_find_member (JsonNode *object, const char *key);
|
||||
|
||||
JsonNode *json_first_child (const JsonNode *node);
|
||||
|
||||
#define json_foreach(i, object_or_array) \
|
||||
for ((i) = json_first_child(object_or_array); \
|
||||
(i) != NULL; \
|
||||
(i) = (i)->next)
|
||||
|
||||
/*** Construction and manipulation ***/
|
||||
|
||||
JsonNode *json_mknull(void);
|
||||
JsonNode *json_mkbool(bool b);
|
||||
JsonNode *json_mkstring(const char *s);
|
||||
JsonNode *json_mknumber(double n);
|
||||
JsonNode *json_mkarray(void);
|
||||
JsonNode *json_mkobject(void);
|
||||
|
||||
void json_append_element(JsonNode *array, JsonNode *element);
|
||||
void json_prepend_element(JsonNode *array, JsonNode *element);
|
||||
void json_append_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
void json_prepend_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
|
||||
void json_remove_from_parent(JsonNode *node);
|
||||
|
||||
/*** Debugging ***/
|
||||
|
||||
/*
|
||||
* Look for structure and encoding problems in a JsonNode or its descendents.
|
||||
*
|
||||
* If a problem is detected, return false, writing a description of the problem
|
||||
* to errmsg (unless errmsg is NULL).
|
||||
*/
|
||||
bool json_check(const JsonNode *node, char errmsg[256]);
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,17 @@ func LocalDirectory() string {
|
||||
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
|
||||
func Mkdir(dirname string) error {
|
||||
return os.Mkdir(dirname, 0755)
|
||||
@@ -169,3 +180,23 @@ func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
||||
})
|
||||
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
|
||||
}
|
||||
|
||||
@@ -3,11 +3,15 @@ package html
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tdewolff/minify"
|
||||
"github.com/tdewolff/minify/js"
|
||||
)
|
||||
|
||||
type assetTypes struct {
|
||||
@@ -63,6 +67,10 @@ func (a *Asset) AsCHexData() string {
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\r\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}
|
||||
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
||||
|
||||
@@ -84,6 +92,16 @@ func (a *Asset) AsCHexData() string {
|
||||
result = strings.ReplaceAll(result, ` {`, `{`)
|
||||
result = strings.ReplaceAll(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
|
||||
@@ -103,6 +121,7 @@ func (a *Asset) AsCHexData() string {
|
||||
return cdata.String()
|
||||
}
|
||||
|
||||
// Dump will output the asset to the terminal
|
||||
func (a *Asset) Dump() {
|
||||
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
||||
}
|
||||
|
||||
@@ -153,6 +153,10 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
||||
assetVariables := slicer.String()
|
||||
var variableName string
|
||||
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)
|
||||
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
||||
cdata.WriteString(assetCdata)
|
||||
@@ -160,9 +164,9 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
||||
}
|
||||
|
||||
if assetVariables.Length() > 0 {
|
||||
cdata.WriteString(fmt.Sprintf("\nconst char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||
cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||
} else {
|
||||
cdata.WriteString("\nconst char *assets[] = { 0x00 };")
|
||||
cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };")
|
||||
}
|
||||
|
||||
// Save file
|
||||
@@ -187,6 +191,7 @@ func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
||||
return assetdb, nil
|
||||
}
|
||||
|
||||
// Dump will output the assets to the terminal
|
||||
func (a *AssetBundle) Dump() {
|
||||
println("Assets:")
|
||||
for _, asset := range a.assets {
|
||||
|
||||
@@ -2,7 +2,6 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// CustomLogger defines what a user can do with a logger
|
||||
@@ -62,37 +61,36 @@ func (l *customLogger) Write(message string) error {
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.processLogMessage(TRACE, format, args...)
|
||||
return l.logger.Trace(format, args...)
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.processLogMessage(DEBUG, format, args...)
|
||||
return l.logger.Debug(format, args...)
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.processLogMessage(INFO, format, args...)
|
||||
return l.logger.Info(format, args...)
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.processLogMessage(WARNING, format, args...)
|
||||
return l.logger.Warning(format, args...)
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.processLogMessage(ERROR, format, args...)
|
||||
return l.logger.Error(format, args...)
|
||||
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
l.logger.processLogMessage(FATAL, format, args...)
|
||||
os.Exit(1)
|
||||
l.logger.Fatal(format, args...)
|
||||
}
|
||||
|
||||
@@ -2,37 +2,32 @@ package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"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
|
||||
type Logger struct {
|
||||
writers []io.Writer
|
||||
logLevel uint8
|
||||
output logger.Logger
|
||||
logLevel LogLevel
|
||||
showLevelInLog bool
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
||||
// are the targets for the logs
|
||||
func New(writers ...io.Writer) *Logger {
|
||||
func New(output logger.Logger) *Logger {
|
||||
result := &Logger{
|
||||
logLevel: INFO,
|
||||
logLevel: logger.INFO,
|
||||
showLevelInLog: true,
|
||||
}
|
||||
for _, writer := range writers {
|
||||
result.AddOutput(writer)
|
||||
output: output,
|
||||
}
|
||||
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
|
||||
// before the messages
|
||||
func (l *Logger) CustomLogger(name string) CustomLogger {
|
||||
@@ -45,99 +40,73 @@ func (l *Logger) HideLogLevel() {
|
||||
}
|
||||
|
||||
// SetLogLevel sets the minimum level of logs that will be output
|
||||
func (l *Logger) SetLogLevel(level uint8) {
|
||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
||||
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
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Writeln(message string) error {
|
||||
return l.write(BYPASS, message+"\n")
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *Logger) Write(message string) error {
|
||||
return l.write(BYPASS, message)
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// processLogMessage formats the given message before writing it out
|
||||
func (l *Logger) processLogMessage(loglevel uint8, format string, args ...interface{}) error {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
return l.writeln(loglevel, message)
|
||||
// Print writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Print(message string) error {
|
||||
return l.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||
return l.processLogMessage(TRACE, 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...)
|
||||
if l.logLevel <= logger.TRACE {
|
||||
return l.output.Trace(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||
return l.processLogMessage(DEBUG, format, args...)
|
||||
if l.logLevel <= logger.DEBUG {
|
||||
return l.output.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||
return l.processLogMessage(INFO, format, args...)
|
||||
if l.logLevel <= logger.INFO {
|
||||
return l.output.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||
return l.processLogMessage(WARNING, format, args...)
|
||||
if l.logLevel <= logger.WARNING {
|
||||
return l.output.Warning(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||
return l.processLogMessage(ERROR, format, args...)
|
||||
|
||||
if l.logLevel <= logger.ERROR {
|
||||
return l.output.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||
l.processLogMessage(FATAL, format, args...)
|
||||
err := l.output.Fatal(fmt.Sprintf(format, args...))
|
||||
// Not much we can do but print it out before exiting
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
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: "",
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
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,6 +6,7 @@ import (
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client defines what a frontend client can do
|
||||
@@ -13,9 +14,8 @@ type Client interface {
|
||||
Quit()
|
||||
NotifyEvent(message string)
|
||||
CallResult(message string)
|
||||
SaveFileDialog(title string, filter string) string
|
||||
OpenFileDialog(title string, filter string) string
|
||||
OpenDirectoryDialog(title string, filter string) string
|
||||
OpenDialog(dialogOptions *options.OpenDialog, callbackID string)
|
||||
SaveDialog(dialogOptions *options.SaveDialog, callbackID string)
|
||||
WindowSetTitle(title string)
|
||||
WindowShow()
|
||||
WindowHide()
|
||||
@@ -28,7 +28,8 @@ type Client interface {
|
||||
WindowSize(width int, height int)
|
||||
WindowFullscreen()
|
||||
WindowUnFullscreen()
|
||||
WindowSetColour(colour string) bool
|
||||
WindowSetColour(colour int)
|
||||
DarkModeEnabled(callbackID string)
|
||||
}
|
||||
|
||||
// DispatchClient is what the frontends use to interface with the
|
||||
@@ -63,7 +64,7 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
||||
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
||||
parsedMessage, err := message.Parse(incomingMessage)
|
||||
if err != nil {
|
||||
d.logger.Trace("Error: " + err.Error())
|
||||
d.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
||||
|
||||
// Check error
|
||||
if err != nil {
|
||||
d.logger.Trace("Error: " + err.Error())
|
||||
d.logger.Error(err.Error())
|
||||
// Hrm... what do we do with this?
|
||||
d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
|
||||
return
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
type CallMessage struct {
|
||||
Name string `json:"name"`
|
||||
Args []interface{} `json:"args"`
|
||||
CallbackID string `json:"callbackID,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Args []json.RawMessage `json:"args"`
|
||||
CallbackID string `json:"callbackID,omitempty"`
|
||||
}
|
||||
|
||||
// callMessageParser does what it says on the tin!
|
||||
@@ -22,6 +22,7 @@ func callMessageParser(message string) (*parsedMessage, error) {
|
||||
callMessage := new(CallMessage)
|
||||
|
||||
m := message[1:]
|
||||
|
||||
err := json.Unmarshal([]byte(m), callMessage)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
|
||||
52
v2/internal/messagedispatcher/message/dialog.go
Normal file
52
v2/internal/messagedispatcher/message/dialog.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dialogMessageParser does what it says on the tin!
|
||||
func dialogMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Dialog messages must be at least 4 bytes
|
||||
if len(message) < 4 {
|
||||
return nil, fmt.Errorf("dialog message was an invalid length")
|
||||
}
|
||||
|
||||
var topic = "bad topic from dialogMessageParser"
|
||||
var responseMessage *parsedMessage
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
// Format of Dialog response messages: D<dialog type><callbackID>|<[]string as json encoded string>
|
||||
case 'D':
|
||||
dialogType := message[1]
|
||||
message = message[2:]
|
||||
idx := strings.IndexByte(message, '|')
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid dialog response message format")
|
||||
}
|
||||
callbackID := message[:idx+1]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
switch dialogType {
|
||||
case 'O':
|
||||
var data []string
|
||||
topic = "dialog:openselected:" + callbackID
|
||||
err := json.Unmarshal([]byte(payloadData), &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: data}
|
||||
case 'S':
|
||||
topic = "dialog:saveselected:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to dialogMessageParser()")
|
||||
}
|
||||
|
||||
return responseMessage, nil
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EventMessage struct {
|
||||
Name string `json:"name"`
|
||||
@@ -12,6 +13,7 @@ type EventMessage struct {
|
||||
type OnEventMessage struct {
|
||||
Name string
|
||||
Callback func(optionalData ...interface{})
|
||||
Counter int
|
||||
}
|
||||
|
||||
// eventMessageParser does what it says on the tin!
|
||||
@@ -27,8 +29,6 @@ func eventMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
case 'e':
|
||||
eventMessage.Name = message[2:]
|
||||
case 'E':
|
||||
m := message[2:]
|
||||
err := json.Unmarshal([]byte(m), eventMessage)
|
||||
|
||||
@@ -3,16 +3,19 @@ package message
|
||||
import "fmt"
|
||||
|
||||
var logMessageMap = map[byte]string{
|
||||
'P': "log:print",
|
||||
'T': "log:trace",
|
||||
'D': "log:debug",
|
||||
'I': "log:info",
|
||||
'W': "log:warning",
|
||||
'E': "log:error",
|
||||
'F': "log:fatal",
|
||||
'S': "log:setlevel",
|
||||
}
|
||||
|
||||
// logMessageParser does what it says on the tin!
|
||||
func logMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
|
||||
// Sanity check: Log messages must be at least 2 bytes
|
||||
if len(message) < 2 {
|
||||
return nil, fmt.Errorf("log message was an invalid length")
|
||||
@@ -23,7 +26,7 @@ func logMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// If the type is invalid, raise error
|
||||
if messageTopic == "" {
|
||||
return nil, fmt.Errorf("log message type '%b' invalid", message[1])
|
||||
return nil, fmt.Errorf("log message type '%c' invalid", message[1])
|
||||
}
|
||||
|
||||
// Create a new parsed message struct
|
||||
|
||||
@@ -14,9 +14,10 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
||||
'L': logMessageParser,
|
||||
'R': runtimeMessageParser,
|
||||
'E': eventMessageParser,
|
||||
'e': eventMessageParser,
|
||||
'C': callMessageParser,
|
||||
'W': windowMessageParser,
|
||||
'D': dialogMessageParser,
|
||||
'S': systemMessageParser,
|
||||
}
|
||||
|
||||
// Parse will attempt to parse the given message
|
||||
|
||||
@@ -22,13 +22,14 @@ func runtimeMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// processBrowserMessage expects messages of the following format:
|
||||
// RB<METHOD><DATA>
|
||||
// O = Open
|
||||
func processBrowserMessage(message string) (*parsedMessage, error) {
|
||||
method := message[2]
|
||||
switch method {
|
||||
case 'U':
|
||||
case 'O':
|
||||
// Open URL
|
||||
url := message[3:]
|
||||
return &parsedMessage{Topic: "runtime:browser:openurl", Data: url}, nil
|
||||
target := message[3:]
|
||||
return &parsedMessage{Topic: "runtime:browser:open", Data: target}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown browser message: %s", message)
|
||||
|
||||
42
v2/internal/messagedispatcher/message/system.go
Normal file
42
v2/internal/messagedispatcher/message/system.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// systemMessageParser does what it says on the tin!
|
||||
func systemMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: system messages must be at least 4 bytes
|
||||
if len(message) < 4 {
|
||||
return nil, fmt.Errorf("system message was an invalid length")
|
||||
}
|
||||
|
||||
var responseMessage *parsedMessage
|
||||
|
||||
// Remove 'S'
|
||||
message = message[1:]
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
// Format of system response messages: S<command><callbackID>|<payload>
|
||||
// DarkModeEnabled
|
||||
case 'D':
|
||||
message = message[1:]
|
||||
idx := strings.IndexByte(message, '|')
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid system response message format")
|
||||
}
|
||||
callbackID := message[:idx]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
topic := "systemresponse:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData == "T"}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||
}
|
||||
|
||||
return responseMessage, nil
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Dispatcher translates messages received from the frontend
|
||||
@@ -20,6 +21,7 @@ type Dispatcher struct {
|
||||
eventChannel <-chan *servicebus.Message
|
||||
windowChannel <-chan *servicebus.Message
|
||||
dialogChannel <-chan *servicebus.Message
|
||||
systemChannel <-chan *servicebus.Message
|
||||
running bool
|
||||
|
||||
servicebus *servicebus.ServiceBus
|
||||
@@ -62,6 +64,11 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
systemChannel, err := servicebus.Subscribe("system:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := &Dispatcher{
|
||||
servicebus: servicebus,
|
||||
eventChannel: eventChannel,
|
||||
@@ -71,6 +78,7 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
||||
quitChannel: quitChannel,
|
||||
windowChannel: windowChannel,
|
||||
dialogChannel: dialogChannel,
|
||||
systemChannel: systemChannel,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -98,6 +106,8 @@ func (d *Dispatcher) Start() error {
|
||||
d.processWindowMessage(windowMessage)
|
||||
case dialogMessage := <-d.dialogChannel:
|
||||
d.processDialogMessage(dialogMessage)
|
||||
case systemMessage := <-d.systemChannel:
|
||||
d.processSystemMessage(systemMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +183,28 @@ func (d *Dispatcher) processCallResult(result *servicebus.Message) {
|
||||
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
|
||||
func (d *Dispatcher) processEvent(result *servicebus.Message) {
|
||||
|
||||
@@ -231,7 +263,7 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
||||
client.frontend.WindowUnFullscreen()
|
||||
}
|
||||
case "setcolour":
|
||||
colour, ok := result.Data().(string)
|
||||
colour, ok := result.Data().(int)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
|
||||
return
|
||||
@@ -317,7 +349,6 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
||||
// processDialogMessage processes dialog messages
|
||||
func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
||||
splitTopic := strings.Split(result.Topic(), ":")
|
||||
|
||||
if len(splitTopic) < 4 {
|
||||
d.logger.Error("Invalid dialog message : %#v", result.Data())
|
||||
return
|
||||
@@ -327,65 +358,42 @@ func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
||||
switch command {
|
||||
case "select":
|
||||
dialogType := splitTopic[2]
|
||||
title := splitTopic[3]
|
||||
filter := ""
|
||||
if len(splitTopic) > 4 {
|
||||
filter = splitTopic[4]
|
||||
}
|
||||
switch dialogType {
|
||||
case "file":
|
||||
responseTopic, ok := result.Data().(string)
|
||||
case "open":
|
||||
dialogOptions, ok := result.Data().(*options.OpenDialog)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid responseTopic for 'dialog:select:file' : %#v", result.Data())
|
||||
d.logger.Error("Invalid data for 'dialog:select:open' : %#v", result.Data())
|
||||
return
|
||||
}
|
||||
d.logger.Info("Opening File dialog! responseTopic = %s", responseTopic)
|
||||
// This is hardcoded in the sender too
|
||||
callbackID := splitTopic[3]
|
||||
|
||||
// TODO: Work out what we mean in a multi window environment...
|
||||
// For now we will just pick the first one
|
||||
var result string
|
||||
for _, client := range d.clients {
|
||||
result = client.frontend.OpenFileDialog(title, filter)
|
||||
client.frontend.OpenDialog(dialogOptions, callbackID)
|
||||
}
|
||||
|
||||
// Send dummy response
|
||||
d.servicebus.Publish(responseTopic, result)
|
||||
case "filesave":
|
||||
responseTopic, ok := result.Data().(string)
|
||||
case "save":
|
||||
dialogOptions, ok := result.Data().(*options.SaveDialog)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid responseTopic for 'dialog:select:filesave' : %#v", result.Data())
|
||||
d.logger.Error("Invalid data for 'dialog:select:save' : %#v", result.Data())
|
||||
return
|
||||
}
|
||||
d.logger.Info("Opening Save File dialog! responseTopic = %s", responseTopic)
|
||||
// This is hardcoded in the sender too
|
||||
callbackID := splitTopic[3]
|
||||
|
||||
// TODO: Work out what we mean in a multi window environment...
|
||||
// For now we will just pick the first one
|
||||
var result string
|
||||
for _, client := range d.clients {
|
||||
result = client.frontend.SaveFileDialog(title, filter)
|
||||
client.frontend.SaveDialog(dialogOptions, callbackID)
|
||||
}
|
||||
|
||||
// Send dummy response
|
||||
d.servicebus.Publish(responseTopic, result)
|
||||
case "directory":
|
||||
responseTopic, ok := result.Data().(string)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid responseTopic for 'dialog:select:directory' : %#v", result.Data())
|
||||
return
|
||||
}
|
||||
d.logger.Info("Opening Directory dialog! responseTopic = %s", responseTopic)
|
||||
|
||||
// TODO: Work out what we mean in a multi window environment...
|
||||
// For now we will just pick the first one
|
||||
var result string
|
||||
for _, client := range d.clients {
|
||||
result = client.frontend.OpenDirectoryDialog(title, filter)
|
||||
}
|
||||
// Send dummy response
|
||||
d.servicebus.Publish(responseTopic, result)
|
||||
|
||||
default:
|
||||
d.logger.Error("Unknown dialog command: %s", command)
|
||||
d.logger.Error("Unknown dialog type: %s", dialogType)
|
||||
}
|
||||
|
||||
default:
|
||||
d.logger.Error("Unknown dialog command: %s", command)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
||||
var wailsPkgVar = ""
|
||||
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
var s string
|
||||
switch x := n.(type) {
|
||||
// Parse import declarations
|
||||
case *ast.ImportSpec:
|
||||
|
||||
@@ -3,19 +3,19 @@ package process
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// Process defines a process that can be executed
|
||||
type Process struct {
|
||||
logger *logger.Logger
|
||||
logger *clilogger.CLILogger
|
||||
cmd *exec.Cmd
|
||||
exitChannel chan bool
|
||||
Running bool
|
||||
}
|
||||
|
||||
// NewProcess creates a new process struct
|
||||
func NewProcess(logger *logger.Logger, cmd string, args ...string) *Process {
|
||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
return &Process{
|
||||
logger: logger,
|
||||
cmd: exec.Command(cmd, args...),
|
||||
@@ -33,10 +33,10 @@ func (p *Process) Start() error {
|
||||
|
||||
p.Running = true
|
||||
|
||||
go func(cmd *exec.Cmd, running *bool, logger *logger.Logger, exitChannel chan bool) {
|
||||
logger.Info("Starting process (PID: %d)", cmd.Process.Pid)
|
||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
||||
cmd.Wait()
|
||||
logger.Info("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||
*running = false
|
||||
exitChannel <- true
|
||||
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package goruntime
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -8,23 +8,23 @@ import (
|
||||
|
||||
// Browser defines all browser related operations
|
||||
type Browser interface {
|
||||
Open(url string) error
|
||||
Open(target 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 {
|
||||
func (b *browser) Open(target string) error {
|
||||
var err error
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
err = exec.Command("xdg-open", target).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", target).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
err = exec.Command("open", target).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package goruntime
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"os"
|
||||
94
v2/internal/runtime/dialog.go
Normal file
94
v2/internal/runtime/dialog.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Dialog defines all Dialog related operations
|
||||
type Dialog interface {
|
||||
Open(dialogOptions *options.OpenDialog) []string
|
||||
Save(dialogOptions *options.SaveDialog) 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,
|
||||
}
|
||||
}
|
||||
|
||||
// processTitleAndFilter return the title and filter from the given params.
|
||||
// title is the first string, filter is the second
|
||||
func (r *dialog) processTitleAndFilter(params ...string) (string, string) {
|
||||
|
||||
var title, filter string
|
||||
|
||||
if len(params) > 0 {
|
||||
title = params[0]
|
||||
}
|
||||
|
||||
if len(params) > 1 {
|
||||
filter = params[1]
|
||||
}
|
||||
|
||||
return title, filter
|
||||
}
|
||||
|
||||
// Open prompts the user to select a file
|
||||
func (r *dialog) Open(dialogOptions *options.OpenDialog) []string {
|
||||
|
||||
// Create unique dialog callback
|
||||
uniqueCallback := crypto.RandomID()
|
||||
|
||||
// Subscribe to the respose channel
|
||||
responseTopic := "dialog:openselected:" + uniqueCallback
|
||||
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||
}
|
||||
|
||||
message := "dialog:select:open:" + uniqueCallback
|
||||
r.bus.Publish(message, dialogOptions)
|
||||
|
||||
// Wait for result
|
||||
var result *servicebus.Message = <-dialogResponseChannel
|
||||
|
||||
// Delete subscription to response topic
|
||||
r.bus.UnSubscribe(responseTopic)
|
||||
|
||||
return result.Data().([]string)
|
||||
}
|
||||
|
||||
// Save prompts the user to select a file
|
||||
func (r *dialog) Save(dialogOptions *options.SaveDialog) string {
|
||||
|
||||
// Create unique dialog callback
|
||||
uniqueCallback := crypto.RandomID()
|
||||
|
||||
// Subscribe to the respose channel
|
||||
responseTopic := "dialog:saveselected:" + uniqueCallback
|
||||
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||
}
|
||||
|
||||
message := "dialog:select:save:" + uniqueCallback
|
||||
r.bus.Publish(message, dialogOptions)
|
||||
|
||||
// Wait for result
|
||||
var result *servicebus.Message = <-dialogResponseChannel
|
||||
|
||||
// Delete subscription to response topic
|
||||
r.bus.UnSubscribe(responseTopic)
|
||||
|
||||
return result.Data().(string)
|
||||
}
|
||||
68
v2/internal/runtime/events.go
Normal file
68
v2/internal/runtime/events.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package runtime
|
||||
|
||||
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{}))
|
||||
Once(eventName string, callback func(optionalData ...interface{}))
|
||||
OnMultiple(eventName string, callback func(optionalData ...interface{}), maxCallbacks int)
|
||||
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 registers a listener for a particular event
|
||||
func (r *event) On(eventName string, callback func(optionalData ...interface{})) {
|
||||
eventMessage := &message.OnEventMessage{
|
||||
Name: eventName,
|
||||
Callback: callback,
|
||||
Counter: -1,
|
||||
}
|
||||
r.bus.Publish("event:on", eventMessage)
|
||||
}
|
||||
|
||||
// Once registers a listener for a particular event. After the first callback, the
|
||||
// listener is deleted.
|
||||
func (r *event) Once(eventName string, callback func(optionalData ...interface{})) {
|
||||
eventMessage := &message.OnEventMessage{
|
||||
Name: eventName,
|
||||
Callback: callback,
|
||||
Counter: 1,
|
||||
}
|
||||
r.bus.Publish("event:on", eventMessage)
|
||||
}
|
||||
|
||||
// OnMultiple registers a listener for a particular event, for a given maximum amount of callbacks.
|
||||
// Once the callback has been run `maxCallbacks` times, the listener is deleted.
|
||||
func (r *event) OnMultiple(eventName string, callback func(optionalData ...interface{}), maxCallbacks int) {
|
||||
eventMessage := &message.OnEventMessage{
|
||||
Name: eventName,
|
||||
Callback: callback,
|
||||
Counter: maxCallbacks,
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
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(params ...string) string
|
||||
SelectFile(params ...string) string
|
||||
SelectDirectory(params ...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,
|
||||
}
|
||||
}
|
||||
|
||||
// processTitleAndFilter return the title and filter from the given params.
|
||||
// title is the first string, filter is the second
|
||||
func (r *dialog) processTitleAndFilter(params ...string) (string, string) {
|
||||
|
||||
var title, filter string
|
||||
|
||||
if len(params) > 0 {
|
||||
title = params[0]
|
||||
}
|
||||
|
||||
if len(params) > 1 {
|
||||
filter = params[1]
|
||||
}
|
||||
|
||||
return title, filter
|
||||
}
|
||||
|
||||
// SelectFile prompts the user to select a file
|
||||
func (r *dialog) SelectFile(params ...string) string {
|
||||
|
||||
// Extract title + filter
|
||||
title, filter := r.processTitleAndFilter(params...)
|
||||
|
||||
// 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
|
||||
message := "dialog:select:file:" + title
|
||||
if filter != "" {
|
||||
message += ":" + filter
|
||||
}
|
||||
r.bus.Publish(message, 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(params ...string) string {
|
||||
|
||||
// Extract title + filter
|
||||
title, filter := r.processTitleAndFilter(params...)
|
||||
|
||||
// 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
|
||||
message := "dialog:select:filesave:" + title
|
||||
if filter != "" {
|
||||
message += ":" + filter
|
||||
}
|
||||
r.bus.Publish(message, 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(params ...string) string {
|
||||
|
||||
// Extract title + filter
|
||||
title, filter := r.processTitleAndFilter(params...)
|
||||
|
||||
// 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
|
||||
message := "dialog:select:directory:" + title
|
||||
if filter != "" {
|
||||
message += ":" + filter
|
||||
}
|
||||
r.bus.Publish(message, responseTopic)
|
||||
|
||||
// Wait for result
|
||||
var result *servicebus.Message = <-dialogResponseChannel
|
||||
|
||||
// Delete subscription to response topic
|
||||
r.bus.UnSubscribe(responseTopic)
|
||||
|
||||
return result.Data().(string)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -12,23 +12,13 @@ The lightweight framework for web-like apps
|
||||
import { SendMessage } from 'ipc';
|
||||
|
||||
/**
|
||||
* Opens the given URL in the system browser
|
||||
* Opens the given URL / filename in the system browser
|
||||
*
|
||||
* @export
|
||||
* @param {string} url
|
||||
* @param {string} target
|
||||
* @returns
|
||||
*/
|
||||
export function OpenURL(url) {
|
||||
return SendMessage('RBU' + url);
|
||||
export function Open(target) {
|
||||
return SendMessage('RBO' + target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -12,14 +12,16 @@ import { SetBindings } from './bindings';
|
||||
import { Init } from './main';
|
||||
|
||||
// Setup global error handler
|
||||
window.onerror = function (/*msg, url, lineNo, columnNo, error*/) {
|
||||
// window.wails.Log.Error('**** Caught Unhandled Error ****');
|
||||
// window.wails.Log.Error('Message: ' + msg);
|
||||
// window.wails.Log.Error('URL: ' + url);
|
||||
// window.wails.Log.Error('Line No: ' + lineNo);
|
||||
// window.wails.Log.Error('Column No: ' + columnNo);
|
||||
// window.wails.Log.Error('error: ' + error);
|
||||
(function () { window.wails.Log.Error(new Error().stack); })();
|
||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
||||
const errorMessage = {
|
||||
message: msg,
|
||||
url: url,
|
||||
line: lineNo,
|
||||
column: columnNo,
|
||||
error: JSON.stringify(error),
|
||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
||||
};
|
||||
window.wails.Log.Error(JSON.stringify(errorMessage));
|
||||
};
|
||||
|
||||
// Initialise the Runtime
|
||||
|
||||
23
v2/internal/runtime/js/core/dialog.js
Normal file
23
v2/internal/runtime/js/core/dialog.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
import { SendMessage } from 'ipc';
|
||||
|
||||
/**
|
||||
* Open a dialog with the given parameters
|
||||
*
|
||||
* @export
|
||||
* @param {object} options
|
||||
*/
|
||||
export function Open(options) {
|
||||
SendMessage('DO'+JSON.stringify(options));
|
||||
}
|
||||
@@ -82,6 +82,38 @@ export function Once(eventName, callback) {
|
||||
OnMultiple(eventName, callback, 1);
|
||||
}
|
||||
|
||||
function notifyListeners(eventData) {
|
||||
|
||||
// Get the event name
|
||||
var 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];
|
||||
|
||||
var 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 listners
|
||||
eventListeners[eventName] = newEventListenerList;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify informs frontend listeners that an event was emitted with the given data
|
||||
*
|
||||
@@ -100,33 +132,7 @@ export function Notify(notifyMessage) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
notifyListeners(message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,66 +143,15 @@ export function Notify(notifyMessage) {
|
||||
*/
|
||||
export function Emit(eventName) {
|
||||
|
||||
// Calculate the data
|
||||
if (arguments.length > 1) {
|
||||
// Notify backend
|
||||
const payload = {
|
||||
name: eventName,
|
||||
data: [].slice.apply(arguments).slice(1),
|
||||
};
|
||||
SendMessage('Ej' + JSON.stringify(payload));
|
||||
} else {
|
||||
SendMessage('ej' + eventName);
|
||||
}
|
||||
const payload = {
|
||||
name: eventName,
|
||||
data: [].slice.apply(arguments).slice(1),
|
||||
};
|
||||
|
||||
}
|
||||
// Notify JS listeners
|
||||
notifyListeners(payload);
|
||||
|
||||
// Callbacks for the heartbeat calls
|
||||
const heartbeatCallbacks = {};
|
||||
// Notify Go listeners
|
||||
SendMessage('Ej' + JSON.stringify(payload));
|
||||
|
||||
/**
|
||||
* 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,6 +25,26 @@ function sendLogMessage(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
|
||||
*
|
||||
@@ -74,3 +94,22 @@ export function Error(message) {
|
||||
export function Fatal(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,
|
||||
};
|
||||
|
||||
@@ -11,11 +11,13 @@ The lightweight framework for web-like apps
|
||||
import * as Log from './log';
|
||||
import * as Browser from './browser';
|
||||
import * as Window from './window';
|
||||
import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
|
||||
import { Callback } from './calls';
|
||||
import * as Dialog from './dialog';
|
||||
import { On, Once, OnMultiple, Emit, Notify } from './events';
|
||||
import { Callback, SystemCall } from './calls';
|
||||
import { AddScript, InjectCSS } from './utils';
|
||||
import { AddIPCListener } from 'ipc';
|
||||
import * as Platform from 'platform';
|
||||
import * as Store from './store';
|
||||
|
||||
export function Init() {
|
||||
// Backend is where the Go struct wrappers get bound to
|
||||
@@ -27,23 +29,35 @@ export function Init() {
|
||||
Log,
|
||||
Browser,
|
||||
Window,
|
||||
Dialog,
|
||||
Events: {
|
||||
On,
|
||||
Once,
|
||||
OnMultiple,
|
||||
Emit,
|
||||
Heartbeat,
|
||||
Acknowledge,
|
||||
},
|
||||
_: {
|
||||
Callback,
|
||||
Notify,
|
||||
AddScript,
|
||||
InjectCSS,
|
||||
Init,
|
||||
AddIPCListener
|
||||
}
|
||||
// Init,
|
||||
AddIPCListener,
|
||||
SystemCall,
|
||||
},
|
||||
Store,
|
||||
};
|
||||
|
||||
// Setup system. Store uses window.wails so needs to be setup after that
|
||||
window.wails.System = {
|
||||
IsDarkMode: Store.New('isdarkmode'),
|
||||
LogLevel: Store.New('loglevel'),
|
||||
};
|
||||
// Copy platform specific information into it
|
||||
Object.assign(window.wails.System, Platform.System);
|
||||
|
||||
// Do platform specific Init
|
||||
Platform.Init();
|
||||
|
||||
window.wailsloader.runtime = true;
|
||||
}
|
||||
93
v2/internal/runtime/js/core/store.js
Normal file
93
v2/internal/runtime/js/core/store.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
/**
|
||||
* Creates a new sync store with the given name and optional default value
|
||||
*
|
||||
* @export
|
||||
* @param {string} name
|
||||
* @param {*} optionalDefault
|
||||
*/
|
||||
export function New(name, optionalDefault) {
|
||||
|
||||
// This is the store state
|
||||
var state;
|
||||
|
||||
// Check we are initialised
|
||||
if( !window.wails) {
|
||||
throw Error('Wails is not initialised');
|
||||
}
|
||||
|
||||
// Store for the callbacks
|
||||
let callbacks = [];
|
||||
|
||||
// Subscribe to updates by providing a callback
|
||||
let subscribe = function(callback) {
|
||||
callbacks.push(callback);
|
||||
};
|
||||
|
||||
// sets the store state to the provided `newstate` value
|
||||
let set = function(newstate) {
|
||||
|
||||
state = newstate;
|
||||
|
||||
// Emit a notification to back end
|
||||
window.wails.Events.Emit('wails:sync:store:updatedbyfrontend:'+name, JSON.stringify(state));
|
||||
|
||||
// Notify callbacks
|
||||
callbacks.forEach( function(callback) {
|
||||
callback(state);
|
||||
});
|
||||
};
|
||||
|
||||
// update mutates the value in the store by calling the
|
||||
// provided method with the current value. The value returned
|
||||
// by the updater function will be set as the new store value
|
||||
let update = function(updater) {
|
||||
var newValue = updater(state);
|
||||
this.set(newValue);
|
||||
};
|
||||
|
||||
// get returns the current value of the store
|
||||
let get = function() {
|
||||
return state;
|
||||
};
|
||||
|
||||
// Setup event callback
|
||||
window.wails.Events.On('wails:sync:store:updatedbybackend:'+name, function(jsonEncodedState) {
|
||||
|
||||
// Parse state
|
||||
let newState = JSON.parse(jsonEncodedState);
|
||||
|
||||
// Todo: Potential preprocessing?
|
||||
|
||||
// Save state
|
||||
state = newState;
|
||||
|
||||
// Notify callbacks
|
||||
callbacks.forEach( function(callback) {
|
||||
callback(state);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Set to the optional default if set
|
||||
if( optionalDefault ) {
|
||||
this.set(optionalDefault);
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
get,
|
||||
set,
|
||||
update,
|
||||
};
|
||||
}
|
||||
15
v2/internal/runtime/js/desktop/common.js
Normal file
15
v2/internal/runtime/js/desktop/common.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
/**
|
||||
* Initialises platform specific code
|
||||
*/
|
||||
export const AppType = "desktop";
|
||||
44
v2/internal/runtime/js/desktop/darwin.js
Normal file
44
v2/internal/runtime/js/desktop/darwin.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
/**
|
||||
* Initialises platform specific code
|
||||
*/
|
||||
|
||||
// import * as common from './common';
|
||||
const common = require('./common');
|
||||
|
||||
export const System = {
|
||||
...common,
|
||||
Platform: () => "darwin",
|
||||
}
|
||||
|
||||
export function SendMessage(message) {
|
||||
window.webkit.messageHandlers.external.postMessage(message);
|
||||
}
|
||||
|
||||
export function Init() {
|
||||
|
||||
// Setup drag handler
|
||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||
window.addEventListener('mousedown', function (e) {
|
||||
var currentElement = e.target;
|
||||
while (currentElement != null) {
|
||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||
break;
|
||||
} else if (currentElement.hasAttribute('data-wails-drag')) {
|
||||
window.webkit.messageHandlers.windowDrag.postMessage(null);
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -14,8 +14,8 @@ The lightweight framework for web-like apps
|
||||
*/
|
||||
|
||||
export const System = {
|
||||
Platform: "linux",
|
||||
AppType: "desktop"
|
||||
...common,
|
||||
Platform: () => "linux",
|
||||
}
|
||||
|
||||
export function SendMessage(message) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# 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) 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) V2 project, not a standalone module.
|
||||
|
||||
@@ -10,28 +10,12 @@ The lightweight framework for web-like apps
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
/**
|
||||
* Opens the given URL in the system browser
|
||||
* Opens the given URL or Filename in the system browser
|
||||
*
|
||||
* @export
|
||||
* @param {string} url
|
||||
* @param {string} target
|
||||
* @returns
|
||||
*/
|
||||
function OpenURL(url) {
|
||||
return window.wails.Browser.OpenURL(url);
|
||||
export function Open(target) {
|
||||
return window.wails.Browser.Open(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
22
v2/internal/runtime/js/runtime/dialog.js
Normal file
22
v2/internal/runtime/js/runtime/dialog.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
|
||||
/**
|
||||
* Open a dialog with the given parameters
|
||||
*
|
||||
* @export
|
||||
* @param {object} options
|
||||
*/
|
||||
export function Open(options) {
|
||||
window.wails.Dialog.Open(options);
|
||||
}
|
||||
@@ -56,35 +56,9 @@ function Emit(eventName) {
|
||||
return window.wails.Events.Emit.apply(null, args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function Heartbeat(eventName, timeInMilliseconds, 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 = {
|
||||
OnMultiple: OnMultiple,
|
||||
On: On,
|
||||
Once: Once,
|
||||
Emit: Emit,
|
||||
Heartbeat: Heartbeat,
|
||||
Acknowledge: Acknowledge
|
||||
};
|
||||
@@ -11,6 +11,26 @@ The lightweight framework for web-like apps
|
||||
/* 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
|
||||
*
|
||||
@@ -54,17 +74,40 @@ function Error(message) {
|
||||
/**
|
||||
* Log the given fatal message with the backend
|
||||
*
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
function 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 = {
|
||||
Print: Print,
|
||||
Trace: Trace,
|
||||
Debug: Debug,
|
||||
Info: Info,
|
||||
Warning: Warning,
|
||||
Error: Error,
|
||||
Fatal: Fatal
|
||||
Fatal: Fatal,
|
||||
SetLogLevel: SetLogLevel,
|
||||
Level: Level,
|
||||
};
|
||||
|
||||
@@ -11,12 +11,18 @@ The lightweight framework for web-like apps
|
||||
|
||||
const Log = require('./log');
|
||||
const Browser = require('./browser');
|
||||
const Dialog = require('./dialog');
|
||||
const Events = require('./events');
|
||||
const Init = require('./init');
|
||||
const System = require('./system');
|
||||
const Store = require('./store');
|
||||
|
||||
module.exports = {
|
||||
Log: Log,
|
||||
Browser: Browser,
|
||||
Dialog: Dialog,
|
||||
Events: Events,
|
||||
Init: Init
|
||||
Init: Init,
|
||||
Log: Log,
|
||||
System: System,
|
||||
Store: Store,
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "1.0.10",
|
||||
"description": "Wails Javascript runtime library",
|
||||
"name": "@wails/runtime",
|
||||
"version": "1.1.0",
|
||||
"description": "Wails V2 Javascript runtime library",
|
||||
"main": "main.js",
|
||||
"types": "runtime.d.ts",
|
||||
"scripts": {
|
||||
|
||||
41
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
41
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
@@ -1,26 +1,49 @@
|
||||
export = wailsapp__runtime;
|
||||
|
||||
interface Store {
|
||||
get(): any;
|
||||
set(value: any): void;
|
||||
subscribe(callback: (newvalue: any) => void): void;
|
||||
update(callback: (currentvalue: any) => any): void;
|
||||
}
|
||||
|
||||
interface Level {
|
||||
TRACE: 1,
|
||||
DEBUG: 2,
|
||||
INFO: 3,
|
||||
WARNING: 4,
|
||||
ERROR: 5,
|
||||
};
|
||||
|
||||
declare const wailsapp__runtime: {
|
||||
Browser: {
|
||||
OpenFile(filename: string): Promise<any>;
|
||||
OpenURL(url: string): Promise<any>;
|
||||
Open(target: string): Promise<any>;
|
||||
};
|
||||
Events: {
|
||||
Acknowledge(eventName: string): void;
|
||||
Emit(eventName: string): void;
|
||||
Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
|
||||
On(eventName: string, callback: () => void): void;
|
||||
OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
|
||||
Once(eventName: string, callback: () => void): void;
|
||||
Emit(eventName: string, data?: any): void;
|
||||
On(eventName: string, callback: (data?: any) => void): void;
|
||||
OnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void;
|
||||
Once(eventName: string, callback: (data?: any) => void): void;
|
||||
};
|
||||
Init(callback: () => void): void;
|
||||
// Init(callback: () => void): void;
|
||||
Log: {
|
||||
Debug(message: string): void;
|
||||
Error(message: string): void;
|
||||
Fatal(message: string): void;
|
||||
Info(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
|
||||
};
|
||||
Store: {
|
||||
New(name: string, defaultValue?: any): Store;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
27
v2/internal/runtime/js/runtime/store.js
Normal file
27
v2/internal/runtime/js/runtime/store.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
|
||||
/**
|
||||
* Create a new Store with the given name and optional default value
|
||||
*
|
||||
* @export
|
||||
* @param {string} name
|
||||
* @param {*} optionalDefault
|
||||
*/
|
||||
function New(name, optionalDefault) {
|
||||
return window.wails.Store.New(name, optionalDefault);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
New: New,
|
||||
};
|
||||
42
v2/internal/runtime/js/runtime/system.js
Normal file
42
v2/internal/runtime/js/runtime/system.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
const Events = require('./events');
|
||||
|
||||
/**
|
||||
* Registers an event listener that will be invoked when the user changes the
|
||||
* desktop theme (light mode / dark mode). The callback receives a boolean which
|
||||
* indicates if dark mode is enabled.
|
||||
*
|
||||
* @export
|
||||
* @param {function} callback The callback to invoke on theme change
|
||||
*/
|
||||
function OnThemeChange(callback) {
|
||||
Events.On("wails:system:themechange", callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if dark mode is curently enabled.
|
||||
*
|
||||
* @export
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function DarkModeEnabled() {
|
||||
return window.wails.System.IsDarkMode.get();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
OnThemeChange: OnThemeChange,
|
||||
DarkModeEnabled: DarkModeEnabled,
|
||||
LogLevel: window.wails.System.LogLevel,
|
||||
Platform: window.wails.System.Platform,
|
||||
AppType: window.wails.System.AppType
|
||||
};
|
||||
21
v2/internal/runtime/js/server/darwin.js
Normal file
21
v2/internal/runtime/js/server/darwin.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
_ __ _ __
|
||||
| | / /___ _(_) /____
|
||||
| | /| / / __ `/ / / ___/
|
||||
| |/ |/ / /_/ / / (__ )
|
||||
|__/|__/\__,_/_/_/____/
|
||||
The lightweight framework for web-like apps
|
||||
(c) Lea Anthony 2019-present
|
||||
*/
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
/**
|
||||
* Initialises platform specific code
|
||||
*/
|
||||
|
||||
export const System = {
|
||||
Platform: "darwin",
|
||||
AppType: "server"
|
||||
}
|
||||
|
||||
export function Init() { }
|
||||
69
v2/internal/runtime/log.go
Normal file
69
v2/internal/runtime/log.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
)
|
||||
|
||||
// Log defines all Log related operations
|
||||
type Log interface {
|
||||
Print(message string)
|
||||
Trace(message string)
|
||||
Debug(message string)
|
||||
Info(message string)
|
||||
Warning(message string)
|
||||
Error(message string)
|
||||
Fatal(message string)
|
||||
SetLogLevel(level logger.LogLevel)
|
||||
}
|
||||
|
||||
type log struct {
|
||||
bus *servicebus.ServiceBus
|
||||
}
|
||||
|
||||
// newLog creates a new Log struct
|
||||
func newLog(bus *servicebus.ServiceBus) Log {
|
||||
return &log{
|
||||
bus: bus,
|
||||
}
|
||||
}
|
||||
|
||||
// Print prints a Print level message
|
||||
func (r *log) Print(message string) {
|
||||
r.bus.Publish("log:print", message)
|
||||
}
|
||||
|
||||
// Trace prints a Trace level message
|
||||
func (r *log) Trace(message string) {
|
||||
r.bus.Publish("log:trace", message)
|
||||
}
|
||||
|
||||
// Debug prints a Debug level message
|
||||
func (r *log) Debug(message string) {
|
||||
r.bus.Publish("log:debug", message)
|
||||
}
|
||||
|
||||
// Info prints a Info level message
|
||||
func (r *log) Info(message string) {
|
||||
r.bus.Publish("log:info", message)
|
||||
}
|
||||
|
||||
// Warning prints a Warning level message
|
||||
func (r *log) Warning(message string) {
|
||||
r.bus.Publish("log:warning", message)
|
||||
}
|
||||
|
||||
// Error prints a Error level message
|
||||
func (r *log) Error(message string) {
|
||||
r.bus.Publish("log:error", message)
|
||||
}
|
||||
|
||||
// Fatal prints a Fatal level message
|
||||
func (r *log) Fatal(message string) {
|
||||
r.bus.Publish("log:fatal", message)
|
||||
}
|
||||
|
||||
// Sets the log level
|
||||
func (r *log) SetLogLevel(level logger.LogLevel) {
|
||||
r.bus.Publish("log:setlevel", level)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package goruntime
|
||||
package runtime
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
|
||||
@@ -8,18 +8,25 @@ type Runtime struct {
|
||||
Events Events
|
||||
Window Window
|
||||
Dialog Dialog
|
||||
System System
|
||||
Store *StoreProvider
|
||||
Log Log
|
||||
bus *servicebus.ServiceBus
|
||||
}
|
||||
|
||||
// New creates a new runtime
|
||||
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||
return &Runtime{
|
||||
result := &Runtime{
|
||||
Browser: newBrowser(),
|
||||
Events: newEvents(serviceBus),
|
||||
Window: newWindow(serviceBus),
|
||||
Dialog: newDialog(serviceBus),
|
||||
System: newSystem(serviceBus),
|
||||
Log: newLog(serviceBus),
|
||||
bus: serviceBus,
|
||||
}
|
||||
result.Store = newStore(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// Quit the application
|
||||
295
v2/internal/runtime/store.go
Normal file
295
v2/internal/runtime/store.go
Normal file
@@ -0,0 +1,295 @@
|
||||
// package runtime contains all the methods and data structures related to the
|
||||
// runtime library of Wails. This includes both Go and JS runtimes.
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Options defines the optional data that may be used
|
||||
// when creating a Store
|
||||
type Options struct {
|
||||
|
||||
// The name of the store
|
||||
Name string
|
||||
|
||||
// The runtime to attach the store to
|
||||
Runtime *Runtime
|
||||
|
||||
// Indicates if notifying Go listeners should be notified of updates
|
||||
// synchronously (on the current thread) or asynchronously using
|
||||
// goroutines
|
||||
NotifySynchronously bool
|
||||
}
|
||||
|
||||
// StoreProvider is a struct that creates Stores
|
||||
type StoreProvider struct {
|
||||
runtime *Runtime
|
||||
}
|
||||
|
||||
// newStore creates new stores using the provided Runtime reference.
|
||||
func newStore(runtime *Runtime) *StoreProvider {
|
||||
return &StoreProvider{
|
||||
runtime: runtime,
|
||||
}
|
||||
}
|
||||
|
||||
// Store is where we keep named data
|
||||
type Store struct {
|
||||
name string
|
||||
data reflect.Value
|
||||
dataType reflect.Type
|
||||
eventPrefix string
|
||||
callbacks []reflect.Value
|
||||
runtime *Runtime
|
||||
notifySynchronously bool
|
||||
|
||||
// Lock
|
||||
mux sync.Mutex
|
||||
|
||||
// Error handler
|
||||
errorHandler func(error)
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// New creates a new store
|
||||
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
||||
|
||||
dataType := reflect.TypeOf(defaultValue)
|
||||
|
||||
result := Store{
|
||||
name: name,
|
||||
runtime: p.runtime,
|
||||
data: reflect.ValueOf(defaultValue),
|
||||
dataType: dataType,
|
||||
}
|
||||
|
||||
// Setup the sync listener
|
||||
result.setupListener()
|
||||
|
||||
return &result
|
||||
}
|
||||
|
||||
// OnError takes a function that will be called
|
||||
// whenever an error occurs
|
||||
func (s *Store) OnError(callback func(error)) {
|
||||
s.errorHandler = callback
|
||||
}
|
||||
|
||||
// Processes the updates sent by the front end
|
||||
func (s *Store) processUpdatedData(data string) error {
|
||||
|
||||
// Decode incoming data
|
||||
var rawdata json.RawMessage
|
||||
d := json.NewDecoder(bytes.NewBufferString(data))
|
||||
err := d.Decode(&rawdata)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create a new instance of our data and unmarshal
|
||||
// the received value into it
|
||||
newData := reflect.New(s.dataType).Interface()
|
||||
err = json.Unmarshal(rawdata, &newData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock mutex for writing
|
||||
s.mux.Lock()
|
||||
|
||||
// Handle nulls
|
||||
if newData == nil {
|
||||
s.data = reflect.Zero(s.dataType)
|
||||
} else {
|
||||
// Store the resultant value in the data store
|
||||
s.data = reflect.ValueOf(newData).Elem()
|
||||
}
|
||||
|
||||
// Unlock mutex
|
||||
s.mux.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setup listener for front end changes
|
||||
func (s *Store) setupListener() {
|
||||
|
||||
// Listen for updates from the front end
|
||||
s.runtime.Events.On("wails:sync:store:updatedbyfrontend:"+s.name, func(data ...interface{}) {
|
||||
|
||||
// Process the incoming data
|
||||
err := s.processUpdatedData(data[0].(string))
|
||||
|
||||
if err != nil {
|
||||
if s.errorHandler != nil {
|
||||
s.errorHandler(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Notify listeners
|
||||
s.notify()
|
||||
})
|
||||
}
|
||||
|
||||
// notify the listeners of the current data state
|
||||
func (s *Store) notify() {
|
||||
|
||||
// Execute callbacks
|
||||
for _, callback := range s.callbacks {
|
||||
|
||||
// Build args
|
||||
args := []reflect.Value{s.data}
|
||||
|
||||
if s.notifySynchronously {
|
||||
callback.Call(args)
|
||||
} else {
|
||||
go callback.Call(args)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Set will update the data held by the store
|
||||
// and notify listeners of the change
|
||||
func (s *Store) Set(data interface{}) error {
|
||||
|
||||
inType := reflect.TypeOf(data)
|
||||
|
||||
if inType != s.dataType {
|
||||
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
|
||||
}
|
||||
|
||||
// Save data
|
||||
s.mux.Lock()
|
||||
s.data = reflect.ValueOf(data)
|
||||
s.mux.Unlock()
|
||||
|
||||
// Stringify data
|
||||
newdata, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
if s.errorHandler != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Emit event to front end
|
||||
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, string(newdata))
|
||||
|
||||
// Notify subscribers
|
||||
s.notify()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// callbackCheck ensures the given function to Subscribe() is
|
||||
// of the correct signature. Absolutely cannot wait for
|
||||
// generics to land rather than writing this nonsense.
|
||||
func (s *Store) callbackCheck(callback interface{}) error {
|
||||
|
||||
// Get type
|
||||
callbackType := reflect.TypeOf(callback)
|
||||
|
||||
// Check callback is a function
|
||||
if callbackType.Kind() != reflect.Func {
|
||||
return fmt.Errorf("invalid value given to store.Subscribe(). Expected 'func(%s)'", s.dataType.String())
|
||||
}
|
||||
|
||||
// Check input param
|
||||
if callbackType.NumIn() != 1 {
|
||||
return fmt.Errorf("invalid number of parameters given in callback function. Expected 1")
|
||||
}
|
||||
|
||||
// Check input data type
|
||||
if callbackType.In(0) != s.dataType {
|
||||
return fmt.Errorf("invalid type for input parameter given in callback function. Expected %s, got %s", s.dataType.String(), callbackType.In(0))
|
||||
}
|
||||
|
||||
// Check output param
|
||||
if callbackType.NumOut() != 0 {
|
||||
return fmt.Errorf("invalid number of return parameters given in callback function. Expected 0")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Subscribe will subscribe to updates to the store by
|
||||
// providing a callback. Any updates to the store are sent
|
||||
// to the callback
|
||||
func (s *Store) Subscribe(callback interface{}) {
|
||||
|
||||
err := s.callbackCheck(callback)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
callbackFunc := reflect.ValueOf(callback)
|
||||
|
||||
s.callbacks = append(s.callbacks, callbackFunc)
|
||||
}
|
||||
|
||||
// updaterCheck ensures the given function to Update() is
|
||||
// of the correct signature. Absolutely cannot wait for
|
||||
// generics to land rather than writing this nonsense.
|
||||
func (s *Store) updaterCheck(updater interface{}) error {
|
||||
|
||||
// Get type
|
||||
updaterType := reflect.TypeOf(updater)
|
||||
|
||||
// Check updater is a function
|
||||
if updaterType.Kind() != reflect.Func {
|
||||
return fmt.Errorf("invalid value given to store.Update(). Expected 'func(%s) %s'", s.dataType.String(), s.dataType.String())
|
||||
}
|
||||
|
||||
// Check input param
|
||||
if updaterType.NumIn() != 1 {
|
||||
return fmt.Errorf("invalid number of parameters given in updater function. Expected 1")
|
||||
}
|
||||
|
||||
// Check input data type
|
||||
if updaterType.In(0) != s.dataType {
|
||||
return fmt.Errorf("invalid type for input parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.In(0))
|
||||
}
|
||||
|
||||
// Check output param
|
||||
if updaterType.NumOut() != 1 {
|
||||
return fmt.Errorf("invalid number of return parameters given in updater function. Expected 1")
|
||||
}
|
||||
|
||||
// Check output data type
|
||||
if updaterType.Out(0) != s.dataType {
|
||||
return fmt.Errorf("invalid type for return parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.Out(0))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update takes a function that is passed the current state.
|
||||
// The result of that function is then set as the new state
|
||||
// of the store. This will notify listeners of the change
|
||||
func (s *Store) Update(updater interface{}) {
|
||||
|
||||
err := s.updaterCheck(updater)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
// Build args
|
||||
args := []reflect.Value{s.data}
|
||||
|
||||
// Make call
|
||||
results := reflect.ValueOf(updater).Call(args)
|
||||
|
||||
// We will only have 1 result. Set the store to it
|
||||
s.Set(results[0].Interface())
|
||||
}
|
||||
59
v2/internal/runtime/system.go
Normal file
59
v2/internal/runtime/system.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
// System defines all System related operations
|
||||
type System interface {
|
||||
IsDarkMode() bool
|
||||
Platform() string
|
||||
AppType() string
|
||||
}
|
||||
|
||||
// system exposes the System interface
|
||||
type system struct {
|
||||
bus *servicebus.ServiceBus
|
||||
}
|
||||
|
||||
// newSystem creates a new System struct
|
||||
func newSystem(bus *servicebus.ServiceBus) System {
|
||||
return &system{
|
||||
bus: bus,
|
||||
}
|
||||
}
|
||||
|
||||
// Platform returns the platform name the application
|
||||
// is running on
|
||||
func (r *system) Platform() string {
|
||||
return runtime.GOOS
|
||||
}
|
||||
|
||||
// On pass through
|
||||
func (r *system) IsDarkMode() bool {
|
||||
|
||||
// Create unique system callback
|
||||
uniqueCallback := crypto.RandomID()
|
||||
|
||||
// Subscribe to the respose channel
|
||||
responseTopic := "systemresponse:" + uniqueCallback
|
||||
systemResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||
}
|
||||
|
||||
message := "system:isdarkmode:" + uniqueCallback
|
||||
r.bus.Publish(message, nil)
|
||||
|
||||
// Wait for result
|
||||
var result *servicebus.Message = <-systemResponseChannel
|
||||
|
||||
// Delete subscription to response topic
|
||||
r.bus.UnSubscribe(responseTopic)
|
||||
|
||||
return result.Data().(bool)
|
||||
}
|
||||
8
v2/internal/runtime/system_desktop.go
Normal file
8
v2/internal/runtime/system_desktop.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build desktop,!server
|
||||
|
||||
package runtime
|
||||
|
||||
// AppType returns the application type, EG: desktop
|
||||
func (r *system) AppType() string {
|
||||
return "desktop"
|
||||
}
|
||||
8
v2/internal/runtime/system_server.go
Normal file
8
v2/internal/runtime/system_server.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build server
|
||||
|
||||
package runtime
|
||||
|
||||
// AppType returns the application type, EG: desktop
|
||||
func (r *system) AppType() string {
|
||||
return "server"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package goruntime
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
@@ -16,7 +16,7 @@ type Window interface {
|
||||
SetTitle(title string)
|
||||
Fullscreen()
|
||||
UnFullscreen()
|
||||
SetColour(colour string)
|
||||
SetColour(colour int)
|
||||
}
|
||||
|
||||
// Window exposes the Windows interface
|
||||
@@ -54,8 +54,8 @@ func (w *window) UnFullscreen() {
|
||||
w.bus.Publish("window:unfullscreen", "")
|
||||
}
|
||||
|
||||
// SetColour sets the window colour to the given string
|
||||
func (w *window) SetColour(colour string) {
|
||||
// SetColour sets the window colour to the given int
|
||||
func (w *window) SetColour(colour int) {
|
||||
w.bus.Publish("window:setcolour", colour)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package subsystem
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime/goruntime"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
@@ -21,11 +21,11 @@ type Binding struct {
|
||||
logger logger.CustomLogger
|
||||
|
||||
// runtime
|
||||
runtime *goruntime.Runtime
|
||||
runtime *runtime.Runtime
|
||||
}
|
||||
|
||||
// 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 *goruntime.Runtime) (*Binding, error) {
|
||||
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
|
||||
@@ -3,10 +3,12 @@ package subsystem
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/binding"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
@@ -25,10 +27,13 @@ type Call struct {
|
||||
|
||||
// logger
|
||||
logger logger.CustomLogger
|
||||
|
||||
// runtime
|
||||
runtime *runtime.Runtime
|
||||
}
|
||||
|
||||
// NewCall creates a new log subsystem
|
||||
func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB) (*Call, error) {
|
||||
// NewCall creates a new call subsystem
|
||||
func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
|
||||
|
||||
// Register quit channel
|
||||
quitChannel, err := bus.Subscribe("quit")
|
||||
@@ -48,6 +53,7 @@ func NewCall(bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB)
|
||||
logger: logger.CustomLogger("Call Subsystem"),
|
||||
DB: DB,
|
||||
bus: bus,
|
||||
runtime: runtime,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -65,7 +71,7 @@ func (c *Call) Start() error {
|
||||
case <-c.quitChannel:
|
||||
c.running = false
|
||||
case callMessage := <-c.callChannel:
|
||||
|
||||
// TODO: Check if this works ok in a goroutine
|
||||
c.processCall(callMessage)
|
||||
}
|
||||
}
|
||||
@@ -87,6 +93,12 @@ func (c *Call) processCall(callMessage *servicebus.Message) {
|
||||
// Lookup method
|
||||
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
|
||||
if registeredMethod == nil {
|
||||
c.sendError(fmt.Errorf("Method not registered"), payload, callMessage.Target())
|
||||
@@ -94,7 +106,12 @@ func (c *Call) processCall(callMessage *servicebus.Message) {
|
||||
}
|
||||
c.logger.Trace("Got registered method: %+v", registeredMethod)
|
||||
|
||||
result, err := registeredMethod.Call(payload.Args)
|
||||
args, err := registeredMethod.ParseArgs(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 {
|
||||
c.sendError(err, payload, callMessage.Target())
|
||||
return
|
||||
@@ -105,6 +122,16 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
message := &CallbackMessage{
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// means it does not expire (default).
|
||||
type eventListener struct {
|
||||
callback func(...interface{}) // Function to call with emitted event data
|
||||
counter int64 // The number of times this callback may be called. -1 = infinite
|
||||
counter int // The number of times this callback may be called. -1 = infinite
|
||||
delete bool // Flag to indicate that this listener should be deleted
|
||||
}
|
||||
|
||||
@@ -60,12 +60,12 @@ func NewEvent(bus *servicebus.ServiceBus, logger *logger.Logger) (*Event, error)
|
||||
}
|
||||
|
||||
// RegisterListener provides a means of subscribing to events of type "eventName"
|
||||
func (e *Event) RegisterListener(eventName string, callback func(...interface{})) {
|
||||
func (e *Event) RegisterListener(eventName string, callback func(...interface{}), counter int) {
|
||||
|
||||
// Create new eventListener
|
||||
thisListener := &eventListener{
|
||||
callback: callback,
|
||||
counter: 0,
|
||||
counter: counter,
|
||||
delete: false,
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (e *Event) Start() error {
|
||||
var message *message.OnEventMessage = eventMessage.Data().(*message.OnEventMessage)
|
||||
eventName := message.Name
|
||||
callback := message.Callback
|
||||
e.RegisterListener(eventName, callback)
|
||||
e.RegisterListener(eventName, callback, message.Counter)
|
||||
e.logger.Trace("Registered listener for event '%s' with callback %p", eventName, callback)
|
||||
default:
|
||||
e.logger.Error("unknown event message: %+v", eventMessage)
|
||||
@@ -141,7 +141,7 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
||||
// Get list of event listeners
|
||||
listeners := e.listeners[eventName]
|
||||
if listeners == nil {
|
||||
println("no listeners for", eventName)
|
||||
e.logger.Trace("No listeners for %s", eventName)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,13 +179,16 @@ func (e *Event) notifyListeners(eventName string, message *message.EventMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// Save new listeners
|
||||
e.listeners[eventName] = newListeners
|
||||
// Save new listeners or remove entry
|
||||
if len(newListeners) > 0 {
|
||||
e.listeners[eventName] = newListeners
|
||||
} else {
|
||||
delete(e.listeners, eventName)
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock
|
||||
e.notifyLock.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (e *Event) shutdown() {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
@@ -16,10 +18,13 @@ type Log struct {
|
||||
|
||||
// Logger!
|
||||
logger *logger.Logger
|
||||
|
||||
// Loglevel store
|
||||
logLevelStore *runtime.Store
|
||||
}
|
||||
|
||||
// NewLog creates a new log subsystem
|
||||
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) {
|
||||
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *runtime.Store) (*Log, error) {
|
||||
|
||||
// Subscribe to log messages
|
||||
logChannel, err := bus.Subscribe("log")
|
||||
@@ -34,9 +39,10 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) {
|
||||
}
|
||||
|
||||
result := &Log{
|
||||
logChannel: logChannel,
|
||||
quitChannel: quitChannel,
|
||||
logger: logger,
|
||||
logChannel: logChannel,
|
||||
quitChannel: quitChannel,
|
||||
logger: logger,
|
||||
logLevelStore: logLevelStore,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -57,6 +63,10 @@ func (l *Log) Start() error {
|
||||
case logMessage := <-l.logChannel:
|
||||
logType := strings.TrimPrefix(logMessage.Topic(), "log:")
|
||||
switch logType {
|
||||
case "print":
|
||||
l.logger.Print(logMessage.Data().(string))
|
||||
case "trace":
|
||||
l.logger.Trace(logMessage.Data().(string))
|
||||
case "debug":
|
||||
l.logger.Debug(logMessage.Data().(string))
|
||||
case "info":
|
||||
@@ -67,6 +77,22 @@ func (l *Log) Start() error {
|
||||
l.logger.Error(logMessage.Data().(string))
|
||||
case "fatal":
|
||||
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:
|
||||
l.logger.Error("unknown log message: %+v", logMessage)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime/goruntime"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ type Runtime struct {
|
||||
logger logger.CustomLogger
|
||||
|
||||
// Runtime library
|
||||
runtime *goruntime.Runtime
|
||||
runtime *runtime.Runtime
|
||||
}
|
||||
|
||||
// NewRuntime creates a new runtime subsystem
|
||||
@@ -32,7 +32,7 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger) (*Runtime, er
|
||||
}
|
||||
|
||||
// Subscribe to log messages
|
||||
runtimeChannel, err := bus.Subscribe("runtime")
|
||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -41,7 +41,7 @@ func NewRuntime(bus *servicebus.ServiceBus, logger *logger.Logger) (*Runtime, er
|
||||
quitChannel: quitChannel,
|
||||
runtimeChannel: runtimeChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: goruntime.New(bus),
|
||||
runtime: runtime.New(bus),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -75,7 +75,7 @@ func (r *Runtime) Start() error {
|
||||
case "browser":
|
||||
err = r.processBrowserMessage(method, runtimeMessage.Data())
|
||||
default:
|
||||
fmt.Errorf("unknown log message: %+v", runtimeMessage)
|
||||
fmt.Errorf("unknown runtime message: %+v", runtimeMessage)
|
||||
}
|
||||
|
||||
// If we had an error, log it
|
||||
@@ -93,7 +93,7 @@ func (r *Runtime) Start() error {
|
||||
}
|
||||
|
||||
// GoRuntime returns the Go Runtime object
|
||||
func (r *Runtime) GoRuntime() *goruntime.Runtime {
|
||||
func (r *Runtime) GoRuntime() *runtime.Runtime {
|
||||
return r.runtime
|
||||
}
|
||||
|
||||
@@ -103,12 +103,12 @@ func (r *Runtime) shutdown() {
|
||||
|
||||
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
|
||||
switch method {
|
||||
case "openurl":
|
||||
url, ok := data.(string)
|
||||
case "open":
|
||||
target, ok := data.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected 1 string parameter for runtime:browser:openurl")
|
||||
return fmt.Errorf("expected 1 string parameter for runtime:browser:open")
|
||||
}
|
||||
go r.runtime.Browser.Open(url)
|
||||
go r.runtime.Browser.Open(target)
|
||||
default:
|
||||
return fmt.Errorf("unknown method runtime:browser:%s", method)
|
||||
}
|
||||
|
||||
30
v2/internal/system/operatingsystem/os_darwin.go
Normal file
30
v2/internal/system/operatingsystem/os_darwin.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package operatingsystem
|
||||
|
||||
import "github.com/wailsapp/wails/v2/internal/shell"
|
||||
|
||||
func platformInfo() (*OS, error) {
|
||||
// Default value
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "MacOS"
|
||||
result.Version = "Unknown"
|
||||
|
||||
stdout, stderr, err := shell.RunCommand(".", "sysctl", "kern.osrelease")
|
||||
println(stdout)
|
||||
println(stderr)
|
||||
println(err)
|
||||
// cmd := CreateCommand(directory, command, args...)
|
||||
// var stdo, stde bytes.Buffer
|
||||
// cmd.Stdout = &stdo
|
||||
// cmd.Stderr = &stde
|
||||
// err := cmd.Run()
|
||||
// return stdo.String(), stde.String(), err
|
||||
// }
|
||||
// sysctl := shell.NewCommand("sysctl")
|
||||
// kern.ostype: Darwin
|
||||
// kern.osrelease: 20.1.0
|
||||
// kern.osrevision: 199506
|
||||
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
29
v2/internal/system/operatingsystem/os_windows.go
Normal file
29
v2/internal/system/operatingsystem/os_windows.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package operatingsystem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
func platformInfo() (*OS, error) {
|
||||
// Default value
|
||||
var result OS
|
||||
result.ID = "Unknown"
|
||||
result.Name = "Windows"
|
||||
result.Version = "Unknown"
|
||||
|
||||
// Credit: https://stackoverflow.com/a/33288328
|
||||
// Ignore errors as it isn't a showstopper
|
||||
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
|
||||
defer key.Close()
|
||||
|
||||
fmt.Printf("%+v\n", key)
|
||||
|
||||
// Ignore errors as it isn't a showstopper
|
||||
productName, _, _ := key.GetStringValue("ProductName")
|
||||
fmt.Println(productName)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -9,24 +9,6 @@ import (
|
||||
"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
|
||||
var pmcommands = []string{
|
||||
@@ -38,8 +20,6 @@ var pmcommands = []string{
|
||||
"zypper",
|
||||
}
|
||||
|
||||
type packagemap = map[string][]*Package
|
||||
|
||||
// Find will attempt to find the system package manager
|
||||
func Find(osid string) PackageManager {
|
||||
|
||||
@@ -70,58 +50,6 @@ func newPackageManager(pmname string, osid string) PackageManager {
|
||||
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
|
||||
// Returns a list of dependancies search for, whether they were found
|
||||
// and whether they were installed
|
||||
|
||||
77
v2/internal/system/packagemanager/pm.go
Normal file
77
v2/internal/system/packagemanager/pm.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package packagemanager
|
||||
|
||||
// Package contains information about a system package
|
||||
type Package struct {
|
||||
Name string
|
||||
Version string
|
||||
InstallCommand map[string]string
|
||||
SystemPackage bool
|
||||
Library bool
|
||||
Optional bool
|
||||
}
|
||||
|
||||
type packagemap = map[string][]*Package
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
8
v2/internal/system/system_darwin.go
Normal file
8
v2/internal/system/system_darwin.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// +build darwin
|
||||
|
||||
package system
|
||||
|
||||
func (i *Info) discover() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -2,14 +2,20 @@
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
import "github.com/wailsapp/wails/v2/internal/system/operatingsystem"
|
||||
|
||||
func (i *Info) discover() {
|
||||
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))
|
||||
func (i *Info) discover() error {
|
||||
|
||||
var err error
|
||||
osinfo, err := operatingsystem.Info()
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
27
v2/internal/templates/ides/vscode/launch.json.tmpl
Normal file
27
v2/internal/templates/ides/vscode/launch.json.tmpl
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Wails: Debug {{.ProjectName}} (Desktop)",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/{{.PathToDesktopBinary}}",
|
||||
"preLaunchTask": "build_desktop",
|
||||
"cwd": "",
|
||||
"env": {},
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "Wails: Debug {{.ProjectName}} (Server)",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/{{.PathToServerBinary}}",
|
||||
"preLaunchTask": "build_server",
|
||||
"cwd": "",
|
||||
"env": {},
|
||||
"args": []
|
||||
},
|
||||
]
|
||||
}
|
||||
23
v2/internal/templates/ides/vscode/tasks.json.tmpl
Normal file
23
v2/internal/templates/ides/vscode/tasks.json.tmpl
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build_desktop",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "{{.TargetDir}}"
|
||||
},
|
||||
"command": "wails build"
|
||||
},
|
||||
{
|
||||
"label": "build_server",
|
||||
"type": "shell",
|
||||
"options": {
|
||||
"cwd": "{{.TargetDir}}"
|
||||
},
|
||||
"command": "wails build -t server"
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// Cahce for the templates
|
||||
@@ -31,11 +32,14 @@ type Data struct {
|
||||
|
||||
// Options for installing a template
|
||||
type Options struct {
|
||||
ProjectName string
|
||||
TemplateName string
|
||||
BinaryName string
|
||||
TargetDir string
|
||||
Logger *logger.Logger
|
||||
ProjectName string
|
||||
TemplateName string
|
||||
BinaryName string
|
||||
TargetDir string
|
||||
Logger *clilogger.CLILogger
|
||||
GenerateVSCode bool
|
||||
PathToDesktopBinary string
|
||||
PathToServerBinary string
|
||||
}
|
||||
|
||||
// Template holds data relating to a template
|
||||
@@ -162,9 +166,24 @@ func Install(options *Options) error {
|
||||
}
|
||||
|
||||
// Did the user want to install in current directory?
|
||||
if options.TargetDir == "." {
|
||||
// Yes - use cwd
|
||||
options.TargetDir = cwd
|
||||
if options.TargetDir == "" {
|
||||
|
||||
// If the current directory is empty, use it
|
||||
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 {
|
||||
// Get the absolute path of the given directory
|
||||
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
||||
@@ -213,35 +232,74 @@ func Install(options *Options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate the directory name
|
||||
err = generateIDEFiles(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OutputList prints the list of available tempaltes to the given logger
|
||||
func OutputList(logger *logger.Logger) error {
|
||||
func OutputList(logger *clilogger.CLILogger) error {
|
||||
templates, err := List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, writer := range logger.Writers() {
|
||||
table := tablewriter.NewWriter(writer)
|
||||
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding("\t") // pad with tabs
|
||||
table.SetNoWhiteSpace(true)
|
||||
for _, template := range templates {
|
||||
table.Append([]string{template.Name, template.ShortName, template.Description})
|
||||
}
|
||||
table.Render()
|
||||
table := tablewriter.NewWriter(logger.Writer)
|
||||
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetTablePadding("\t") // pad with tabs
|
||||
table.SetNoWhiteSpace(true)
|
||||
for _, template := range templates {
|
||||
table.Append([]string{template.Name, template.ShortName, template.Description})
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -34,18 +34,8 @@ func (wc *WebClient) CallResult(message string) {
|
||||
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 {
|
||||
// OpenDialog is a noop in the webclient
|
||||
func (wc *WebClient) OpenDialog(title string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -59,8 +49,7 @@ func (wc *WebClient) WindowFullscreen() {}
|
||||
func (wc *WebClient) WindowUnFullscreen() {}
|
||||
|
||||
// WindowSetColour is a noop in the webclient
|
||||
func (wc *WebClient) WindowSetColour(colour string) bool {
|
||||
return false
|
||||
func (wc *WebClient) WindowSetColour(colour int) {
|
||||
}
|
||||
|
||||
// Run processes messages from the remote webclient
|
||||
|
||||
59
v2/pkg/clilogger/clilogger.go
Normal file
59
v2/pkg/clilogger/clilogger.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package clilogger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// CLILogger is used by the cli
|
||||
type CLILogger struct {
|
||||
Writer io.Writer
|
||||
mute bool
|
||||
}
|
||||
|
||||
// New cli logger
|
||||
func New(writer io.Writer) *CLILogger {
|
||||
return &CLILogger{
|
||||
Writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
// Mute sets whether the logger should be muted
|
||||
func (c *CLILogger) Mute(value bool) {
|
||||
c.mute = value
|
||||
}
|
||||
|
||||
// Print works like Printf
|
||||
func (c *CLILogger) Print(message string, args ...interface{}) {
|
||||
if c.mute {
|
||||
return
|
||||
}
|
||||
|
||||
_, err := fmt.Fprintf(c.Writer, message, args...)
|
||||
if err != nil {
|
||||
c.Fatal("Fatal: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Println works like Printf but with a line ending
|
||||
func (c *CLILogger) Println(message string, args ...interface{}) {
|
||||
if c.mute {
|
||||
return
|
||||
}
|
||||
temp := fmt.Sprintf(message, args...)
|
||||
_, err := fmt.Fprintln(c.Writer, temp)
|
||||
if err != nil {
|
||||
c.Fatal("Fatal: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal prints the given message then aborts
|
||||
func (c *CLILogger) Fatal(message string, args ...interface{}) {
|
||||
temp := fmt.Sprintf(message, args...)
|
||||
_, err := fmt.Fprintln(c.Writer, "FATAL: "+temp)
|
||||
if err != nil {
|
||||
println("FATAL: ", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"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/shell"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// BaseBuilder is the common builder struct
|
||||
@@ -305,7 +305,7 @@ func (b *BaseBuilder) NpmRunWithEnvironment(projectDir, buildTarget string, verb
|
||||
}
|
||||
|
||||
// BuildFrontend executes the `npm build` command for the frontend directory
|
||||
func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
|
||||
func (b *BaseBuilder) BuildFrontend(outputLogger *clilogger.CLILogger) error {
|
||||
verbose := false
|
||||
|
||||
frontendDir := filepath.Join(b.projectData.Path, "frontend")
|
||||
@@ -313,10 +313,10 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
|
||||
// Check there is an 'InstallCommand' provided in wails.json
|
||||
if b.projectData.InstallCommand == "" {
|
||||
// No - don't install
|
||||
outputLogger.Writeln(" - No Install command. Skipping.")
|
||||
outputLogger.Println(" - No Install command. Skipping.")
|
||||
} else {
|
||||
// Do install if needed
|
||||
outputLogger.Writeln(" - Installing dependencies...")
|
||||
outputLogger.Println(" - Installing dependencies...")
|
||||
if err := b.NpmInstallUsingCommand(frontendDir, b.projectData.InstallCommand); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -324,12 +324,12 @@ func (b *BaseBuilder) BuildFrontend(outputLogger *logger.Logger) error {
|
||||
|
||||
// Check if there is a build command
|
||||
if b.projectData.BuildCommand == "" {
|
||||
outputLogger.Writeln(" - No Build command. Skipping.")
|
||||
outputLogger.Println(" - No Build command. Skipping.")
|
||||
// No - ignore
|
||||
return nil
|
||||
}
|
||||
|
||||
outputLogger.Writeln(" - Compiling Frontend Project")
|
||||
outputLogger.Println(" - Compiling Frontend Project")
|
||||
cmd := strings.Split(b.projectData.BuildCommand, " ")
|
||||
stdout, stderr, err := shell.RunCommand(frontendDir, cmd[0], cmd[1:]...)
|
||||
if verbose || err != nil {
|
||||
|
||||
@@ -6,8 +6,9 @@ import (
|
||||
"runtime"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/project"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
// Mode is the type used to indicate the build modes
|
||||
@@ -24,16 +25,16 @@ var modeMap = []string{"Debug", "Production"}
|
||||
|
||||
// Options contains all the build options as well as the project data
|
||||
type Options struct {
|
||||
LDFlags string // Optional flags to pass to linker
|
||||
Logger *logger.Logger // All output to the logger
|
||||
OutputType string // EG: desktop, server....
|
||||
Mode Mode // release or debug
|
||||
ProjectData *project.Project // The project data
|
||||
Pack bool // Create a package for the app after building
|
||||
Platform string // The platform to build for
|
||||
Compiler string // The compiler command to use
|
||||
IgnoreFrontend bool // Indicates if the frontend does not need building
|
||||
OutputFile string // Override the output filename
|
||||
LDFlags string // Optional flags to pass to linker
|
||||
Logger *clilogger.CLILogger // All output to the logger
|
||||
OutputType string // EG: desktop, server....
|
||||
Mode Mode // release or debug
|
||||
ProjectData *project.Project // The project data
|
||||
Pack bool // Create a package for the app after building
|
||||
Platform string // The platform to build for
|
||||
Compiler string // The compiler command to use
|
||||
IgnoreFrontend bool // Indicates if the frontend does not need building
|
||||
OutputFile string // Override the output filename
|
||||
}
|
||||
|
||||
// GetModeAsString returns the current mode as a string
|
||||
@@ -47,11 +48,6 @@ func Build(options *Options) (string, error) {
|
||||
// Extract logger
|
||||
outputLogger := options.Logger
|
||||
|
||||
// Create a default logger if it doesn't exist
|
||||
if outputLogger == nil {
|
||||
outputLogger = logger.New()
|
||||
}
|
||||
|
||||
// Get working directory
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -59,7 +55,7 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatforms := slicer.String([]string{"linux"})
|
||||
validPlatforms := slicer.String([]string{"linux", "darwin"})
|
||||
if !validPlatforms.Contains(options.Platform) {
|
||||
return "", fmt.Errorf("platform %s not supported", options.Platform)
|
||||
}
|
||||
@@ -94,8 +90,15 @@ func Build(options *Options) (string, error) {
|
||||
// Initialise Builder
|
||||
builder.SetProjectData(projectData)
|
||||
|
||||
// Generate Frontend JS Package
|
||||
outputLogger.Println(" - Generating Backend JS Package")
|
||||
// Ignore the parser report coming back
|
||||
_, err = parser.GenerateWailsFrontendPackage()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !options.IgnoreFrontend {
|
||||
outputLogger.Writeln(" - Building Wails Frontend")
|
||||
outputLogger.Println(" - Building Wails Frontend")
|
||||
err = builder.BuildFrontend(outputLogger)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -103,7 +106,7 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
|
||||
// Build the base assets
|
||||
outputLogger.Writeln(" - Compiling Assets")
|
||||
outputLogger.Println(" - Compiling Assets")
|
||||
err = builder.BuildRuntime(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -114,16 +117,17 @@ func Build(options *Options) (string, error) {
|
||||
}
|
||||
|
||||
// Compile the application
|
||||
outputLogger.Write(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
||||
outputLogger.Print(" - Compiling Application in " + GetModeAsString(options.Mode) + " mode...")
|
||||
err = builder.CompileProject(options)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
outputLogger.Writeln("done.")
|
||||
outputLogger.Println("done.")
|
||||
|
||||
// Do we need to pack the app?
|
||||
if options.Pack {
|
||||
|
||||
outputLogger.Writeln(" - Packaging Application")
|
||||
outputLogger.Println(" - Packaging Application")
|
||||
|
||||
// TODO: Allow cross platform build
|
||||
err = packageProject(options, runtime.GOOS)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user