mirror of
https://github.com/taigrr/wails.git
synced 2026-04-16 03:40:49 -07:00
Compare commits
83 Commits
feature/v2
...
v2-alpha-o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
febd867fa7 | ||
|
|
cbd98b5a1a | ||
|
|
c8e0aea69c | ||
|
|
7c0b236eb0 | ||
|
|
16debbd109 | ||
|
|
39bfa5d910 | ||
|
|
6424579a9e | ||
|
|
a962ae6f63 | ||
|
|
c7dee158ba | ||
|
|
237d25089d | ||
|
|
265328d648 | ||
|
|
6f40e85a6e | ||
|
|
96d8509da3 | ||
|
|
598445ab0f | ||
|
|
24788e2fd3 | ||
|
|
bbf4dde43f | ||
|
|
0afd27ab45 | ||
|
|
d498423ec2 | ||
|
|
66ce84973c | ||
|
|
55e6a0f312 | ||
|
|
81e83fdf18 | ||
|
|
f9b79d24f8 | ||
|
|
0599a47bfe | ||
|
|
817c55d318 | ||
|
|
14146c8c0c | ||
|
|
18adac20d4 | ||
|
|
eb4bff89da | ||
|
|
c66dc777f3 | ||
|
|
9003462457 | ||
|
|
e124f0a220 | ||
|
|
c6d3f57712 | ||
|
|
b4c669ff86 | ||
|
|
2d1b2c0947 | ||
|
|
4a0c5aa785 | ||
|
|
f48d7f8f60 | ||
|
|
651f24f641 | ||
|
|
8fd77148ca | ||
|
|
0dc0762fdf | ||
|
|
1a92550709 | ||
|
|
bffc15bc14 | ||
|
|
198d206c46 | ||
|
|
bb8e848ef6 | ||
|
|
bac3e9e5c1 | ||
|
|
bc5eddeb66 | ||
|
|
8e7258d812 | ||
|
|
7118762cec | ||
|
|
6af92cf0a4 | ||
|
|
1bb91634f7 | ||
|
|
f71ce7913f | ||
|
|
53db687a26 | ||
|
|
13939d3d6b | ||
|
|
552c6b8711 | ||
|
|
feee2b3db2 | ||
|
|
9889c2bdbb | ||
|
|
2432fccf71 | ||
|
|
70510fd180 | ||
|
|
17c6201469 | ||
|
|
0f209c8900 | ||
|
|
cbf043585c | ||
|
|
5ae621ceaa | ||
|
|
1231b59443 | ||
|
|
b18d4fbf41 | ||
|
|
9ec5605e63 | ||
|
|
98a4de8878 | ||
|
|
5fe709f558 | ||
|
|
5231a6893b | ||
|
|
74f3ce990f | ||
|
|
998a913853 | ||
|
|
964844835c | ||
|
|
4e152bb849 | ||
|
|
51133d098c | ||
|
|
d4191e7d1b | ||
|
|
9c273bc745 | ||
|
|
c6f6ad6beb | ||
|
|
4362a14459 | ||
|
|
0080e9e311 | ||
|
|
83d9297cac | ||
|
|
9806b9c651 | ||
|
|
2a20867d00 | ||
|
|
48ff661150 | ||
|
|
19d59bef51 | ||
|
|
bdcb2fe810 | ||
|
|
25a157e661 |
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
echo "**** Checking if Wails passes unit tests ****"
|
echo "**** Checking if Wails passes unit tests ****"
|
||||||
if ! go test ./...
|
if ! go test ./lib/... ./runtime/... ./cmd/...
|
||||||
then
|
then
|
||||||
echo ""
|
echo ""
|
||||||
echo "ERROR: Unit tests failed!"
|
echo "ERROR: Unit tests failed!"
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ package build
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
// AddBuildSubcommand adds the `build` command for the Wails application
|
||||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
func AddBuildSubcommand(app *clir.Cli) {
|
||||||
|
|
||||||
outputType := "desktop"
|
outputType := "desktop"
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// Setup pack flag
|
// Setup pack flag
|
||||||
pack := false
|
pack := false
|
||||||
command.BoolFlag("package", "Create a platform specific package", &pack)
|
command.BoolFlag("pack", "Create a platform specific package", &pack)
|
||||||
|
|
||||||
compilerCommand := "go"
|
compilerCommand := "go"
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||||
@@ -43,7 +43,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
// Quiet Build
|
// Quiet Build
|
||||||
quiet := false
|
quiet := false
|
||||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||||
|
|
||||||
// ldflags to pass to `go`
|
// ldflags to pass to `go`
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
@@ -53,19 +53,19 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
logFile := ""
|
logFile := ""
|
||||||
command.StringFlag("l", "Log to file", &logFile)
|
command.StringFlag("l", "Log to file", &logFile)
|
||||||
|
|
||||||
// Retain assets
|
|
||||||
keepAssets := false
|
|
||||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
logger.Mute(quiet)
|
|
||||||
|
if !quiet {
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate output type
|
// Validate output type
|
||||||
if !validTargetTypes.Contains(outputType) {
|
if !validTargetTypes.Contains(outputType) {
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
logger.Fatal(fmt.Sprintf("Output type '%s' is not valid.", outputType))
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !quiet {
|
if !quiet {
|
||||||
@@ -73,8 +73,8 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||||
logger.Println(task)
|
logger.Writeln(task)
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
logger.Writeln(strings.Repeat("-", len(task)))
|
||||||
|
|
||||||
// Setup mode
|
// Setup mode
|
||||||
mode := build.Debug
|
mode := build.Debug
|
||||||
@@ -91,7 +91,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
Platform: platform,
|
Platform: platform,
|
||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
KeepAssets: keepAssets,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return doBuild(buildOptions)
|
return doBuild(buildOptions)
|
||||||
@@ -108,12 +107,11 @@ func doBuild(buildOptions *build.Options) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output stats
|
// Output stats
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
buildOptions.Logger.Println("")
|
buildOptions.Logger.Writeln("")
|
||||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
buildOptions.Logger.Writeln(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
||||||
buildOptions.Logger.Println("")
|
buildOptions.Logger.Writeln("")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package dev
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -10,21 +9,28 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/process"
|
"github.com/wailsapp/wails/v2/internal/process"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
// AddSubcommand adds the `dev` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("dev", "Development mode")
|
command := app.NewSubCommand("dev", "Development mode")
|
||||||
|
|
||||||
|
outputType := "desktop"
|
||||||
|
|
||||||
|
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||||
|
|
||||||
|
// Setup target type flag
|
||||||
|
description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
|
||||||
|
command.StringFlag("t", description, &outputType)
|
||||||
|
|
||||||
// Passthrough ldflags
|
// Passthrough ldflags
|
||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
@@ -35,12 +41,18 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// extensions to trigger rebuilds
|
// extensions to trigger rebuilds
|
||||||
extensions := "go"
|
extensions := "go"
|
||||||
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
command.StringFlag("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if !validTargetTypes.Contains(outputType) {
|
||||||
|
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||||
|
}
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
|
|
||||||
// TODO: Check you are in a project directory
|
// TODO: Check you are in a project directory
|
||||||
@@ -52,7 +64,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
defer watcher.Close()
|
defer watcher.Close()
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
var debugBinaryProcess *process.Process = nil
|
||||||
var buildFrontend bool = false
|
var buildFrontend bool = true
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
@@ -62,24 +74,19 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
debounceQuit := make(chan bool, 1)
|
debounceQuit := make(chan bool, 1)
|
||||||
|
|
||||||
// Do initial build
|
// Do initial build
|
||||||
logger.Println("Building application for development...")
|
logger.Info("Building application for development...")
|
||||||
debugBinaryProcess, err = restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||||
// logger.Println("event: %+v", event)
|
// logger.Info("event: %+v", event)
|
||||||
|
|
||||||
// Check for new directories
|
// Check for new directories
|
||||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||||
// If this is a folder, add it to our watch list
|
// If this is a folder, add it to our watch list
|
||||||
if fs.DirExists(event.Name) {
|
if fs.DirExists(event.Name) {
|
||||||
if !strings.Contains(event.Name, "node_modules") {
|
if !strings.Contains(event.Name, "node_modules") {
|
||||||
err := watcher.Add(event.Name)
|
watcher.Add(event.Name)
|
||||||
if err != nil {
|
logger.Info("Watching directory: %s", event.Name)
|
||||||
logger.Fatal("%s", err.Error())
|
|
||||||
}
|
|
||||||
logger.Println("Watching directory: %s", event.Name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -88,7 +95,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
// Check for file writes
|
// Check for file writes
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
|
||||||
logger.Println("modified file: %s", event.Name)
|
// logger.Info("modified file: %s", event.Name)
|
||||||
var rebuild bool = false
|
var rebuild bool = false
|
||||||
|
|
||||||
// Iterate all file patterns
|
// Iterate all file patterns
|
||||||
@@ -105,20 +112,21 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !rebuild {
|
if !rebuild {
|
||||||
logger.Println("Filename change: %s did not match extension list (%s)", event.Name, extensions)
|
logger.Info("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
if buildFrontend {
|
||||||
|
logger.Info("Full rebuild triggered: %s updated", event.Name)
|
||||||
|
} else {
|
||||||
|
logger.Info("Partial build triggered: %s updated", event.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// Do a rebuild
|
// Do a rebuild
|
||||||
|
|
||||||
// Try and build the app
|
// Try and build the app
|
||||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess)
|
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error during build: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If we have a new process, save it
|
// If we have a new process, save it
|
||||||
if newBinaryProcess != nil {
|
if newBinaryProcess != nil {
|
||||||
debugBinaryProcess = newBinaryProcess
|
debugBinaryProcess = newBinaryProcess
|
||||||
@@ -144,7 +152,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
if strings.Contains(dir, "node_modules") {
|
if strings.Contains(dir, "node_modules") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Println("Watching directory: %s", dir)
|
logger.Info("Watching directory: %s", dir)
|
||||||
err = watcher.Add(dir)
|
err = watcher.Add(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
logger.Fatal(err.Error())
|
||||||
@@ -156,7 +164,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
for quit == false {
|
for quit == false {
|
||||||
select {
|
select {
|
||||||
case <-quitChannel:
|
case <-quitChannel:
|
||||||
println("Caught quit")
|
println()
|
||||||
// Notify debouncer to quit
|
// Notify debouncer to quit
|
||||||
debounceQuit <- true
|
debounceQuit <- true
|
||||||
quit = true
|
quit = true
|
||||||
@@ -165,13 +173,10 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Kill the current program if running
|
// Kill the current program if running
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
err := debugBinaryProcess.Kill()
|
debugBinaryProcess.Kill()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Println("Development mode exited")
|
logger.Info("Development mode exited")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -198,15 +203,15 @@ exit:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
func restartApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
||||||
println()
|
println()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err.Error())
|
logger.Error("Build Failed: %s", err.Error())
|
||||||
return nil, errors.Wrap(err, "Build Failed:")
|
return nil
|
||||||
}
|
}
|
||||||
logger.Println("Build new binary: %s", appBinary)
|
logger.Info("Build new binary: %s", appBinary)
|
||||||
|
|
||||||
// Kill existing binary if need be
|
// Kill existing binary if need be
|
||||||
if debugBinaryProcess != nil {
|
if debugBinaryProcess != nil {
|
||||||
@@ -226,17 +231,14 @@ func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string,
|
|||||||
err = newProcess.Start()
|
err = newProcess.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Remove binary
|
// Remove binary
|
||||||
deleteError := fs.DeleteFile(appBinary)
|
fs.DeleteFile(appBinary)
|
||||||
if deleteError != nil {
|
|
||||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
|
||||||
}
|
|
||||||
logger.Fatal("Unable to start application: %s", err.Error())
|
logger.Fatal("Unable to start application: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return newProcess, nil
|
return newProcess
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
func buildApp(logger *logger.Logger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||||
|
|
||||||
// Create random output file
|
// Create random output file
|
||||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||||
@@ -251,7 +253,7 @@ func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, co
|
|||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
OutputFile: outputFile,
|
OutputFile: outputFile,
|
||||||
IgnoreFrontend: true,
|
IgnoreFrontend: !buildFrontend,
|
||||||
}
|
}
|
||||||
|
|
||||||
return build.Build(buildOptions)
|
return build.Build(buildOptions)
|
||||||
|
|||||||
@@ -2,37 +2,37 @@ package doctor
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/system"
|
"github.com/wailsapp/wails/v2/internal/system"
|
||||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `doctor` command for the Wails application
|
// AddSubcommand adds the `doctor` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
logger := clilogger.New(w)
|
// Create logger
|
||||||
|
logger := logger.New()
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
logger.Print("Scanning system - please wait...")
|
print("Scanning system - please wait...")
|
||||||
|
|
||||||
// Get system info
|
// Get system info
|
||||||
info, err := system.GetInfo()
|
info, err := system.GetInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Println("Done.")
|
println("Done.")
|
||||||
|
|
||||||
// Start a new tabwriter
|
// Start a new tabwriter
|
||||||
w := new(tabwriter.Writer)
|
w := new(tabwriter.Writer)
|
||||||
@@ -112,22 +112,22 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
fmt.Fprintf(w, "\n")
|
fmt.Fprintf(w, "\n")
|
||||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
fmt.Fprintf(w, "* - Optional Dependency\n")
|
||||||
w.Flush()
|
w.Flush()
|
||||||
logger.Println("")
|
println()
|
||||||
logger.Println("Diagnosis")
|
println("Diagnosis")
|
||||||
logger.Println("---------\n")
|
println("---------\n")
|
||||||
|
|
||||||
// Generate an appropriate diagnosis
|
// Generate an appropriate diagnosis
|
||||||
|
|
||||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
||||||
logger.Println("Your system is ready for Wails development!")
|
println("Your system is ready for Wails development!")
|
||||||
}
|
}
|
||||||
|
|
||||||
if dependenciesAvailableRequired != 0 {
|
if dependenciesAvailableRequired != 0 {
|
||||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
if dependenciesAvailableOptional != 0 {
|
if dependenciesAvailableOptional != 0 {
|
||||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(externalPackages) > 0 {
|
if len(externalPackages) > 0 {
|
||||||
@@ -135,18 +135,18 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
if p.Optional {
|
if p.Optional {
|
||||||
print("[Optional] ")
|
print("[Optional] ")
|
||||||
}
|
}
|
||||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
println("Install " + p.Name + ": " + p.InstallCommand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dependenciesMissing) != 0 {
|
if len(dependenciesMissing) != 0 {
|
||||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
||||||
log.Println("Fatal:")
|
println("Fatal:")
|
||||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
||||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("")
|
println()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,17 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/pkg/errors"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/templates"
|
"github.com/wailsapp/wails/v2/internal/templates"
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/git"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `init` command for the Wails application
|
// AddSubcommand adds the `init` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli) error {
|
||||||
|
|
||||||
// Load the template shortnames
|
// Load the template shortnames
|
||||||
validShortNames, err := templates.TemplateShortNames()
|
validShortNames, err := templates.TemplateShortNames()
|
||||||
@@ -34,24 +32,13 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
command.StringFlag("n", "Name of project", &projectName)
|
command.StringFlag("n", "Name of project", &projectName)
|
||||||
|
|
||||||
// Setup project directory
|
// Setup project directory
|
||||||
projectDirectory := ""
|
projectDirectory := "."
|
||||||
command.StringFlag("d", "Project directory", &projectDirectory)
|
command.StringFlag("d", "Project directory", &projectDirectory)
|
||||||
|
|
||||||
// Quiet Init
|
// Quiet Init
|
||||||
quiet := false
|
quiet := false
|
||||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
command.BoolFlag("q", "Supress output to console", &quiet)
|
||||||
|
|
||||||
initGit := false
|
|
||||||
gitInstalled := git.IsInstalled()
|
|
||||||
if gitInstalled {
|
|
||||||
// Git Init
|
|
||||||
command.BoolFlag("g", "Initialise git repository", &initGit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VSCode project files
|
|
||||||
vscode := false
|
|
||||||
command.BoolFlag("vscode", "Generate VSCode project files", &vscode)
|
|
||||||
|
|
||||||
// List templates
|
// List templates
|
||||||
list := false
|
list := false
|
||||||
command.BoolFlag("l", "List templates", &list)
|
command.BoolFlag("l", "List templates", &list)
|
||||||
@@ -59,29 +46,32 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
// Create logger
|
// Create logger
|
||||||
logger := clilogger.New(w)
|
logger := logger.New()
|
||||||
logger.Mute(quiet)
|
|
||||||
|
if !quiet {
|
||||||
|
logger.AddOutput(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
// Are we listing templates?
|
// Are we listing templates?
|
||||||
if list {
|
if list {
|
||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
err := templates.OutputList(logger)
|
err := templates.OutputList(logger)
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate output type
|
// Validate output type
|
||||||
if !validShortNames.Contains(templateName) {
|
if !validShortNames.Contains(templateName) {
|
||||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
logger.Write(fmt.Sprintf("ERROR: Template '%s' is not valid", templateName))
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
command.PrintHelp()
|
command.PrintHelp()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate name
|
// Validate name
|
||||||
if len(projectName) == 0 {
|
if len(projectName) == 0 {
|
||||||
logger.Println("ERROR: Project name required")
|
logger.Writeln("ERROR: Project name required")
|
||||||
logger.Println("")
|
logger.Writeln("")
|
||||||
command.PrintHelp()
|
command.PrintHelp()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -91,23 +81,15 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
||||||
logger.Println(task)
|
logger.Writeln(task)
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
logger.Writeln(strings.Repeat("-", len(task)))
|
||||||
|
|
||||||
// Create Template Options
|
// Create Template Options
|
||||||
options := &templates.Options{
|
options := &templates.Options{
|
||||||
ProjectName: projectName,
|
ProjectName: projectName,
|
||||||
TargetDir: projectDirectory,
|
TargetDir: projectDirectory,
|
||||||
TemplateName: templateName,
|
TemplateName: templateName,
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
GenerateVSCode: vscode,
|
|
||||||
InitGit: initGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to discover author details from git config
|
|
||||||
err := findAuthorDetails(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return initProject(options)
|
return initProject(options)
|
||||||
@@ -128,55 +110,11 @@ func initProject(options *templates.Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.InitGit {
|
|
||||||
err = initGit(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output stats
|
// Output stats
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
options.Logger.Println("")
|
options.Logger.Writeln("")
|
||||||
options.Logger.Println("Project Name: " + options.ProjectName)
|
options.Logger.Writeln(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
||||||
options.Logger.Println("Project Directory: " + options.TargetDir)
|
options.Logger.Writeln("")
|
||||||
options.Logger.Println("Project Template: " + options.TemplateName)
|
|
||||||
if options.GenerateVSCode {
|
|
||||||
options.Logger.Println("VSCode config files generated.")
|
|
||||||
}
|
|
||||||
if options.InitGit {
|
|
||||||
options.Logger.Println("Git repository initialised.")
|
|
||||||
}
|
|
||||||
options.Logger.Println("")
|
|
||||||
options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
|
||||||
options.Logger.Println("")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGit(options *templates.Options) error {
|
|
||||||
err := git.InitRepo(options.TargetDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Unable to initialise git repository:")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findAuthorDetails(options *templates.Options) error {
|
|
||||||
if git.IsInstalled() {
|
|
||||||
name, err := git.Name()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorName = strings.TrimSpace(name)
|
|
||||||
|
|
||||||
email, err := git.Email()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorEmail = strings.TrimSpace(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ import (
|
|||||||
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("update", "Update the Wails CLI")
|
command := app.NewSubCommand("update", "Update the Wails CLI")
|
||||||
command.LongDescription(`This command allows you to update your version of Wails.`)
|
command.LongDescription(`This command allows you to update your version of the Wails CLI.`)
|
||||||
|
|
||||||
// Setup flags
|
// Setup flags
|
||||||
var prereleaseRequired bool
|
var prereleaseRequired bool
|
||||||
command.BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
|
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
||||||
|
|
||||||
var specificVersion string
|
var specificVersion string
|
||||||
command.StringFlag("version", "Install a specific version (Overrides other flags)", &specificVersion)
|
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logger.Print("Installing Wails " + desiredVersion + "...")
|
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
||||||
|
|
||||||
// Run command in non module directory
|
// Run command in non module directory
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
@@ -158,7 +158,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
logger.Println("Wails updated to " + desiredVersion)
|
logger.Println("Wails CLI updated to " + desiredVersion)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,35 +17,16 @@ func fatal(message string) {
|
|||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
version := "v2.0.0-alpha"
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||||
|
|
||||||
build.AddBuildSubcommand(app, os.Stdout)
|
build.AddBuildSubcommand(app)
|
||||||
err = initialise.AddSubcommand(app, os.Stdout)
|
err = initialise.AddSubcommand(app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
err = doctor.AddSubcommand(app)
|
||||||
err = debug.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
err = doctor.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dev.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = generate.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = update.AddSubcommand(app, os.Stdout, version)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err.Error())
|
fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.26"
|
var version = "v2.0.0-alpha.54"
|
||||||
|
|||||||
29
v2/go.mod
29
v2/go.mod
@@ -1,29 +1,14 @@
|
|||||||
module github.com/wailsapp/wails/v2
|
module github.com/wailsapp/wails/v2
|
||||||
|
|
||||||
go 1.16
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/leaanthony/clir v1.0.2
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/leaanthony/gosod v0.0.3
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/leaanthony/slicer v1.4.1
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/matryer/is v1.3.0
|
||||||
github.com/gorilla/websocket v1.4.1
|
|
||||||
github.com/imdario/mergo v0.3.11
|
|
||||||
github.com/jackmordaunt/icns v1.0.0
|
|
||||||
github.com/leaanthony/clir v1.0.4
|
|
||||||
github.com/leaanthony/gosod v0.0.4
|
|
||||||
github.com/leaanthony/slicer v1.5.0
|
|
||||||
github.com/matryer/is v1.4.0
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
github.com/olekukonko/tablewriter v0.0.4
|
||||||
github.com/pkg/errors v0.9.1
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
|
||||||
github.com/tdewolff/test v1.0.6 // indirect
|
|
||||||
github.com/xyproto/xpm v1.2.1
|
github.com/xyproto/xpm v1.2.1
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
nhooyr.io/websocket v1.7.4
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
|
||||||
nhooyr.io/websocket v1.8.6
|
|
||||||
)
|
)
|
||||||
|
|||||||
129
v2/go.sum
129
v2/go.sum
@@ -1,122 +1,57 @@
|
|||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
|
||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/leaanthony/clir v1.0.2 h1:Ham9Ep7NH95il0x/ci5s2ExJa/K4B2dE8L2uaSj2cxo=
|
||||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
github.com/leaanthony/clir v1.0.2/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/leaanthony/gosod v0.0.3 h1:VlPilye0zoH4I0WihShyoiTHyiAN1v6dcBW4mMvGJao=
|
||||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
github.com/leaanthony/gosod v0.0.3/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
github.com/leaanthony/slicer v1.4.1 h1:X/SmRIDhkUAolP79mSTO0jTcVX1k504PJBqvV6TwP0w=
|
||||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
github.com/leaanthony/slicer v1.4.1/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
github.com/matryer/is v1.3.0 h1:9qiso3jaJrOe6qBRJRBt2Ldht05qDiFP9le0JOIhRSI=
|
||||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
|
||||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
|
||||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
|
||||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
|
||||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
||||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
nhooyr.io/websocket v1.7.4 h1:w/LGB2sZT0RV8lZYR7nfyaYz4PUbYZ5oF7NBon2M0NY=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
nhooyr.io/websocket v1.7.4/go.mod h1:PxYxCwFdFYQ0yRvtQz3s/dC+VEm7CSuC/4b9t8MQQxw=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
|
||||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init initialises the application for a debug environment
|
// Init initialises the application for a debug environment
|
||||||
@@ -19,10 +20,10 @@ func (a *App) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set log levels
|
// Set log levels
|
||||||
greeting := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if len(*greeting) > 0 {
|
if len(*loglevel) > 0 {
|
||||||
switch strings.ToLower(*greeting) {
|
switch strings.ToLower(*loglevel) {
|
||||||
case "trace":
|
case "trace":
|
||||||
a.logger.SetLogLevel(logger.TRACE)
|
a.logger.SetLogLevel(logger.TRACE)
|
||||||
case "info":
|
case "info":
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build !desktop,!hybrid,!server,!dev
|
// +build !desktop,!hybrid,!server
|
||||||
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
@@ -10,9 +10,7 @@ package app
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
@@ -22,15 +20,12 @@ type App struct {
|
|||||||
Height int
|
Height int
|
||||||
Resizable bool
|
Resizable bool
|
||||||
|
|
||||||
// Indicates if the app is running in debug mode
|
Features *features.Features
|
||||||
debug bool
|
|
||||||
|
|
||||||
logger *logger.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateApp returns a null application
|
// CreateApp returns a null application
|
||||||
func CreateApp(_ *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
return &App{}, nil
|
return &App{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
@@ -39,3 +34,8 @@ func (a *App) Run() error {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind the dummy interface
|
||||||
|
func (a *App) Bind(dummy interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,159 +3,117 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"os"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/internal/signal"
|
"github.com/wailsapp/wails/v2/internal/signal"
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
appType string
|
|
||||||
|
|
||||||
window *ffenestri.Application
|
window *ffenestri.Application
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
signal *signal.Manager
|
signal *signal.Manager
|
||||||
options *options.App
|
|
||||||
|
|
||||||
// Subsystems
|
// Subsystems
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
//binding *subsystem.Binding
|
binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
menu *subsystem.Menu
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Indicates if the app is in debug mode
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// This is our binding DB
|
// This is our binding DB
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
|
|
||||||
// Application Stores
|
// Feature flags
|
||||||
loglevelStore *runtime.Store
|
Features *features.Features
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
|
|
||||||
// Merge default options
|
// Merge default options
|
||||||
options.MergeDefaults(appoptions)
|
options.mergeDefaults()
|
||||||
|
|
||||||
// Set up logger
|
// Set up logger
|
||||||
myLogger := logger.New(appoptions.Logger)
|
myLogger := logger.New(os.Stdout)
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
myLogger.SetLogLevel(logger.TRACE)
|
||||||
|
|
||||||
// Create the menu manager
|
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||||
menuManager := menumanager.NewManager()
|
Title: options.Title,
|
||||||
|
Width: options.Width,
|
||||||
|
Height: options.Height,
|
||||||
|
MinWidth: options.MinWidth,
|
||||||
|
MinHeight: options.MinHeight,
|
||||||
|
MaxWidth: options.MaxWidth,
|
||||||
|
MaxHeight: options.MaxHeight,
|
||||||
|
Frameless: options.Frameless,
|
||||||
|
|
||||||
// Process the application menu
|
// This should be controlled by the compile time flags...
|
||||||
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
DevTools: true,
|
||||||
|
|
||||||
// Process context menus
|
Resizable: !options.DisableResize,
|
||||||
contextMenus := options.GetContextMenus(appoptions)
|
Fullscreen: options.Fullscreen,
|
||||||
for _, contextMenu := range contextMenus {
|
}, myLogger)
|
||||||
menuManager.AddContextMenu(contextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process tray menus
|
|
||||||
trayMenus := options.GetTrayMenus(appoptions)
|
|
||||||
for _, trayMenu := range trayMenus {
|
|
||||||
menuManager.AddTrayMenu(trayMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
|
||||||
|
|
||||||
// Create binding exemptions - Ugly hack. There must be a better way
|
|
||||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
appType: "desktop",
|
window: window,
|
||||||
window: window,
|
servicebus: servicebus.New(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
logger: myLogger,
|
||||||
logger: myLogger,
|
bindings: binding.NewBindings(myLogger),
|
||||||
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
Features: features.New(),
|
||||||
menuManager: menuManager,
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.options = appoptions
|
return result
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
err := result.Init()
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Setup a context
|
|
||||||
var subsystemWaitGroup sync.WaitGroup
|
|
||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
|
||||||
|
|
||||||
// Setup signal handler
|
// Setup signal handler
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
signal, err := signal.NewManager(a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.signal = signalsubsystem
|
a.signal = signal
|
||||||
a.signal.Start()
|
a.signal.Start()
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
err = a.servicebus.Start()
|
a.servicebus.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
// Start the runtime
|
||||||
if err != nil {
|
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtimesubsystem
|
|
||||||
err = a.runtime.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.runtime = runtime
|
||||||
|
a.runtime.Start()
|
||||||
|
|
||||||
// Application Stores
|
// Start the binding subsystem
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.binding = binding
|
||||||
|
a.binding.Start()
|
||||||
|
|
||||||
// Start the logging subsystem
|
// Start the logging subsystem
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.log = log
|
a.log = log
|
||||||
err = a.log.Start()
|
a.log.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the dispatcher
|
// create the dispatcher
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||||
@@ -163,43 +121,23 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.dispatcher = dispatcher
|
a.dispatcher = dispatcher
|
||||||
err = dispatcher.Start()
|
dispatcher.Start()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
// Start the eventing subsystem
|
||||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.event = eventsubsystem
|
|
||||||
err = a.event.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the menu subsystem
|
|
||||||
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.menu = menusubsystem
|
|
||||||
err = a.menu.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.event = event
|
||||||
|
a.event.Start()
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = callSubsystem
|
|
||||||
err = a.call.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
a.call = call
|
||||||
|
a.call.Start()
|
||||||
|
|
||||||
// Dump bindings as a debug
|
// Dump bindings as a debug
|
||||||
bindingDump, err := a.bindings.ToJSON()
|
bindingDump, err := a.bindings.ToJSON()
|
||||||
@@ -207,29 +145,20 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||||
a.logger.Trace("Ffenestri.Run() exited")
|
a.logger.Trace("Ffenestri.Run() exited")
|
||||||
if err != nil {
|
a.servicebus.Stop()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close down all the subsystems
|
return result
|
||||||
a.logger.Trace("Cancelling subsystems")
|
}
|
||||||
cancel()
|
|
||||||
subsystemWaitGroup.Wait()
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
a.logger.Trace("Cancelling dispatcher")
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
dispatcher.Close()
|
|
||||||
|
// Add the struct to the bindings
|
||||||
// Close log
|
err := a.bindings.Add(structPtr)
|
||||||
a.logger.Trace("Stopping log")
|
if err != nil {
|
||||||
log.Close()
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
a.logger.Trace("Stopping Service bus")
|
|
||||||
err = a.servicebus.Stop()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
|
|
||||||
// Set up logger
|
// Set up logger
|
||||||
myLogger := logger.New(appoptions.Logger)
|
myLogger := logger.New(appoptions.Logger)
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
// Create the menu manager
|
// Create the menu manager
|
||||||
menuManager := menumanager.NewManager()
|
menuManager := menumanager.NewManager()
|
||||||
@@ -119,14 +118,6 @@ func (a *App) Run() error {
|
|||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
ctx, cancel := context.WithCancel(parentContext)
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
// Start the service bus
|
// Start the service bus
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
err = a.servicebus.Start()
|
err = a.servicebus.Start()
|
||||||
@@ -134,7 +125,7 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -212,6 +203,14 @@ func (a *App) Run() error {
|
|||||||
// Generate backend.js
|
// Generate backend.js
|
||||||
a.bindings.GenerateBackendJS()
|
a.bindings.GenerateBackendJS()
|
||||||
|
|
||||||
|
// Setup signal handler
|
||||||
|
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.signal = signalsubsystem
|
||||||
|
a.signal.Start()
|
||||||
|
|
||||||
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
||||||
a.logger.Trace("Bridge.Run() exited")
|
a.logger.Trace("Bridge.Run() exited")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -236,6 +235,10 @@ func (a *App) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown callback
|
||||||
|
if a.shutdownCallback != nil {
|
||||||
|
a.shutdownCallback()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
@@ -42,7 +43,8 @@ type App struct {
|
|||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
|
|
||||||
debug bool
|
// Feature flags
|
||||||
|
Features *features.Features
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
@@ -56,15 +58,17 @@ func CreateApp(options *Options) *App {
|
|||||||
myLogger.SetLogLevel(logger.INFO)
|
myLogger.SetLogLevel(logger.INFO)
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
||||||
Title: options.Title,
|
Title: options.Title,
|
||||||
Width: options.Width,
|
Width: options.Width,
|
||||||
Height: options.Height,
|
Height: options.Height,
|
||||||
MinWidth: options.MinWidth,
|
MinWidth: options.MinWidth,
|
||||||
MinHeight: options.MinHeight,
|
MinHeight: options.MinHeight,
|
||||||
MaxWidth: options.MaxWidth,
|
MaxWidth: options.MaxWidth,
|
||||||
MaxHeight: options.MaxHeight,
|
MaxHeight: options.MaxHeight,
|
||||||
StartHidden: options.StartHidden,
|
Frameless: options.Frameless,
|
||||||
DevTools: options.DevTools,
|
|
||||||
|
// This should be controlled by the compile time flags...
|
||||||
|
DevTools: true,
|
||||||
|
|
||||||
Resizable: !options.DisableResize,
|
Resizable: !options.DisableResize,
|
||||||
Fullscreen: options.Fullscreen,
|
Fullscreen: options.Fullscreen,
|
||||||
@@ -75,12 +79,9 @@ func CreateApp(options *Options) *App {
|
|||||||
webserver: webserver.NewWebServer(myLogger),
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
servicebus: servicebus.New(myLogger),
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
bindings: binding.NewBindings(myLogger),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
app.Init()
|
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +185,7 @@ func (a *App) Run() error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
result := a.window.Run(dispatcher, bindingDump)
|
result := a.window.Run(dispatcher, bindingDump, a.Features)
|
||||||
a.servicebus.Stop()
|
a.servicebus.Stop()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -192,3 +193,14 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
|
|
||||||
|
// Add the struct to the bindings
|
||||||
|
err := a.bindings.Add(structPtr)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
34
v2/internal/app/options.go
Normal file
34
v2/internal/app/options.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
// Options for creating the App
|
||||||
|
type Options struct {
|
||||||
|
Title string
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
DisableResize bool
|
||||||
|
Fullscreen bool
|
||||||
|
Frameless bool
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
MaxWidth int
|
||||||
|
MaxHeight int
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeDefaults will set the minimum default values for an application
|
||||||
|
func (o *Options) mergeDefaults() {
|
||||||
|
|
||||||
|
// Create a default title
|
||||||
|
if len(o.Title) == 0 {
|
||||||
|
o.Title = "My Wails App"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default width
|
||||||
|
if o.Width == 0 {
|
||||||
|
o.Width = 1024
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default height
|
||||||
|
if o.Height == 0 {
|
||||||
|
o.Height = 768
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,13 +6,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||||
@@ -20,57 +17,36 @@ import (
|
|||||||
|
|
||||||
// App defines a Wails application structure
|
// App defines a Wails application structure
|
||||||
type App struct {
|
type App struct {
|
||||||
appType string
|
|
||||||
|
|
||||||
binding *subsystem.Binding
|
binding *subsystem.Binding
|
||||||
call *subsystem.Call
|
call *subsystem.Call
|
||||||
event *subsystem.Event
|
event *subsystem.Event
|
||||||
log *subsystem.Log
|
log *subsystem.Log
|
||||||
runtime *subsystem.Runtime
|
runtime *subsystem.Runtime
|
||||||
|
|
||||||
options *options.App
|
|
||||||
|
|
||||||
bindings *binding.Bindings
|
bindings *binding.Bindings
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
dispatcher *messagedispatcher.Dispatcher
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
webserver *webserver.WebServer
|
webserver *webserver.WebServer
|
||||||
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
loglevelStore *runtime.Store
|
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create App
|
// Create App
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
func CreateApp(options *Options) *App {
|
||||||
|
options.mergeDefaults()
|
||||||
|
// We ignore the inputs (for now)
|
||||||
|
|
||||||
// Merge default options
|
// TODO: Allow logger output override on CLI
|
||||||
options.MergeDefaults(appoptions)
|
myLogger := logger.New(os.Stdout)
|
||||||
|
myLogger.SetLogLevel(logger.TRACE)
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(appoptions.Logger)
|
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
appType: "server",
|
bindings: binding.NewBindings(myLogger),
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
logger: myLogger,
|
||||||
logger: myLogger,
|
servicebus: servicebus.New(myLogger),
|
||||||
servicebus: servicebus.New(myLogger),
|
webserver: webserver.NewWebServer(myLogger),
|
||||||
webserver: webserver.NewWebServer(myLogger),
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise app
|
return result
|
||||||
result.Init()
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
@@ -107,21 +83,8 @@ func (a *App) Run() error {
|
|||||||
if debugMode {
|
if debugMode {
|
||||||
a.servicebus.Debug()
|
a.servicebus.Debug()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the runtime
|
|
||||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtime
|
|
||||||
a.runtime.Start()
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
|
||||||
|
|
||||||
a.servicebus.Start()
|
a.servicebus.Start()
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,6 +97,14 @@ func (a *App) Run() error {
|
|||||||
a.dispatcher = dispatcher
|
a.dispatcher = dispatcher
|
||||||
a.dispatcher.Start()
|
a.dispatcher.Start()
|
||||||
|
|
||||||
|
// Start the runtime
|
||||||
|
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.runtime = runtime
|
||||||
|
a.runtime.Start()
|
||||||
|
|
||||||
// Start the binding subsystem
|
// Start the binding subsystem
|
||||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,7 +122,7 @@ func (a *App) Run() error {
|
|||||||
a.event.Start()
|
a.event.Start()
|
||||||
|
|
||||||
// Start the call subsystem
|
// Start the call subsystem
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -171,3 +142,14 @@ func (a *App) Run() error {
|
|||||||
|
|
||||||
return cli.Run()
|
return cli.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bind a struct to the application by passing in
|
||||||
|
// a pointer to it
|
||||||
|
func (a *App) Bind(structPtr interface{}) {
|
||||||
|
|
||||||
|
// Add the struct to the bindings
|
||||||
|
err := a.bindings.Add(structPtr)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Fatal("Error during binding: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,55 +2,30 @@ package binding
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Bindings struct {
|
type Bindings struct {
|
||||||
db *DB
|
db *DB
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
exemptions slicer.StringSlicer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
// NewBindings returns a new Bindings object
|
||||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
func NewBindings(logger *logger.Logger) *Bindings {
|
||||||
result := &Bindings{
|
return &Bindings{
|
||||||
db: newDB(),
|
db: newDB(),
|
||||||
logger: logger.CustomLogger("Bindings"),
|
logger: logger.CustomLogger("Bindings"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, exemption := range exemptions {
|
|
||||||
if exemptions == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
|
||||||
// Yuk yuk yuk! Is there a better way?
|
|
||||||
name = strings.TrimSuffix(name, "-fm")
|
|
||||||
result.exemptions.Add(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the structs to bind
|
|
||||||
for _, ptr := range structPointersToBind {
|
|
||||||
err := result.Add(ptr)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the given struct methods to the Bindings
|
// Add the given struct methods to the Bindings
|
||||||
func (b *Bindings) Add(structPtr interface{}) error {
|
func (b *Bindings) Add(structPtr interface{}) error {
|
||||||
|
|
||||||
methods, err := b.getMethods(structPtr)
|
methods, err := getMethods(structPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
return fmt.Errorf("unable to Add() - %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
@@ -59,8 +34,29 @@ func (b *Bindings) Add(structPtr interface{}) error {
|
|||||||
structName := splitName[1]
|
structName := splitName[1]
|
||||||
methodName := splitName[2]
|
methodName := splitName[2]
|
||||||
|
|
||||||
|
// Is this WailsInit?
|
||||||
|
if method.IsWailsInit() {
|
||||||
|
err := b.db.AddWailsInit(method)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.logger.Trace("Registered WailsInit method: %s", method.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this WailsShutdown?
|
||||||
|
if method.IsWailsShutdown() {
|
||||||
|
err := b.db.AddWailsShutdown(method)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.logger.Trace("Registered WailsShutdown method: %s", method.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Add it as a regular method
|
// Add it as a regular method
|
||||||
b.db.AddMethod(packageName, structName, methodName, method)
|
b.db.AddMethod(packageName, structName, methodName, method)
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package binding
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BoundMethod defines all the data related to a Go method that is
|
// BoundMethod defines all the data related to a Go method that is
|
||||||
@@ -16,6 +16,58 @@ type BoundMethod struct {
|
|||||||
Method reflect.Value `json:"-"`
|
Method reflect.Value `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsWailsInit returns true if the method name is "WailsInit"
|
||||||
|
func (b *BoundMethod) IsWailsInit() bool {
|
||||||
|
return strings.HasSuffix(b.Name, "WailsInit")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsWailsShutdown returns true if the method name is "WailsShutdown"
|
||||||
|
func (b *BoundMethod) IsWailsShutdown() bool {
|
||||||
|
return strings.HasSuffix(b.Name, "WailsShutdown")
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWailsInit checks if the WailsInit signature is correct
|
||||||
|
func (b *BoundMethod) VerifyWailsInit() error {
|
||||||
|
// Must only have 1 input
|
||||||
|
if b.InputCount() != 1 {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check input type
|
||||||
|
if !b.Inputs[0].IsType("*goruntime.Runtime") {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must only have 1 output
|
||||||
|
if b.OutputCount() != 1 {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check output type
|
||||||
|
if !b.Outputs[0].IsError() {
|
||||||
|
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input must be of type Runtime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyWailsShutdown checks if the WailsShutdown signature is correct
|
||||||
|
func (b *BoundMethod) VerifyWailsShutdown() error {
|
||||||
|
// Must have no inputs
|
||||||
|
if b.InputCount() != 0 {
|
||||||
|
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must have no outputs
|
||||||
|
if b.OutputCount() != 0 {
|
||||||
|
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input must be of type Runtime
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// InputCount returns the number of inputs this bound method has
|
// InputCount returns the number of inputs this bound method has
|
||||||
func (b *BoundMethod) InputCount() int {
|
func (b *BoundMethod) InputCount() int {
|
||||||
return len(b.Inputs)
|
return len(b.Inputs)
|
||||||
@@ -26,29 +78,6 @@ func (b *BoundMethod) OutputCount() int {
|
|||||||
return len(b.Outputs)
|
return len(b.Outputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseArgs method converts the input json into the types expected by the method
|
|
||||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
|
||||||
|
|
||||||
result := make([]interface{}, b.InputCount())
|
|
||||||
if len(args) != b.InputCount() {
|
|
||||||
return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Name, b.InputCount())
|
|
||||||
}
|
|
||||||
for index, arg := range args {
|
|
||||||
typ := b.Inputs[index].reflectType
|
|
||||||
inputValue := reflect.New(typ).Interface()
|
|
||||||
err := json.Unmarshal(arg, inputValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if inputValue == nil {
|
|
||||||
result[index] = reflect.Zero(typ).Interface()
|
|
||||||
} else {
|
|
||||||
result[index] = reflect.ValueOf(inputValue).Elem().Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call will attempt to call this bound method with the given args
|
// Call will attempt to call this bound method with the given args
|
||||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
||||||
// Check inputs
|
// Check inputs
|
||||||
@@ -65,8 +94,17 @@ func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
|||||||
|
|
||||||
// Iterate over given arguments
|
// Iterate over given arguments
|
||||||
for index, arg := range args {
|
for index, arg := range args {
|
||||||
|
|
||||||
|
// Attempt to convert the argument to the type expected by the method
|
||||||
|
value, err := convertArgToValue(arg, b.Inputs[index])
|
||||||
|
|
||||||
|
// If it fails, return a suitable error
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%s (parameter %d): %s", b.Name, index+1, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// Save the converted argument
|
// Save the converted argument
|
||||||
callArgs[index] = reflect.ValueOf(arg)
|
callArgs[index] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the call
|
// Do the call
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ type DB struct {
|
|||||||
// It used for performance gains at runtime
|
// It used for performance gains at runtime
|
||||||
methodMap map[string]*BoundMethod
|
methodMap map[string]*BoundMethod
|
||||||
|
|
||||||
|
// These are slices of methods registered using WailsInit and WailsShutdown
|
||||||
|
wailsInitMethods []*BoundMethod
|
||||||
|
wailsShutdownMethods []*BoundMethod
|
||||||
|
|
||||||
// Lock to ensure sync access to the data
|
// Lock to ensure sync access to the data
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -90,6 +94,38 @@ func (d *DB) AddMethod(packageName string, structName string, methodName string,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddWailsInit checks the given method is a WailsInit method and if it
|
||||||
|
// is, adds it to the list of WailsInit methods
|
||||||
|
func (d *DB) AddWailsInit(method *BoundMethod) error {
|
||||||
|
err := method.VerifyWailsInit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the db whilst processing and unlock on return
|
||||||
|
d.lock.Lock()
|
||||||
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
|
d.wailsInitMethods = append(d.wailsInitMethods, method)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWailsShutdown checks the given method is a WailsInit method and if it
|
||||||
|
// is, adds it to the list of WailsShutdown methods
|
||||||
|
func (d *DB) AddWailsShutdown(method *BoundMethod) error {
|
||||||
|
err := method.VerifyWailsShutdown()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the db whilst processing and unlock on return
|
||||||
|
d.lock.Lock()
|
||||||
|
defer d.lock.Unlock()
|
||||||
|
|
||||||
|
d.wailsShutdownMethods = append(d.wailsShutdownMethods, method)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToJSON converts the method map to JSON
|
// ToJSON converts the method map to JSON
|
||||||
func (d *DB) ToJSON() (string, error) {
|
func (d *DB) ToJSON() (string, error) {
|
||||||
|
|
||||||
@@ -102,3 +138,13 @@ func (d *DB) ToJSON() (string, error) {
|
|||||||
// Return zero copy string as this string will be read only
|
// Return zero copy string as this string will be read only
|
||||||
return *(*string)(unsafe.Pointer(&bytes)), err
|
return *(*string)(unsafe.Pointer(&bytes)), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WailsInitMethods returns the list of registered WailsInit methods
|
||||||
|
func (d *DB) WailsInitMethods() []*BoundMethod {
|
||||||
|
return d.wailsInitMethods
|
||||||
|
}
|
||||||
|
|
||||||
|
// WailsShutdownMethods returns the list of registered WailsInit methods
|
||||||
|
func (d *DB) WailsShutdownMethods() []*BoundMethod {
|
||||||
|
return d.wailsShutdownMethods
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const backend = {`)
|
|||||||
sortedPackageNames.Add(packageName)
|
sortedPackageNames.Add(packageName)
|
||||||
}
|
}
|
||||||
sortedPackageNames.Sort()
|
sortedPackageNames.Sort()
|
||||||
for _, packageName := range sortedPackageNames.AsSlice() {
|
sortedPackageNames.Each(func(packageName string) {
|
||||||
packages := store[packageName]
|
packages := store[packageName]
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
@@ -67,7 +67,8 @@ const backend = {`)
|
|||||||
sortedStructNames.Add(structName)
|
sortedStructNames.Add(structName)
|
||||||
}
|
}
|
||||||
sortedStructNames.Sort()
|
sortedStructNames.Sort()
|
||||||
for _, structName := range sortedStructNames.AsSlice() {
|
|
||||||
|
sortedStructNames.Each(func(structName string) {
|
||||||
structs := packages[structName]
|
structs := packages[structName]
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
@@ -78,7 +79,7 @@ const backend = {`)
|
|||||||
}
|
}
|
||||||
sortedMethodNames.Sort()
|
sortedMethodNames.Sort()
|
||||||
|
|
||||||
for _, methodName := range sortedMethodNames.AsSlice() {
|
sortedMethodNames.Each(func(methodName string) {
|
||||||
methodDetails := structs[methodName]
|
methodDetails := structs[methodName]
|
||||||
output.WriteString(" /**\n")
|
output.WriteString(" /**\n")
|
||||||
output.WriteString(" * " + methodName + "\n")
|
output.WriteString(" * " + methodName + "\n")
|
||||||
@@ -109,13 +110,16 @@ const backend = {`)
|
|||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
output.WriteString(fmt.Sprintf(" },"))
|
output.WriteString(fmt.Sprintf(" },"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
|
||||||
|
})
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }"))
|
output.WriteString(fmt.Sprintf(" }"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
})
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }\n"))
|
output.WriteString(fmt.Sprintf(" }\n"))
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
}
|
})
|
||||||
|
|
||||||
output.WriteString(`};
|
output.WriteString(`};
|
||||||
export default backend;`)
|
export default backend;`)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package binding
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a
|
// isStructPtr returns true if the value given is a
|
||||||
@@ -13,35 +12,14 @@ func isStructPtr(value interface{}) bool {
|
|||||||
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFunction returns true if the given value is a function
|
func getMethods(value interface{}) ([]*BoundMethod, error) {
|
||||||
func isFunction(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Func
|
|
||||||
}
|
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a struct
|
|
||||||
func isStruct(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|
||||||
|
|
||||||
// Create result placeholder
|
// Create result placeholder
|
||||||
var result []*BoundMethod
|
var result []*BoundMethod
|
||||||
|
|
||||||
// Check type
|
// Check type
|
||||||
if !isStructPtr(value) {
|
if !isStructPtr(value) {
|
||||||
|
return nil, fmt.Errorf("not a pointer to an interface")
|
||||||
if isStruct(value) {
|
|
||||||
name := reflect.ValueOf(value).Type().Name()
|
|
||||||
return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFunction(value) {
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
|
|
||||||
return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct.", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a pointer to a struct.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process Struct
|
// Process Struct
|
||||||
@@ -56,11 +34,6 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
fullMethodName := baseName + "." + methodName
|
fullMethodName := baseName + "." + methodName
|
||||||
method := structValue.MethodByName(methodName)
|
method := structValue.MethodByName(methodName)
|
||||||
|
|
||||||
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
|
||||||
if b.exemptions.Contains(methodReflectName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new method
|
// Create new method
|
||||||
boundMethod := &BoundMethod{
|
boundMethod := &BoundMethod{
|
||||||
Name: fullMethodName,
|
Name: fullMethodName,
|
||||||
@@ -83,8 +56,6 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
boundMethod.Inputs = inputs
|
boundMethod.Inputs = inputs
|
||||||
|
|
||||||
// Iterate outputs
|
// Iterate outputs
|
||||||
// TODO: Determine what to do about limiting return types
|
|
||||||
// especially around errors.
|
|
||||||
outputParamCount := methodType.NumOut()
|
outputParamCount := methodType.NumOut()
|
||||||
var outputs []*Parameter
|
var outputs []*Parameter
|
||||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
||||||
@@ -100,3 +71,39 @@ func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// convertArgToValue
|
||||||
|
func convertArgToValue(input interface{}, target *Parameter) (result reflect.Value, err error) {
|
||||||
|
|
||||||
|
// Catch type conversion panics thrown by convert
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// Modify error
|
||||||
|
err = fmt.Errorf("%s", r.(string)[23:])
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Do the conversion
|
||||||
|
|
||||||
|
// Handle nil values
|
||||||
|
if input == nil {
|
||||||
|
switch target.reflectType.Kind() {
|
||||||
|
case reflect.Chan,
|
||||||
|
reflect.Func,
|
||||||
|
reflect.Interface,
|
||||||
|
reflect.Map,
|
||||||
|
reflect.Ptr,
|
||||||
|
reflect.Slice:
|
||||||
|
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||||
|
default:
|
||||||
|
return reflect.Zero(target.reflectType), fmt.Errorf("Unable to use null value")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't like doing this but it's the only way to
|
||||||
|
// handle recover() correctly
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ type BridgeClient struct {
|
|||||||
messageCache chan string
|
messageCache chan string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b BridgeClient) DeleteTrayMenuByID(id string) {
|
||||||
|
b.session.sendMessage("TD" + id)
|
||||||
|
}
|
||||||
|
|
||||||
func NewBridgeClient() *BridgeClient {
|
func NewBridgeClient() *BridgeClient {
|
||||||
return &BridgeClient{
|
return &BridgeClient{
|
||||||
messageCache: make(chan string, 100),
|
messageCache: make(chan string, 100),
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ type DialogClient struct {
|
|||||||
log *logger.Logger
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DialogClient) DeleteTrayMenuByID(id string) {
|
||||||
|
}
|
||||||
|
|
||||||
func NewDialogClient(log *logger.Logger) *DialogClient {
|
func NewDialogClient(log *logger.Logger) *DialogClient {
|
||||||
return &DialogClient{
|
return &DialogClient{
|
||||||
log: log,
|
log: log,
|
||||||
|
|||||||
89
v2/internal/colour/colour.go
Normal file
89
v2/internal/colour/colour.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package colour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wzshiming/ctc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Col(col ctc.Color, text string) string {
|
||||||
|
return fmt.Sprintf("%s%s%s", col, text, ctc.Reset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Yellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Red(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Blue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Green(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Cyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Magenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func White(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Black(text string) string {
|
||||||
|
return Col(ctc.ForegroundBrightBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkYellow(text string) string {
|
||||||
|
return Col(ctc.ForegroundYellow, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkRed(text string) string {
|
||||||
|
return Col(ctc.ForegroundRed, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlue(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlue, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkGreen(text string) string {
|
||||||
|
return Col(ctc.ForegroundGreen, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkCyan(text string) string {
|
||||||
|
return Col(ctc.ForegroundCyan, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkMagenta(text string) string {
|
||||||
|
return Col(ctc.ForegroundMagenta, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkWhite(text string) string {
|
||||||
|
return Col(ctc.ForegroundWhite, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DarkBlack(text string) string {
|
||||||
|
return Col(ctc.ForegroundBlack, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rainbowCols = []func(string) string{Red, Yellow, Green, Cyan, Blue, Magenta}
|
||||||
|
|
||||||
|
func Rainbow(text string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for i := 0; i < len(text); i++ {
|
||||||
|
fn := rainbowCols[i%len(rainbowCols)]
|
||||||
|
builder.WriteString(fn(text[i : i+1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
13
v2/internal/features/features.go
Normal file
13
v2/internal/features/features.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
// Features holds generic and platform specific feature flags
|
||||||
|
type Features struct {
|
||||||
|
Linux *Linux
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Features object
|
||||||
|
func New() *Features {
|
||||||
|
return &Features{
|
||||||
|
Linux: &Linux{},
|
||||||
|
}
|
||||||
|
}
|
||||||
5
v2/internal/features/features_linux.go
Normal file
5
v2/internal/features/features_linux.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
// Linux holds linux specific feature flags
|
||||||
|
type Linux struct {
|
||||||
|
}
|
||||||
23
v2/internal/ffenestri/features_linux.go
Normal file
23
v2/internal/ffenestri/features_linux.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package ffenestri
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||||
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "ffenestri.h"
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import "github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
|
func (a *Application) processOSFeatureFlags(features *features.Features) {
|
||||||
|
|
||||||
|
// Process Linux features
|
||||||
|
// linux := features.Linux
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,11 +5,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
"github.com/wailsapp/wails/v2/internal/features"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -17,9 +15,6 @@ import (
|
|||||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
|
||||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ffenestri.h"
|
#include "ffenestri.h"
|
||||||
|
|
||||||
@@ -27,16 +22,42 @@ import (
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
|
// DEBUG is the global Ffenestri debug flag.
|
||||||
|
// TODO: move to compile time.
|
||||||
|
var DEBUG bool = true
|
||||||
|
|
||||||
|
// Config defines how our application should be configured
|
||||||
|
type Config struct {
|
||||||
|
Title string
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
MinWidth int
|
||||||
|
MinHeight int
|
||||||
|
MaxWidth int
|
||||||
|
MaxHeight int
|
||||||
|
DevTools bool
|
||||||
|
Resizable bool
|
||||||
|
Fullscreen bool
|
||||||
|
Frameless bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultConfig = &Config{
|
||||||
|
Title: "My Wails App",
|
||||||
|
Width: 800,
|
||||||
|
Height: 600,
|
||||||
|
DevTools: true,
|
||||||
|
Resizable: true,
|
||||||
|
Fullscreen: false,
|
||||||
|
Frameless: false,
|
||||||
|
}
|
||||||
|
|
||||||
// Application is our main application object
|
// Application is our main application object
|
||||||
type Application struct {
|
type Application struct {
|
||||||
config *options.App
|
config *Config
|
||||||
memory []unsafe.Pointer
|
memory []unsafe.Pointer
|
||||||
|
|
||||||
// This is the main app pointer
|
// This is the main app pointer
|
||||||
app *C.struct_Application
|
app unsafe.Pointer
|
||||||
|
|
||||||
// Manages menus
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Logger
|
// Logger
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -57,18 +78,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewApplicationWithConfig creates a new application based on the given config
|
// NewApplicationWithConfig creates a new application based on the given config
|
||||||
func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuManager *menumanager.Manager) *Application {
|
func NewApplicationWithConfig(config *Config, logger *logger.Logger) *Application {
|
||||||
return &Application{
|
return &Application{
|
||||||
config: config,
|
config: config,
|
||||||
logger: logger.CustomLogger("Ffenestri"),
|
logger: logger.CustomLogger("Ffenestri"),
|
||||||
menuManager: menuManager,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApplication creates a new Application with the default config
|
// NewApplication creates a new Application with the default config
|
||||||
func NewApplication(logger *logger.Logger) *Application {
|
func NewApplication(logger *logger.Logger) *Application {
|
||||||
return &Application{
|
return &Application{
|
||||||
config: options.Default,
|
config: defaultConfig,
|
||||||
logger: logger.CustomLogger("Ffenestri"),
|
logger: logger.CustomLogger("Ffenestri"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,29 +121,18 @@ type DispatchClient interface {
|
|||||||
SendMessage(string)
|
SendMessage(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func intToColour(colour int) (C.int, C.int, C.int, C.int) {
|
|
||||||
var alpha = C.int(colour & 0xFF)
|
|
||||||
var blue = C.int((colour >> 8) & 0xFF)
|
|
||||||
var green = C.int((colour >> 16) & 0xFF)
|
|
||||||
var red = C.int((colour >> 24) & 0xFF)
|
|
||||||
return red, green, blue, alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
// Run the application
|
||||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug bool) error {
|
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, features *features.Features) error {
|
||||||
title := a.string2CString(a.config.Title)
|
title := a.string2CString(a.config.Title)
|
||||||
width := C.int(a.config.Width)
|
width := C.int(a.config.Width)
|
||||||
height := C.int(a.config.Height)
|
height := C.int(a.config.Height)
|
||||||
resizable := a.bool2Cint(!a.config.DisableResize)
|
resizable := a.bool2Cint(a.config.Resizable)
|
||||||
devtools := a.bool2Cint(a.config.DevTools)
|
devtools := a.bool2Cint(a.config.DevTools)
|
||||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
||||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen)
|
||||||
logLevel := C.int(a.config.LogLevel)
|
|
||||||
hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose)
|
|
||||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose)
|
|
||||||
|
|
||||||
// Save app reference
|
// Save app reference
|
||||||
a.app = (*C.struct_Application)(app)
|
a.app = unsafe.Pointer(app)
|
||||||
|
|
||||||
// Set Min Window Size
|
// Set Min Window Size
|
||||||
minWidth := C.int(a.config.MinWidth)
|
minWidth := C.int(a.config.MinWidth)
|
||||||
@@ -136,16 +145,11 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
||||||
|
|
||||||
// Set debug if needed
|
// Set debug if needed
|
||||||
C.SetDebug(app, a.bool2Cint(debug))
|
C.SetDebug(app, a.bool2Cint(DEBUG))
|
||||||
|
|
||||||
// TODO: Move frameless to Linux options
|
// Set Frameless
|
||||||
// if a.config.Frameless {
|
if a.config.Frameless {
|
||||||
// C.DisableFrame(a.app)
|
C.DisableFrame(a.app)
|
||||||
// }
|
|
||||||
|
|
||||||
if a.config.RGBA != 0 {
|
|
||||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
|
||||||
C.SetColour(a.app, r, g, b, alpha)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape bindings so C doesn't freak out
|
// Escape bindings so C doesn't freak out
|
||||||
@@ -154,16 +158,13 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
// Set bindings
|
// Set bindings
|
||||||
C.SetBindings(app, a.string2CString(bindings))
|
C.SetBindings(app, a.string2CString(bindings))
|
||||||
|
|
||||||
|
// Process feature flags
|
||||||
|
a.processFeatureFlags(features)
|
||||||
|
|
||||||
// save the dispatcher in a package variable so that the C callbacks
|
// save the dispatcher in a package variable so that the C callbacks
|
||||||
// can access it
|
// can access it
|
||||||
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
||||||
|
|
||||||
// Process platform settings
|
|
||||||
err := a.processPlatformSettings()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we could initialise the application
|
// Check we could initialise the application
|
||||||
if app != nil {
|
if app != nil {
|
||||||
// Yes - Save memory reference and run app, cleaning up afterwards
|
// Yes - Save memory reference and run app, cleaning up afterwards
|
||||||
@@ -185,3 +186,11 @@ func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug
|
|||||||
func messageFromWindowCallback(data *C.char) {
|
func messageFromWindowCallback(data *C.char) {
|
||||||
dispatcher.DispatchMessage(C.GoString(data))
|
dispatcher.DispatchMessage(C.GoString(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Application) processFeatureFlags(features *features.Features) {
|
||||||
|
|
||||||
|
// Process generic features
|
||||||
|
|
||||||
|
// Process OS Specific flags
|
||||||
|
a.processOSFeatureFlags(features)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,43 +2,23 @@
|
|||||||
#define __FFENESTRI_H__
|
#define __FFENESTRI_H__
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
struct Application;
|
|
||||||
|
|
||||||
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose);
|
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen);
|
||||||
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
|
||||||
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
|
||||||
extern void Run(struct Application*, int argc, char **argv);
|
extern void Run(void *app, int argc, char **argv);
|
||||||
extern void DestroyApplication(struct Application*);
|
extern void DestroyApplication(void *app);
|
||||||
extern void SetDebug(struct Application*, int flag);
|
extern void SetDebug(void *app, int flag);
|
||||||
extern void SetBindings(struct Application*, const char *bindings);
|
extern void SetBindings(void *app, const char *bindings);
|
||||||
extern void ExecJS(struct Application*, const char *script);
|
extern void ExecJS(void *app, const char *script);
|
||||||
extern void Hide(struct Application*);
|
extern void Quit(void *app);
|
||||||
extern void Show(struct Application*);
|
extern void SetTitle(void *app, const char *title);
|
||||||
extern void Center(struct Application*);
|
extern void Fullscreen(void *app);
|
||||||
extern void Maximise(struct Application*);
|
extern void UnFullscreen(void *app);
|
||||||
extern void Unmaximise(struct Application*);
|
extern int SetColour(void *app, const char *colourString);
|
||||||
extern void ToggleMaximise(struct Application*);
|
extern void DisableFrame(void *app);
|
||||||
extern void Minimise(struct Application*);
|
extern char *SaveFileDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void Unminimise(struct Application*);
|
extern char *OpenFileDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void ToggleMinimise(struct Application*);
|
extern char *OpenDirectoryDialogOnMainThread(void *appPointer, char *title);
|
||||||
extern void SetColour(struct Application*, int red, int green, int blue, int alpha);
|
|
||||||
extern void SetSize(struct Application*, int width, int height);
|
|
||||||
extern void SetPosition(struct Application*, int x, int y);
|
|
||||||
extern void Quit(struct Application*);
|
|
||||||
extern void SetTitle(struct Application*, const char *title);
|
|
||||||
extern void Fullscreen(struct Application*);
|
|
||||||
extern void UnFullscreen(struct Application*);
|
|
||||||
extern void ToggleFullscreen(struct Application*);
|
|
||||||
extern void DisableFrame(struct Application*);
|
|
||||||
extern void OpenDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories);
|
|
||||||
extern void SaveDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
|
||||||
extern void MessageDialog(struct Application*, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton);
|
|
||||||
extern void DarkModeEnabled(struct Application*, char *callbackID);
|
|
||||||
extern void SetApplicationMenu(struct Application*, const char *);
|
|
||||||
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
|
||||||
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
@@ -67,144 +66,44 @@ func (c *Client) WindowUnFullscreen() {
|
|||||||
C.UnFullscreen(c.app.app)
|
C.UnFullscreen(c.app.app)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowShow will show the window
|
|
||||||
func (c *Client) WindowShow() {
|
|
||||||
C.Show(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowHide will hide the window
|
|
||||||
func (c *Client) WindowHide() {
|
|
||||||
C.Hide(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowCenter will hide the window
|
|
||||||
func (c *Client) WindowCenter() {
|
|
||||||
C.Center(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowMaximise will maximise the window
|
|
||||||
func (c *Client) WindowMaximise() {
|
|
||||||
C.Maximise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowMinimise will minimise the window
|
|
||||||
func (c *Client) WindowMinimise() {
|
|
||||||
C.Minimise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowUnmaximise will unmaximise the window
|
|
||||||
func (c *Client) WindowUnmaximise() {
|
|
||||||
C.Unmaximise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowUnminimise will unminimise the window
|
|
||||||
func (c *Client) WindowUnminimise() {
|
|
||||||
C.Unminimise(c.app.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowPosition will position the window to x,y on the
|
|
||||||
// monitor that the window is mostly on
|
|
||||||
func (c *Client) WindowPosition(x int, y int) {
|
|
||||||
C.SetPosition(c.app.app, C.int(x), C.int(y))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowSize will resize the window to the given
|
|
||||||
// width and height
|
|
||||||
func (c *Client) WindowSize(width int, height int) {
|
|
||||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WindowSetMinSize(width int, height int) {
|
|
||||||
C.SetMinWindowSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) WindowSetMaxSize(width int, height int) {
|
|
||||||
C.SetMaxWindowSize(c.app.app, C.int(width), C.int(height))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WindowSetColour sets the window colour
|
// WindowSetColour sets the window colour
|
||||||
func (c *Client) WindowSetColour(colour int) {
|
func (c *Client) WindowSetColour(colour string) bool {
|
||||||
r, g, b, a := intToColour(colour)
|
result := C.SetColour(c.app.app, c.app.string2CString(colour))
|
||||||
C.SetColour(c.app.app, r, g, b, a)
|
return result == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenDialog will open a dialog with the given title and filter
|
// OpenFileDialog will open a file dialog with the given title
|
||||||
func (c *Client) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
func (c *Client) OpenFileDialog(title string) string {
|
||||||
C.OpenDialog(c.app.app,
|
cstring := C.OpenFileDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
c.app.string2CString(callbackID),
|
var result string
|
||||||
c.app.string2CString(dialogOptions.Title),
|
if cstring != nil {
|
||||||
c.app.string2CString(dialogOptions.Filters),
|
result = C.GoString(cstring)
|
||||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
// Free the C string that was allocated by the dialog
|
||||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
C.free(unsafe.Pointer(cstring))
|
||||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.AllowMultiple),
|
|
||||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.ResolvesAliases),
|
|
||||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveDialog will open a dialog with the given title and filter
|
|
||||||
func (c *Client) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
|
||||||
C.SaveDialog(c.app.app,
|
|
||||||
c.app.string2CString(callbackID),
|
|
||||||
c.app.string2CString(dialogOptions.Title),
|
|
||||||
c.app.string2CString(dialogOptions.Filters),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
|
||||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
|
||||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
|
||||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MessageDialog will open a message dialog with the given options
|
|
||||||
func (c *Client) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
|
||||||
|
|
||||||
// Sanity check button length
|
|
||||||
if len(dialogOptions.Buttons) > 4 {
|
|
||||||
c.app.logger.Error("Given %d message dialog buttons. Maximum is 4", len(dialogOptions.Buttons))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
// Process buttons
|
// SaveFileDialog will open a save file dialog with the given title
|
||||||
buttons := []string{"", "", "", ""}
|
func (c *Client) SaveFileDialog(title string) string {
|
||||||
for i, button := range dialogOptions.Buttons {
|
cstring := C.SaveFileDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
buttons[i] = button
|
var result string
|
||||||
|
if cstring != nil {
|
||||||
|
result = C.GoString(cstring)
|
||||||
|
// Free the C string that was allocated by the dialog
|
||||||
|
C.free(unsafe.Pointer(cstring))
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
C.MessageDialog(c.app.app,
|
|
||||||
c.app.string2CString(callbackID),
|
|
||||||
c.app.string2CString(string(dialogOptions.Type)),
|
|
||||||
c.app.string2CString(dialogOptions.Title),
|
|
||||||
c.app.string2CString(dialogOptions.Message),
|
|
||||||
c.app.string2CString(dialogOptions.Icon),
|
|
||||||
c.app.string2CString(buttons[0]),
|
|
||||||
c.app.string2CString(buttons[1]),
|
|
||||||
c.app.string2CString(buttons[2]),
|
|
||||||
c.app.string2CString(buttons[3]),
|
|
||||||
c.app.string2CString(dialogOptions.DefaultButton),
|
|
||||||
c.app.string2CString(dialogOptions.CancelButton))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
// OpenDirectoryDialog will open a directory dialog with the given title
|
||||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
func (c *Client) OpenDirectoryDialog(title string) string {
|
||||||
}
|
cstring := C.OpenDirectoryDialogOnMainThread(c.app.app, c.app.string2CString(title))
|
||||||
|
var result string
|
||||||
func (c *Client) SetApplicationMenu(applicationMenuJSON string) {
|
if cstring != nil {
|
||||||
C.SetApplicationMenu(c.app.app, c.app.string2CString(applicationMenuJSON))
|
result = C.GoString(cstring)
|
||||||
}
|
// Free the C string that was allocated by the dialog
|
||||||
|
C.free(unsafe.Pointer(cstring))
|
||||||
func (c *Client) SetTrayMenu(trayMenuJSON string) {
|
}
|
||||||
C.SetTrayMenu(c.app.app, c.app.string2CString(trayMenuJSON))
|
return result
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
|
||||||
C.UpdateTrayMenuLabel(c.app.app, c.app.string2CString(JSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
|
||||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ struct hashmap_s dialogIconCache;
|
|||||||
// Dispatch Method
|
// Dispatch Method
|
||||||
typedef void (^dispatchMethod)(void);
|
typedef void (^dispatchMethod)(void);
|
||||||
|
|
||||||
|
TrayMenuStore *TrayMenuStoreSingleton;
|
||||||
|
|
||||||
// dispatch will execute the given `func` pointer
|
// dispatch will execute the given `func` pointer
|
||||||
void dispatch(dispatchMethod func) {
|
void dispatch(dispatchMethod func) {
|
||||||
dispatch_async(dispatch_get_main_queue(), func);
|
dispatch_async(dispatch_get_main_queue(), func);
|
||||||
@@ -46,6 +48,18 @@ int hashmap_log(void *const context, struct hashmap_element_s *const e) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void filelog(const char *message) {
|
||||||
|
FILE *fp = fopen("/tmp/wailslog.txt", "ab");
|
||||||
|
if (fp != NULL)
|
||||||
|
{
|
||||||
|
fputs(message, fp);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The delegate class for tray menus
|
||||||
|
Class trayMenuDelegateClass;
|
||||||
|
|
||||||
// Utility function to visualise a hashmap
|
// Utility function to visualise a hashmap
|
||||||
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
||||||
printf("%s = { ", name);
|
printf("%s = { ", name);
|
||||||
@@ -79,6 +93,7 @@ struct Application {
|
|||||||
id mouseEvent;
|
id mouseEvent;
|
||||||
id mouseDownMonitor;
|
id mouseDownMonitor;
|
||||||
id mouseUpMonitor;
|
id mouseUpMonitor;
|
||||||
|
int activationPolicy;
|
||||||
|
|
||||||
// Window Data
|
// Window Data
|
||||||
const char *title;
|
const char *title;
|
||||||
@@ -112,13 +127,12 @@ struct Application {
|
|||||||
int useToolBar;
|
int useToolBar;
|
||||||
int hideToolbarSeparator;
|
int hideToolbarSeparator;
|
||||||
int windowBackgroundIsTranslucent;
|
int windowBackgroundIsTranslucent;
|
||||||
|
int hasURLHandlers;
|
||||||
|
const char *startupURL;
|
||||||
|
|
||||||
// Menu
|
// Menu
|
||||||
Menu *applicationMenu;
|
Menu *applicationMenu;
|
||||||
|
|
||||||
// Tray
|
|
||||||
TrayMenuStore* trayMenuStore;
|
|
||||||
|
|
||||||
// Context Menus
|
// Context Menus
|
||||||
ContextMenuStore *contextMenuStore;
|
ContextMenuStore *contextMenuStore;
|
||||||
|
|
||||||
@@ -131,6 +145,9 @@ struct Application {
|
|||||||
// shutting down flag
|
// shutting down flag
|
||||||
bool shuttingDown;
|
bool shuttingDown;
|
||||||
|
|
||||||
|
// Running flag
|
||||||
|
bool running;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug works like sprintf but mutes if the global debug flag is true
|
// Debug works like sprintf but mutes if the global debug flag is true
|
||||||
@@ -253,7 +270,7 @@ void Hide(struct Application *app) {
|
|||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
msg(app->application, s("hide:"))
|
msg(app->mainWindow, s("orderOut:"));
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,8 +308,19 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
|||||||
// TODO: Check this actually does reduce flicker
|
// TODO: Check this actually does reduce flicker
|
||||||
msg(app->config, s("setValue:forKey:"), msg(c("NSNumber"), s("numberWithBool:"), 0), str("suppressesIncrementalRendering"));
|
msg(app->config, s("setValue:forKey:"), msg(c("NSNumber"), s("numberWithBool:"), 0), str("suppressesIncrementalRendering"));
|
||||||
|
|
||||||
|
// We are now running!
|
||||||
|
app->running = true;
|
||||||
|
|
||||||
|
|
||||||
// Notify backend we are ready (system startup)
|
// Notify backend we are ready (system startup)
|
||||||
app->sendMessageToBackend("SS");
|
const char *readyMessage = "SS";
|
||||||
|
if( app->startupURL == NULL ) {
|
||||||
|
app->sendMessageToBackend("SS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readyMessage = concat("SS", app->startupURL);
|
||||||
|
app->sendMessageToBackend(readyMessage);
|
||||||
|
MEMFREE(readyMessage);
|
||||||
|
|
||||||
} else if( strcmp(name, "windowDrag") == 0 ) {
|
} else if( strcmp(name, "windowDrag") == 0 ) {
|
||||||
// Guard against null events
|
// Guard against null events
|
||||||
@@ -390,6 +418,11 @@ void ExecJS(struct Application *app, const char *js) {
|
|||||||
|
|
||||||
void willFinishLaunching(id self, SEL cmd, id sender) {
|
void willFinishLaunching(id self, SEL cmd, id sender) {
|
||||||
struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
||||||
|
// If there are URL Handlers, register a listener for them
|
||||||
|
if( app->hasURLHandlers ) {
|
||||||
|
id eventManager = msg(c("NSAppleEventManager"), s("sharedAppleEventManager"));
|
||||||
|
msg(eventManager, s("setEventHandler:andSelector:forEventClass:andEventID:"), self, s("getUrl:withReplyEvent:"), kInternetEventClass, kAEGetURL);
|
||||||
|
}
|
||||||
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,12 +447,6 @@ void themeChanged(id self, SEL cmd, id sender) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void willFinishLaunching(id self) {
|
|
||||||
// struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
|
||||||
// Debug(app, "willFinishLaunching called!");
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
int releaseNSObject(void *const context, struct hashmap_element_s *const e) {
|
int releaseNSObject(void *const context, struct hashmap_element_s *const e) {
|
||||||
msg(e->data, s("release"));
|
msg(e->data, s("release"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -452,6 +479,10 @@ void DestroyApplication(struct Application *app) {
|
|||||||
Debug(app, "Almost a double free for app->bindings");
|
Debug(app, "Almost a double free for app->bindings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( app->startupURL != NULL ) {
|
||||||
|
MEMFREE(app->startupURL);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove mouse monitors
|
// Remove mouse monitors
|
||||||
if( app->mouseDownMonitor != NULL ) {
|
if( app->mouseDownMonitor != NULL ) {
|
||||||
msg( c("NSEvent"), s("removeMonitor:"), app->mouseDownMonitor);
|
msg( c("NSEvent"), s("removeMonitor:"), app->mouseDownMonitor);
|
||||||
@@ -466,7 +497,7 @@ void DestroyApplication(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the tray menu store
|
// Delete the tray menu store
|
||||||
DeleteTrayMenuStore(app->trayMenuStore);
|
DeleteTrayMenuStore(TrayMenuStoreSingleton);
|
||||||
|
|
||||||
// Delete the context menu store
|
// Delete the context menu store
|
||||||
DeleteContextMenuStore(app->contextMenuStore);
|
DeleteContextMenuStore(app->contextMenuStore);
|
||||||
@@ -498,31 +529,6 @@ void DestroyApplication(struct Application *app) {
|
|||||||
Debug(app, "Finished Destroying Application");
|
Debug(app, "Finished Destroying Application");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit will stop the cocoa application and free up all the memory
|
|
||||||
// used by the application
|
|
||||||
void Quit(struct Application *app) {
|
|
||||||
Debug(app, "Quit Called");
|
|
||||||
ON_MAIN_THREAD (
|
|
||||||
// Terminate app
|
|
||||||
msg(app->application, s("stop:"), NULL);
|
|
||||||
id fakeevent = msg(c("NSEvent"),
|
|
||||||
s("otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:"),
|
|
||||||
15, // Type
|
|
||||||
msg(c("CGPoint"), s("init:x:y:"), 0, 0), // location
|
|
||||||
0, // flags
|
|
||||||
0, // timestamp
|
|
||||||
0, // window
|
|
||||||
NULL, // context
|
|
||||||
0, // subtype
|
|
||||||
0, // data1
|
|
||||||
0 // data2
|
|
||||||
);
|
|
||||||
msg(c("NSApp"), s("postEvent:atStart:"), fakeevent, true);
|
|
||||||
// msg(c(app->mainWindow), s("performClose:"))
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTitle sets the main window title to the given string
|
// SetTitle sets the main window title to the given string
|
||||||
void SetTitle(struct Application *app, const char *title) {
|
void SetTitle(struct Application *app, const char *title) {
|
||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
@@ -1058,7 +1064,7 @@ void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
|||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
AddTrayMenuToStore(TrayMenuStoreSingleton, trayMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||||
@@ -1067,7 +1073,13 @@ void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
|||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
UpdateTrayMenuInStore(TrayMenuStoreSingleton, trayMenuJSON);
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteTrayMenuByID(struct Application *app, const char *id) {
|
||||||
|
ON_MAIN_THREAD(
|
||||||
|
DeleteTrayMenuInStore(TrayMenuStoreSingleton, id);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1076,7 +1088,7 @@ void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
|||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
UpdateTrayMenuLabelInStore(TrayMenuStoreSingleton, JSON);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1137,7 +1149,7 @@ void processDecorations(struct Application *app) {
|
|||||||
void createApplication(struct Application *app) {
|
void createApplication(struct Application *app) {
|
||||||
id application = msg(c("NSApplication"), s("sharedApplication"));
|
id application = msg(c("NSApplication"), s("sharedApplication"));
|
||||||
app->application = application;
|
app->application = application;
|
||||||
msg(application, s("setActivationPolicy:"), 0);
|
msg(application, s("setActivationPolicy:"), app->activationPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
||||||
@@ -1161,27 +1173,59 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getURL(id self, SEL selector, id event, id replyEvent) {
|
||||||
|
struct Application *app = (struct Application *)objc_getAssociatedObject(self, "application");
|
||||||
|
id desc = msg(event, s("paramDescriptorForKeyword:"), keyDirectObject);
|
||||||
|
id url = msg(desc, s("stringValue"));
|
||||||
|
const char* curl = cstr(url);
|
||||||
|
if( curl == NULL ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this was an incoming URL, but we aren't running yet
|
||||||
|
// save it to return when we complete
|
||||||
|
if( app->running != true ) {
|
||||||
|
app->startupURL = STRCOPY(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* message = concat("UC", curl);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openURLs(id self, SEL selector, id event) {
|
||||||
|
filelog("\n\nI AM HERE!!!!!\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void createDelegate(struct Application *app) {
|
void createDelegate(struct Application *app) {
|
||||||
// Define delegate
|
|
||||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
// Define delegate
|
||||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
Class appDelegate = objc_allocateClassPair((Class) c("NSResponder"), "AppDelegate", 0);
|
||||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
class_addProtocol(appDelegate, objc_getProtocol("NSTouchBarProvider"));
|
||||||
// class_addMethod(delegateClass, s("applicationWillTerminate:"), (IMP) closeWindow, "v@:@");
|
|
||||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
class_addMethod(appDelegate, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||||
|
class_addMethod(appDelegate, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||||
|
|
||||||
// All Menu Items use a common callback
|
// All Menu Items use a common callback
|
||||||
class_addMethod(delegateClass, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
|
class_addMethod(appDelegate, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
|
||||||
|
|
||||||
|
// If there are URL Handlers, register the callback method
|
||||||
|
if( app->hasURLHandlers ) {
|
||||||
|
class_addMethod(appDelegate, s("getUrl:withReplyEvent:"), (IMP) getURL, "i@:@@");
|
||||||
|
}
|
||||||
|
|
||||||
// Script handler
|
// Script handler
|
||||||
class_addMethod(delegateClass, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
class_addMethod(appDelegate, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
||||||
objc_registerClassPair(delegateClass);
|
objc_registerClassPair(appDelegate);
|
||||||
|
|
||||||
// Create delegate
|
// Create delegate
|
||||||
id delegate = msg((id)delegateClass, s("new"));
|
id delegate = msg((id)appDelegate, s("new"));
|
||||||
objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN);
|
objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
// Theme change listener
|
// Theme change listener
|
||||||
class_addMethod(delegateClass, s("themeChanged:"), (IMP) themeChanged, "v@:@@");
|
class_addMethod(appDelegate, s("themeChanged:"), (IMP) themeChanged, "v@:@@");
|
||||||
|
|
||||||
// Get defaultCenter
|
// Get defaultCenter
|
||||||
id defaultCenter = msg(c("NSDistributedNotificationCenter"), s("defaultCenter"));
|
id defaultCenter = msg(c("NSDistributedNotificationCenter"), s("defaultCenter"));
|
||||||
@@ -1193,15 +1237,21 @@ void createDelegate(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool windowShouldClose(id self, SEL cmd, id sender) {
|
bool windowShouldClose(id self, SEL cmd, id sender) {
|
||||||
msg(sender, s("orderBack:"));
|
msg(sender, s("orderOut:"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool windowShouldExit(id self, SEL cmd, id sender) {
|
||||||
|
msg(sender, s("orderOut:"));
|
||||||
|
messageFromWindowCallback("WC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void createMainWindow(struct Application *app) {
|
void createMainWindow(struct Application *app) {
|
||||||
// Create main window
|
// Create main window
|
||||||
id mainWindow = ALLOC("NSWindow");
|
id mainWindow = ALLOC("NSWindow");
|
||||||
mainWindow = msg(mainWindow, s("initWithContentRect:styleMask:backing:defer:"),
|
mainWindow = msg(mainWindow, s("initWithContentRect:styleMask:backing:defer:"),
|
||||||
CGRectMake(0, 0, app->width, app->height), app->decorations, NSBackingStoreBuffered, NO);
|
CGRectMake(0, 0, app->width, app->height), app->decorations, NSBackingStoreBuffered, NO);
|
||||||
msg(mainWindow, s("autorelease"));
|
msg(mainWindow, s("autorelease"));
|
||||||
|
|
||||||
// Set Appearance
|
// Set Appearance
|
||||||
@@ -1215,14 +1265,16 @@ void createMainWindow(struct Application *app) {
|
|||||||
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
msg(mainWindow, s("setTitlebarAppearsTransparent:"), app->titlebarAppearsTransparent ? YES : NO);
|
||||||
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
msg(mainWindow, s("setTitleVisibility:"), app->hideTitle);
|
||||||
|
|
||||||
if( app->hideWindowOnClose ) {
|
// Create window delegate to override windowShouldClose
|
||||||
// Create window delegate to override windowShouldClose
|
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "WindowDelegate", 0);
|
||||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "WindowDelegate", 0);
|
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSWindowDelegate"));
|
||||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSWindowDelegate"));
|
if( app->hideWindowOnClose ) {
|
||||||
class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldClose, "v@:@");
|
class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldClose, "v@:@");
|
||||||
app->windowDelegate = msg((id)delegateClass, s("new"));
|
} else {
|
||||||
msg(mainWindow, s("setDelegate:"), app->windowDelegate);
|
class_replaceMethod(delegateClass, s("windowShouldClose:"), (IMP) windowShouldExit, "v@:@");
|
||||||
}
|
}
|
||||||
|
app->windowDelegate = msg((id)delegateClass, s("new"));
|
||||||
|
msg(mainWindow, s("setDelegate:"), app->windowDelegate);
|
||||||
|
|
||||||
app->mainWindow = mainWindow;
|
app->mainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
@@ -1641,6 +1693,35 @@ void processUserDialogIcons(struct Application *app) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrayMenuWillOpen(id self, SEL selector, id menu) {
|
||||||
|
// Extract tray menu id from menu
|
||||||
|
id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID");
|
||||||
|
const char* trayMenuID = cstr(trayMenuIDStr);
|
||||||
|
const char *message = concat("Mo", trayMenuID);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrayMenuDidClose(id self, SEL selector, id menu) {
|
||||||
|
// Extract tray menu id from menu
|
||||||
|
id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID");
|
||||||
|
const char* trayMenuID = cstr(trayMenuIDStr);
|
||||||
|
const char *message = concat("Mc", trayMenuID);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createTrayMenuDelegate() {
|
||||||
|
// Define delegate
|
||||||
|
trayMenuDelegateClass = objc_allocateClassPair((Class) c("NSObject"), "MenuDelegate", 0);
|
||||||
|
class_addProtocol(trayMenuDelegateClass, objc_getProtocol("NSMenuDelegate"));
|
||||||
|
class_addMethod(trayMenuDelegateClass, s("menuWillOpen:"), (IMP) TrayMenuWillOpen, "v@:@");
|
||||||
|
class_addMethod(trayMenuDelegateClass, s("menuDidClose:"), (IMP) TrayMenuDidClose, "v@:@");
|
||||||
|
|
||||||
|
// Script handler
|
||||||
|
objc_registerClassPair(trayMenuDelegateClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Run(struct Application *app, int argc, char **argv) {
|
void Run(struct Application *app, int argc, char **argv) {
|
||||||
|
|
||||||
@@ -1653,6 +1734,9 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
// Define delegate
|
// Define delegate
|
||||||
createDelegate(app);
|
createDelegate(app);
|
||||||
|
|
||||||
|
// Define tray delegate
|
||||||
|
createTrayMenuDelegate();
|
||||||
|
|
||||||
// Create the main window
|
// Create the main window
|
||||||
createMainWindow(app);
|
createMainWindow(app);
|
||||||
|
|
||||||
@@ -1823,7 +1907,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup initial trays
|
// Setup initial trays
|
||||||
ShowTrayMenusInStore(app->trayMenuStore);
|
ShowTrayMenusInStore(TrayMenuStoreSingleton);
|
||||||
|
|
||||||
// Process dialog icons
|
// Process dialog icons
|
||||||
processUserDialogIcons(app);
|
processUserDialogIcons(app);
|
||||||
@@ -1837,6 +1921,23 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
MEMFREE(internalCode);
|
MEMFREE(internalCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetActivationPolicy(struct Application* app, int policy) {
|
||||||
|
app->activationPolicy = policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HasURLHandlers(struct Application* app) {
|
||||||
|
app->hasURLHandlers = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit will stop the cocoa application and free up all the memory
|
||||||
|
// used by the application
|
||||||
|
void Quit(struct Application *app) {
|
||||||
|
Debug(app, "Quit Called");
|
||||||
|
msg(app->application, s("stop:"), NULL);
|
||||||
|
SetSize(app, 0, 0);
|
||||||
|
Show(app);
|
||||||
|
Hide(app);
|
||||||
|
}
|
||||||
|
|
||||||
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
void* NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||||
|
|
||||||
@@ -1883,7 +1984,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->applicationMenu = NULL;
|
result->applicationMenu = NULL;
|
||||||
|
|
||||||
// Tray
|
// Tray
|
||||||
result->trayMenuStore = NewTrayMenuStore();
|
TrayMenuStoreSingleton = NewTrayMenuStore();
|
||||||
|
|
||||||
// Context Menus
|
// Context Menus
|
||||||
result->contextMenuStore = NewContextMenuStore();
|
result->contextMenuStore = NewContextMenuStore();
|
||||||
@@ -1899,6 +2000,14 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
|
|
||||||
result->shuttingDown = false;
|
result->shuttingDown = false;
|
||||||
|
|
||||||
|
result->activationPolicy = NSApplicationActivationPolicyRegular;
|
||||||
|
|
||||||
|
result->hasURLHandlers = 0;
|
||||||
|
|
||||||
|
result->startupURL = NULL;
|
||||||
|
|
||||||
|
result->running = false;
|
||||||
|
|
||||||
return (void*) result;
|
return (void*) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ func (a *Application) processPlatformSettings() error {
|
|||||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set activation policy
|
||||||
|
C.SetActivationPolicy(a.app, C.int(mac.ActivationPolicy))
|
||||||
|
|
||||||
// Check if the webview should be transparent
|
// Check if the webview should be transparent
|
||||||
if mac.WebviewIsTransparent {
|
if mac.WebviewIsTransparent {
|
||||||
C.WebviewIsTransparent(a.app)
|
C.WebviewIsTransparent(a.app)
|
||||||
@@ -87,5 +90,10 @@ func (a *Application) processPlatformSettings() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process URL Handlers
|
||||||
|
if a.config.Mac.URLHandlers != nil {
|
||||||
|
C.HasURLHandlers(a.app)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
// Macros to make it slightly more sane
|
// Macros to make it slightly more sane
|
||||||
#define msg objc_msgSend
|
#define msg objc_msgSend
|
||||||
|
|
||||||
|
#define kInternetEventClass 'GURL'
|
||||||
|
#define kAEGetURL 'GURL'
|
||||||
|
#define keyDirectObject '----'
|
||||||
|
|
||||||
#define c(str) (id)objc_getClass(str)
|
#define c(str) (id)objc_getClass(str)
|
||||||
#define s(str) sel_registerName(str)
|
#define s(str) sel_registerName(str)
|
||||||
#define u(str) sel_getUid(str)
|
#define u(str) sel_getUid(str)
|
||||||
@@ -66,6 +70,10 @@
|
|||||||
#define NSControlStateValueOff 0
|
#define NSControlStateValueOff 0
|
||||||
#define NSControlStateValueOn 1
|
#define NSControlStateValueOn 1
|
||||||
|
|
||||||
|
#define NSApplicationActivationPolicyRegular 0
|
||||||
|
#define NSApplicationActivationPolicyAccessory 1
|
||||||
|
#define NSApplicationActivationPolicyProhibited 2
|
||||||
|
|
||||||
// Unbelievably, if the user swaps their button preference
|
// Unbelievably, if the user swaps their button preference
|
||||||
// then right buttons are reported as left buttons
|
// then right buttons are reported as left buttons
|
||||||
#define NSEventMaskLeftMouseDown 1 << 1
|
#define NSEventMaskLeftMouseDown 1 << 1
|
||||||
@@ -110,6 +118,10 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
|||||||
//void SetContextMenus(struct Application* app, const char *);
|
//void SetContextMenus(struct Application* app, const char *);
|
||||||
void AddTrayMenu(struct Application* app, const char *);
|
void AddTrayMenu(struct Application* app, const char *);
|
||||||
|
|
||||||
|
void SetActivationPolicy(struct Application* app, int policy);
|
||||||
|
|
||||||
void* lookupStringConstant(id constantName);
|
void* lookupStringConstant(id constantName);
|
||||||
|
|
||||||
|
void HasURLHandlers(struct Application* app);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -80,7 +80,6 @@ struct Application
|
|||||||
int height;
|
int height;
|
||||||
int resizable;
|
int resizable;
|
||||||
int devtools;
|
int devtools;
|
||||||
int startHidden;
|
|
||||||
int fullscreen;
|
int fullscreen;
|
||||||
int minWidth;
|
int minWidth;
|
||||||
int minHeight;
|
int minHeight;
|
||||||
@@ -101,7 +100,7 @@ struct Application
|
|||||||
int lock;
|
int lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden)
|
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen)
|
||||||
{
|
{
|
||||||
// Setup main application struct
|
// Setup main application struct
|
||||||
struct Application *result = malloc(sizeof(struct Application));
|
struct Application *result = malloc(sizeof(struct Application));
|
||||||
@@ -116,11 +115,15 @@ void *NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->maxWidth = 0;
|
result->maxWidth = 0;
|
||||||
result->maxHeight = 0;
|
result->maxHeight = 0;
|
||||||
result->frame = 1;
|
result->frame = 1;
|
||||||
result->startHidden = startHidden;
|
|
||||||
|
|
||||||
// Default drag button is PRIMARY
|
// Default drag button is PRIMARY
|
||||||
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
||||||
|
|
||||||
|
// printf("\n\nWidth: %d\n", result->width);
|
||||||
|
// printf("Height: %d\n\n", result->height);
|
||||||
|
|
||||||
|
// Features
|
||||||
|
|
||||||
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
||||||
|
|
||||||
// Create a unique ID based on the current unix timestamp
|
// Create a unique ID based on the current unix timestamp
|
||||||
@@ -136,9 +139,10 @@ void *NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
return (void *)result;
|
return (void *)result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DestroyApplication(struct Application *app)
|
void DestroyApplication(void *appPointer)
|
||||||
{
|
{
|
||||||
Debug("Destroying Application");
|
Debug("Destroying Application");
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
|
||||||
g_application_quit(G_APPLICATION(app->application));
|
g_application_quit(G_APPLICATION(app->application));
|
||||||
|
|
||||||
@@ -167,11 +171,9 @@ void DestroyApplication(struct Application *app)
|
|||||||
// Disconnect signal handlers
|
// Disconnect signal handlers
|
||||||
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
||||||
g_signal_handler_disconnect(manager, app->signalInvoke);
|
g_signal_handler_disconnect(manager, app->signalInvoke);
|
||||||
if( app->frame == 0) {
|
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
||||||
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
||||||
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
||||||
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
|
||||||
}
|
|
||||||
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
||||||
|
|
||||||
// Release the main GTK Application
|
// Release the main GTK Application
|
||||||
@@ -189,34 +191,45 @@ void DestroyApplication(struct Application *app)
|
|||||||
|
|
||||||
// Quit will stop the gtk application and free up all the memory
|
// Quit will stop the gtk application and free up all the memory
|
||||||
// used by the application
|
// used by the application
|
||||||
void Quit(struct Application *app)
|
void Quit(void *appPointer)
|
||||||
{
|
{
|
||||||
Debug("Quit Called");
|
Debug("Quit Called");
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_close((GtkWindow *)app->mainWindow);
|
gtk_window_close((GtkWindow *)app->mainWindow);
|
||||||
g_application_quit((GApplication *)app->application);
|
g_application_quit((GApplication *)app->application);
|
||||||
DestroyApplication(app);
|
DestroyApplication(appPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTitle sets the main window title to the given string
|
// SetTitle sets the main window title to the given string
|
||||||
void SetTitle(struct Application *app, const char *title)
|
void SetTitle(void *appPointer, const char *title)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_set_title(app->mainWindow, title);
|
gtk_window_set_title(app->mainWindow, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fullscreen sets the main window to be fullscreen
|
// Fullscreen sets the main window to be fullscreen
|
||||||
void Fullscreen(struct Application *app)
|
void Fullscreen(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_fullscreen(app->mainWindow);
|
gtk_window_fullscreen(app->mainWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnFullscreen resets the main window after a fullscreen
|
// UnFullscreen resets the main window after a fullscreen
|
||||||
void UnFullscreen(struct Application *app)
|
void UnFullscreen(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
gtk_window_unfullscreen(app->mainWindow);
|
gtk_window_unfullscreen(app->mainWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setMinMaxSize(struct Application *app)
|
void Center(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMinMaxSize(void *appPointer)
|
||||||
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GdkGeometry size;
|
GdkGeometry size;
|
||||||
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
@@ -232,43 +245,32 @@ void setMinMaxSize(struct Application *app)
|
|||||||
size.min_width = app->minWidth;
|
size.min_width = app->minWidth;
|
||||||
flags |= GDK_HINT_MIN_SIZE;
|
flags |= GDK_HINT_MIN_SIZE;
|
||||||
}
|
}
|
||||||
|
Debug("size: %dx%d", app->width, app->height);
|
||||||
|
Debug("min: %dx%d", size.min_width, size.min_height);
|
||||||
|
Debug("max: %dx%d", size.max_width, size.max_height);
|
||||||
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) {
|
// OpenFileDialog opens a dialog to select a file
|
||||||
|
// NOTE: The result is a string that will need to be freed!
|
||||||
|
char *OpenFileDialog(void *appPointer, char *title)
|
||||||
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GtkFileChooserNative *native;
|
GtkFileChooserNative *native;
|
||||||
GtkFileChooserAction action = chooserAction;
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||||
gint res;
|
gint res;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
char *title = args[0];
|
|
||||||
char *filter = args[1];
|
|
||||||
|
|
||||||
native = gtk_file_chooser_native_new(title,
|
native = gtk_file_chooser_native_new(title,
|
||||||
app->mainWindow,
|
app->mainWindow,
|
||||||
action,
|
action,
|
||||||
"_Open",
|
"_Open",
|
||||||
"_Cancel");
|
"_Cancel");
|
||||||
|
|
||||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
|
||||||
|
|
||||||
// If we have filters, process them
|
|
||||||
if (filter[0] != '\0') {
|
|
||||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
|
||||||
gchar **filters = g_strsplit(filter, ",", -1);
|
|
||||||
gint i;
|
|
||||||
for(i = 0; filters && filters[i]; i++) {
|
|
||||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
|
||||||
// Debug("Adding filter pattern: %s\n", filters[i]);
|
|
||||||
}
|
|
||||||
gtk_file_filter_set_name(file_filter, filter);
|
|
||||||
gtk_file_chooser_add_filter(chooser, file_filter);
|
|
||||||
g_strfreev(filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
if (res == GTK_RESPONSE_ACCEPT)
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
{
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
filename = gtk_file_chooser_get_filename(chooser);
|
filename = gtk_file_chooser_get_filename(chooser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,65 +279,104 @@ char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAc
|
|||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// openFileDialogInternal opens a dialog to select a file
|
// SaveFileDialog opens a dialog to select a file
|
||||||
// NOTE: The result is a string that will need to be freed!
|
// NOTE: The result is a string that will need to be freed!
|
||||||
char *openFileDialogInternal(struct Application *app, char **args)
|
char *SaveFileDialog(void *appPointer, char *title)
|
||||||
{
|
{
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args);
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
GtkFileChooserNative *native;
|
||||||
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
|
||||||
|
gint res;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
native = gtk_file_chooser_native_new(title,
|
||||||
|
app->mainWindow,
|
||||||
|
action,
|
||||||
|
"_Save",
|
||||||
|
"_Cancel");
|
||||||
|
|
||||||
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
|
filename = gtk_file_chooser_get_filename(chooser);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(native);
|
||||||
|
|
||||||
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveFileDialogInternal opens a dialog to select a file
|
// OpenDirectoryDialog opens a dialog to select a directory
|
||||||
// NOTE: The result is a string that will need to be freed!
|
// NOTE: The result is a string that will need to be freed!
|
||||||
char *saveFileDialogInternal(struct Application *app, char **args)
|
char *OpenDirectoryDialog(void *appPointer, char *title)
|
||||||
{
|
{
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args);
|
struct Application *app = (struct Application *)appPointer;
|
||||||
|
GtkFileChooserNative *native;
|
||||||
|
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
|
||||||
|
gint res;
|
||||||
|
char *foldername;
|
||||||
|
|
||||||
|
native = gtk_file_chooser_native_new(title,
|
||||||
|
app->mainWindow,
|
||||||
|
action,
|
||||||
|
"_Open",
|
||||||
|
"_Cancel");
|
||||||
|
|
||||||
|
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||||
|
if (res == GTK_RESPONSE_ACCEPT)
|
||||||
|
{
|
||||||
|
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||||
|
foldername = gtk_file_chooser_get_filename(chooser);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_unref(native);
|
||||||
|
|
||||||
|
return foldername;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetMinWindowSize(void *appPointer, int minWidth, int minHeight)
|
||||||
// openDirectoryDialogInternal opens a dialog to select a directory
|
|
||||||
// NOTE: The result is a string that will need to be freed!
|
|
||||||
char *openDirectoryDialogInternal(struct Application *app, char **args)
|
|
||||||
{
|
|
||||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->minWidth = minWidth;
|
app->minWidth = minWidth;
|
||||||
app->minHeight = minHeight;
|
app->minHeight = minHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
void SetMaxWindowSize(void *appPointer, int maxWidth, int maxHeight)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->maxWidth = maxWidth;
|
app->maxWidth = maxWidth;
|
||||||
app->maxHeight = maxHeight;
|
app->maxHeight = maxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetColour sets the colour of the webview to the given colour string
|
// SetColour sets the colour of the webview to the given colour string
|
||||||
int SetColour(struct Application *app, const char *colourString)
|
int SetColour(void *appPointer, const char *colourString)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
GdkRGBA rgba;
|
GdkRGBA rgba;
|
||||||
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||||
if (result == FALSE)
|
if (result == FALSE)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Debug("Setting webview colour to: %s", colourString);
|
Debug("Setting webview colour to: %s", colourString);
|
||||||
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableFrame disables the window frame
|
// DisableFrame disables the window frame
|
||||||
void DisableFrame(struct Application *app)
|
void DisableFrame(void *appPointer)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)appPointer;
|
||||||
app->frame = 0;
|
app->frame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void syncCallback(GObject *source_object,
|
void syncCallback(GObject *source_object,
|
||||||
GAsyncResult *res,
|
GAsyncResult *res,
|
||||||
void *data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
struct Application *app = (struct Application *)data;
|
|
||||||
|
struct Application *app = (struct Application *)user_data;
|
||||||
app->lock = 0;
|
app->lock = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,6 +384,8 @@ void syncEval(struct Application *app, const gchar *script)
|
|||||||
{
|
{
|
||||||
|
|
||||||
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
||||||
|
// Debug("[%p] webview\n", webView);
|
||||||
|
// Debug("[%p] Running sync\n", script);
|
||||||
|
|
||||||
// wait for lock to free
|
// wait for lock to free
|
||||||
while (app->lock == 1)
|
while (app->lock == 1)
|
||||||
@@ -352,15 +395,18 @@ void syncEval(struct Application *app, const gchar *script)
|
|||||||
// Set lock
|
// Set lock
|
||||||
app->lock = 1;
|
app->lock = 1;
|
||||||
|
|
||||||
|
//
|
||||||
webkit_web_view_run_javascript(
|
webkit_web_view_run_javascript(
|
||||||
webView,
|
webView,
|
||||||
script,
|
script,
|
||||||
NULL, syncCallback, (void*)app);
|
NULL, syncCallback, app);
|
||||||
|
|
||||||
while (app->lock == 1)
|
while (app->lock == 1)
|
||||||
{
|
{
|
||||||
|
// Debug("[%p] Waiting for callback\n", script);
|
||||||
g_main_context_iteration(0, true);
|
g_main_context_iteration(0, true);
|
||||||
}
|
}
|
||||||
|
// Debug("[%p] Finished\n", script);
|
||||||
}
|
}
|
||||||
|
|
||||||
void asyncEval(WebKitWebView *webView, const gchar *script)
|
void asyncEval(WebKitWebView *webView, const gchar *script)
|
||||||
@@ -371,7 +417,7 @@ void asyncEval(WebKitWebView *webView, const gchar *script)
|
|||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef void (*dispatchMethod)(struct Application *app, void *);
|
typedef void (*dispatchMethod)(void *app, void *);
|
||||||
|
|
||||||
struct dispatchData
|
struct dispatchData
|
||||||
{
|
{
|
||||||
@@ -383,14 +429,20 @@ struct dispatchData
|
|||||||
gboolean executeMethod(gpointer data)
|
gboolean executeMethod(gpointer data)
|
||||||
{
|
{
|
||||||
struct dispatchData *d = (struct dispatchData *)data;
|
struct dispatchData *d = (struct dispatchData *)data;
|
||||||
(d->method)(d->app, d->args);
|
struct Application *app = (struct Application *)(d->app);
|
||||||
|
// Debug("Webview %p\n", app->webView);
|
||||||
|
// Debug("Args %s\n", d->args);
|
||||||
|
// Debug("Method %p\n", (d->method));
|
||||||
|
(d->method)(app, d->args);
|
||||||
|
// Debug("Method Execute Complete. Freeing memory");
|
||||||
g_free(d);
|
g_free(d);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecJS(struct Application *app, char *js)
|
void ExecJS(void *app, char *js)
|
||||||
{
|
{
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
struct dispatchData *data =
|
||||||
|
(struct dispatchData *)g_new(struct dispatchData, 1);
|
||||||
data->method = (dispatchMethod)syncEval;
|
data->method = (dispatchMethod)syncEval;
|
||||||
data->args = js;
|
data->args = js;
|
||||||
data->app = app;
|
data->app = app;
|
||||||
@@ -398,14 +450,13 @@ void ExecJS(struct Application *app, char *js)
|
|||||||
gdk_threads_add_idle(executeMethod, data);
|
gdk_threads_add_idle(executeMethod, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef char *(*dialogMethod)(struct Application *app, void *);
|
typedef char *(*dialogMethod)(void *app, void *);
|
||||||
|
|
||||||
struct dialogCall
|
struct dialogCall
|
||||||
{
|
{
|
||||||
struct Application *app;
|
struct Application *app;
|
||||||
dialogMethod method;
|
dialogMethod method;
|
||||||
void *args;
|
void *args;
|
||||||
void *filter;
|
|
||||||
char *result;
|
char *result;
|
||||||
int done;
|
int done;
|
||||||
};
|
};
|
||||||
@@ -413,70 +464,79 @@ struct dialogCall
|
|||||||
gboolean executeMethodWithReturn(gpointer data)
|
gboolean executeMethodWithReturn(gpointer data)
|
||||||
{
|
{
|
||||||
struct dialogCall *d = (struct dialogCall *)data;
|
struct dialogCall *d = (struct dialogCall *)data;
|
||||||
|
struct Application *app = (struct Application *)(d->app);
|
||||||
d->result = (d->method)(d->app, d->args);
|
Debug("Webview %p\n", app->webView);
|
||||||
|
Debug("Args %s\n", d->args);
|
||||||
|
Debug("Method %p\n", (d->method));
|
||||||
|
d->result = (d->method)(app, d->args);
|
||||||
d->done = 1;
|
d->done = 1;
|
||||||
|
// Debug("Method Execute Complete. Freeing memory");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *OpenFileDialog(struct Application *app, char *title, char *filter)
|
char *OpenFileDialogOnMainThread(void *app, char *title)
|
||||||
{
|
{
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
data->result = NULL;
|
data->result = NULL;
|
||||||
data->done = 0;
|
data->done = 0;
|
||||||
data->method = (dialogMethod)openFileDialogInternal;
|
data->method = (dialogMethod)OpenFileDialog;
|
||||||
const char* dialogArgs[]={ title, filter };
|
data->args = title;
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
|
||||||
|
|
||||||
while (data->done == 0)
|
|
||||||
{
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
g_free(data);
|
|
||||||
return data->result;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *SaveFileDialog(struct Application *app, char *title, char *filter)
|
|
||||||
{
|
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
|
||||||
data->result = NULL;
|
|
||||||
data->done = 0;
|
|
||||||
data->method = (dialogMethod)saveFileDialogInternal;
|
|
||||||
const char* dialogArgs[]={ title, filter };
|
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
data->app = app;
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
while (data->done == 0)
|
while (data->done == 0)
|
||||||
{
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
Debug("Dialog done");
|
Debug("Dialog done");
|
||||||
Debug("Result = %s\n", data->result);
|
Debug("Result = %s\n", data->result);
|
||||||
|
|
||||||
g_free(data);
|
g_free(data);
|
||||||
// Fingers crossed this wasn't freed by g_free above
|
// Fingers crossed this wasn't freed by g_free above
|
||||||
return data->result;
|
return data->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *OpenDirectoryDialog(struct Application *app, char *title, char *filter)
|
char *SaveFileDialogOnMainThread(void *app, char *title)
|
||||||
{
|
{
|
||||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
data->result = NULL;
|
data->result = NULL;
|
||||||
data->done = 0;
|
data->done = 0;
|
||||||
data->method = (dialogMethod)openDirectoryDialogInternal;
|
data->method = (dialogMethod)SaveFileDialog;
|
||||||
const char* dialogArgs[]={ title, filter };
|
data->args = title;
|
||||||
data->args = dialogArgs;
|
|
||||||
data->app = app;
|
data->app = app;
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
while (data->done == 0)
|
while (data->done == 0)
|
||||||
{
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
Debug("Dialog done");
|
||||||
|
Debug("Result = %s\n", data->result);
|
||||||
|
g_free(data);
|
||||||
|
// Fingers crossed this wasn't freed by g_free above
|
||||||
|
return data->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *OpenDirectoryDialogOnMainThread(void *app, char *title)
|
||||||
|
{
|
||||||
|
struct dialogCall *data =
|
||||||
|
(struct dialogCall *)g_new(struct dialogCall, 1);
|
||||||
|
data->result = NULL;
|
||||||
|
data->done = 0;
|
||||||
|
data->method = (dialogMethod)OpenDirectoryDialog;
|
||||||
|
data->args = title;
|
||||||
|
data->app = app;
|
||||||
|
|
||||||
|
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||||
|
|
||||||
|
while (data->done == 0)
|
||||||
|
{
|
||||||
|
// Debug("Waiting for dialog");
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
}
|
||||||
Debug("Directory Dialog done");
|
Debug("Directory Dialog done");
|
||||||
@@ -495,8 +555,10 @@ void setIcon(struct Application *app)
|
|||||||
|
|
||||||
static void load_finished_cb(WebKitWebView *webView,
|
static void load_finished_cb(WebKitWebView *webView,
|
||||||
WebKitLoadEvent load_event,
|
WebKitLoadEvent load_event,
|
||||||
struct Application *app)
|
gpointer userData)
|
||||||
{
|
{
|
||||||
|
struct Application *app;
|
||||||
|
|
||||||
switch (load_event)
|
switch (load_event)
|
||||||
{
|
{
|
||||||
// case WEBKIT_LOAD_STARTED:
|
// case WEBKIT_LOAD_STARTED:
|
||||||
@@ -518,6 +580,7 @@ static void load_finished_cb(WebKitWebView *webView,
|
|||||||
case WEBKIT_LOAD_FINISHED:
|
case WEBKIT_LOAD_FINISHED:
|
||||||
/* Load finished, we can now stop the spinner */
|
/* Load finished, we can now stop the spinner */
|
||||||
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||||
|
app = (struct Application *)userData;
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
Debug("Binding Methods");
|
Debug("Binding Methods");
|
||||||
@@ -574,23 +637,21 @@ static void load_finished_cb(WebKitWebView *webView,
|
|||||||
setMinMaxSize(app);
|
setMinMaxSize(app);
|
||||||
|
|
||||||
// Centre by default
|
// Centre by default
|
||||||
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
Center(app);
|
||||||
|
|
||||||
// Show window and focus
|
// Show window and focus
|
||||||
if( app->startHidden == 0) {
|
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
gtk_widget_grab_focus(app->webView);
|
||||||
gtk_widget_grab_focus(app->webView);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean disable_context_menu_cb(
|
static gboolean
|
||||||
WebKitWebView *web_view,
|
disable_context_menu_cb(WebKitWebView *web_view,
|
||||||
WebKitContextMenu *context_menu,
|
WebKitContextMenu *context_menu,
|
||||||
GdkEvent *event,
|
GdkEvent *event,
|
||||||
WebKitHitTestResult *hit_test_result,
|
WebKitHitTestResult *hit_test_result,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -605,11 +666,12 @@ static void printEvent(const char *message, GdkEventButton *event)
|
|||||||
event->time);
|
event->time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void dragWindow(WebKitUserContentManager *contentManager,
|
static void dragWindow(WebKitUserContentManager *contentManager,
|
||||||
WebKitJavascriptResult *result,
|
WebKitJavascriptResult *result,
|
||||||
struct Application *app)
|
gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
|
||||||
// If we get this message erroneously, ignore
|
// If we get this message erroneously, ignore
|
||||||
if (app->dragButtonEvent == NULL)
|
if (app->dragButtonEvent == NULL)
|
||||||
{
|
{
|
||||||
@@ -634,22 +696,31 @@ static void dragWindow(WebKitUserContentManager *contentManager,
|
|||||||
// Clear the event
|
// Clear the event
|
||||||
app->dragButtonEvent = NULL;
|
app->dragButtonEvent = NULL;
|
||||||
|
|
||||||
return;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
Debug("I am pressing a button");
|
||||||
|
|
||||||
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
||||||
{
|
{
|
||||||
|
printEvent("Drag button event was saved", event);
|
||||||
app->dragButtonEvent = event;
|
app->dragButtonEvent = event;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
|
Debug("I am releasing a button");
|
||||||
|
|
||||||
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
||||||
{
|
{
|
||||||
|
printEvent("Drag button event was reset", event);
|
||||||
|
|
||||||
app->dragButtonEvent = NULL;
|
app->dragButtonEvent = NULL;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -657,8 +728,9 @@ gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Applicat
|
|||||||
|
|
||||||
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
||||||
WebKitJavascriptResult *result,
|
WebKitJavascriptResult *result,
|
||||||
struct Application *app)
|
gpointer arg)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)arg;
|
||||||
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
||||||
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
||||||
char *message = jsc_value_to_string(value);
|
char *message = jsc_value_to_string(value);
|
||||||
@@ -675,225 +747,16 @@ static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
|||||||
g_free(message);
|
g_free(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDebug(struct Application *app, int flag)
|
void SetDebug(void *applicationPointer, int flag)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
debug = flag;
|
debug = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCurrentMonitorGeometry gets the geometry of the monitor
|
void SetBindings(void *applicationPointer, const char *bindings)
|
||||||
// that the window is mostly on.
|
|
||||||
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
|
|
||||||
// Get the monitor that the window is currently on
|
|
||||||
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
|
|
||||||
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
|
||||||
GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window);
|
|
||||||
|
|
||||||
// Get the geometry of the monitor
|
|
||||||
GdkRectangle result;
|
|
||||||
gdk_monitor_get_geometry (monitor,&result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
* Window Position *
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
// Position holds an x/y corrdinate
|
|
||||||
struct Position {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Internal call for setting the position of the window.
|
|
||||||
void setPositionInternal(struct Application *app, struct Position *pos) {
|
|
||||||
|
|
||||||
// Get the monitor geometry
|
|
||||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
|
||||||
|
|
||||||
// Move the window relative to the monitor
|
|
||||||
gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y);
|
|
||||||
|
|
||||||
// Free memory
|
|
||||||
free(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetPosition sets the position of the window to the given x/y
|
|
||||||
// coordinates. The x/y values are relative to the monitor
|
|
||||||
// the window is mostly on.
|
|
||||||
void SetPosition(struct Application *app, int x, int y) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)setPositionInternal;
|
|
||||||
struct Position *pos = malloc(sizeof(struct Position));
|
|
||||||
pos->x = x;
|
|
||||||
pos->y = y;
|
|
||||||
data->args = pos;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************
|
|
||||||
* Window Size *
|
|
||||||
***************/
|
|
||||||
|
|
||||||
// Size holds a width/height
|
|
||||||
struct Size {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Internal call for setting the size of the window.
|
|
||||||
void setSizeInternal(struct Application *app, struct Size *size) {
|
|
||||||
gtk_window_resize(app->mainWindow, size->width, size->height);
|
|
||||||
free(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSize sets the size of the window to the given width/height
|
|
||||||
void SetSize(struct Application *app, int width, int height) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)setSizeInternal;
|
|
||||||
struct Size *size = malloc(sizeof(struct Size));
|
|
||||||
size->width = width;
|
|
||||||
size->height = height;
|
|
||||||
data->args = size;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// centerInternal will center the main window on the monitor it is mostly in
|
|
||||||
void centerInternal(struct Application *app)
|
|
||||||
{
|
{
|
||||||
// Get the geometry of the monitor
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
|
||||||
|
|
||||||
// Get the window width/height
|
|
||||||
int windowWidth, windowHeight;
|
|
||||||
gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight);
|
|
||||||
|
|
||||||
// Place the window at the center of the monitor
|
|
||||||
gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Center the window
|
|
||||||
void Center(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to centerInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)centerInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// hideInternal hides the main window
|
|
||||||
void hideInternal(struct Application *app) {
|
|
||||||
gtk_widget_hide (GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide places the hideInternal method onto the main thread for execution
|
|
||||||
void Hide(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to hideInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)hideInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// showInternal shows the main window
|
|
||||||
void showInternal(struct Application *app) {
|
|
||||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
|
||||||
gtk_widget_grab_focus(app->webView);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show places the showInternal method onto the main thread for execution
|
|
||||||
void Show(struct Application *app) {
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)showInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// maximiseInternal maximises the main window
|
|
||||||
void maximiseInternal(struct Application *app) {
|
|
||||||
gtk_window_maximize(GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maximise places the maximiseInternal method onto the main thread for execution
|
|
||||||
void Maximise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to maximiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)maximiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unmaximiseInternal unmaximises the main window
|
|
||||||
void unmaximiseInternal(struct Application *app) {
|
|
||||||
gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmaximise places the unmaximiseInternal method onto the main thread for execution
|
|
||||||
void Unmaximise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to unmaximiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)unmaximiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// minimiseInternal minimises the main window
|
|
||||||
void minimiseInternal(struct Application *app) {
|
|
||||||
gtk_window_iconify(app->mainWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimise places the minimiseInternal method onto the main thread for execution
|
|
||||||
void Minimise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to minimiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)minimiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// unminimiseInternal unminimises the main window
|
|
||||||
void unminimiseInternal(struct Application *app) {
|
|
||||||
gtk_window_present(app->mainWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unminimise places the unminimiseInternal method onto the main thread for execution
|
|
||||||
void Unminimise(struct Application *app) {
|
|
||||||
|
|
||||||
// Setup a call to unminimiseInternal on the main thread
|
|
||||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
|
||||||
data->method = (dispatchMethod)unminimiseInternal;
|
|
||||||
data->app = app;
|
|
||||||
|
|
||||||
// Add call to main thread
|
|
||||||
gdk_threads_add_idle(executeMethod, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SetBindings(struct Application *app, const char *bindings)
|
|
||||||
{
|
|
||||||
const char *temp = concat("window.wailsbindings = \"", bindings);
|
const char *temp = concat("window.wailsbindings = \"", bindings);
|
||||||
const char *jscall = concat(temp, "\";");
|
const char *jscall = concat(temp, "\";");
|
||||||
free((void *)temp);
|
free((void *)temp);
|
||||||
@@ -903,15 +766,18 @@ void SetBindings(struct Application *app, const char *bindings)
|
|||||||
// This is called when the close button on the window is pressed
|
// This is called when the close button on the window is pressed
|
||||||
gboolean close_button_pressed(GtkWidget *widget,
|
gboolean close_button_pressed(GtkWidget *widget,
|
||||||
GdkEvent *event,
|
GdkEvent *event,
|
||||||
struct Application *app)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)user_data;
|
||||||
app->sendMessageToBackend("WC"); // Window Close
|
app->sendMessageToBackend("WC"); // Window Close
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setupWindow(struct Application *app)
|
static void setupWindow(void *applicationPointer)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
|
|
||||||
// Create the window
|
// Create the window
|
||||||
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
||||||
// Save reference
|
// Save reference
|
||||||
@@ -930,14 +796,10 @@ static void setupWindow(struct Application *app)
|
|||||||
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
||||||
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
||||||
|
|
||||||
// Setup the window drag handler if this is a frameless app
|
// Setup the window drag handler
|
||||||
if ( app->frame == 0 ) {
|
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
||||||
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
||||||
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
|
||||||
// Setup the mouse handlers
|
|
||||||
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
|
||||||
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
|
||||||
}
|
|
||||||
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
||||||
|
|
||||||
// Save reference
|
// Save reference
|
||||||
@@ -946,6 +808,9 @@ static void setupWindow(struct Application *app)
|
|||||||
// Add the webview to the window
|
// Add the webview to the window
|
||||||
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
||||||
|
|
||||||
|
// Setup the mouse handlers
|
||||||
|
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
||||||
|
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
||||||
|
|
||||||
// Load default HTML
|
// Load default HTML
|
||||||
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
||||||
@@ -970,13 +835,20 @@ static void setupWindow(struct Application *app)
|
|||||||
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void activate(GtkApplication* _, struct Application *app)
|
static void
|
||||||
|
activate(GtkApplication *app,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
setupWindow(app);
|
struct Application *mainApp = (struct Application *)user_data;
|
||||||
|
|
||||||
|
// Main Window
|
||||||
|
setupWindow(mainApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Run(struct Application *app, int argc, char **argv)
|
void Run(void *applicationPointer, int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
struct Application *app = (struct Application *)applicationPointer;
|
||||||
|
|
||||||
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
||||||
g_application_run(G_APPLICATION(app->application), argc, argv);
|
g_application_run(G_APPLICATION(app->application), argc, argv);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,151 +180,151 @@ id processAcceleratorKey(const char *key) {
|
|||||||
return str("");
|
return str("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if( STREQ(key, "Backspace") ) {
|
if( STREQ(key, "backspace") ) {
|
||||||
return strunicode(0x0008);
|
return strunicode(0x0008);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Tab") ) {
|
if( STREQ(key, "tab") ) {
|
||||||
return strunicode(0x0009);
|
return strunicode(0x0009);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Return") ) {
|
if( STREQ(key, "return") ) {
|
||||||
return strunicode(0x000d);
|
return strunicode(0x000d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Escape") ) {
|
if( STREQ(key, "escape") ) {
|
||||||
return strunicode(0x001b);
|
return strunicode(0x001b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Left") ) {
|
if( STREQ(key, "left") ) {
|
||||||
return strunicode(0x001c);
|
return strunicode(0x001c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Right") ) {
|
if( STREQ(key, "right") ) {
|
||||||
return strunicode(0x001d);
|
return strunicode(0x001d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Up") ) {
|
if( STREQ(key, "up") ) {
|
||||||
return strunicode(0x001e);
|
return strunicode(0x001e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Down") ) {
|
if( STREQ(key, "down") ) {
|
||||||
return strunicode(0x001f);
|
return strunicode(0x001f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Space") ) {
|
if( STREQ(key, "space") ) {
|
||||||
return strunicode(0x0020);
|
return strunicode(0x0020);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Delete") ) {
|
if( STREQ(key, "delete") ) {
|
||||||
return strunicode(0x007f);
|
return strunicode(0x007f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Home") ) {
|
if( STREQ(key, "home") ) {
|
||||||
return strunicode(0x2196);
|
return strunicode(0x2196);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "End") ) {
|
if( STREQ(key, "end") ) {
|
||||||
return strunicode(0x2198);
|
return strunicode(0x2198);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Up") ) {
|
if( STREQ(key, "page up") ) {
|
||||||
return strunicode(0x21de);
|
return strunicode(0x21de);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "Page Down") ) {
|
if( STREQ(key, "page down") ) {
|
||||||
return strunicode(0x21df);
|
return strunicode(0x21df);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F1") ) {
|
if( STREQ(key, "f1") ) {
|
||||||
return strunicode(0xf704);
|
return strunicode(0xf704);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F2") ) {
|
if( STREQ(key, "f2") ) {
|
||||||
return strunicode(0xf705);
|
return strunicode(0xf705);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F3") ) {
|
if( STREQ(key, "f3") ) {
|
||||||
return strunicode(0xf706);
|
return strunicode(0xf706);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F4") ) {
|
if( STREQ(key, "f4") ) {
|
||||||
return strunicode(0xf707);
|
return strunicode(0xf707);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F5") ) {
|
if( STREQ(key, "f5") ) {
|
||||||
return strunicode(0xf708);
|
return strunicode(0xf708);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F6") ) {
|
if( STREQ(key, "f6") ) {
|
||||||
return strunicode(0xf709);
|
return strunicode(0xf709);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F7") ) {
|
if( STREQ(key, "f7") ) {
|
||||||
return strunicode(0xf70a);
|
return strunicode(0xf70a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F8") ) {
|
if( STREQ(key, "f8") ) {
|
||||||
return strunicode(0xf70b);
|
return strunicode(0xf70b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F9") ) {
|
if( STREQ(key, "f9") ) {
|
||||||
return strunicode(0xf70c);
|
return strunicode(0xf70c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F10") ) {
|
if( STREQ(key, "f10") ) {
|
||||||
return strunicode(0xf70d);
|
return strunicode(0xf70d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F11") ) {
|
if( STREQ(key, "f11") ) {
|
||||||
return strunicode(0xf70e);
|
return strunicode(0xf70e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F12") ) {
|
if( STREQ(key, "f12") ) {
|
||||||
return strunicode(0xf70f);
|
return strunicode(0xf70f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F13") ) {
|
if( STREQ(key, "f13") ) {
|
||||||
return strunicode(0xf710);
|
return strunicode(0xf710);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F14") ) {
|
if( STREQ(key, "f14") ) {
|
||||||
return strunicode(0xf711);
|
return strunicode(0xf711);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F15") ) {
|
if( STREQ(key, "f15") ) {
|
||||||
return strunicode(0xf712);
|
return strunicode(0xf712);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F16") ) {
|
if( STREQ(key, "f16") ) {
|
||||||
return strunicode(0xf713);
|
return strunicode(0xf713);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F17") ) {
|
if( STREQ(key, "f17") ) {
|
||||||
return strunicode(0xf714);
|
return strunicode(0xf714);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F18") ) {
|
if( STREQ(key, "f18") ) {
|
||||||
return strunicode(0xf715);
|
return strunicode(0xf715);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F19") ) {
|
if( STREQ(key, "f19") ) {
|
||||||
return strunicode(0xf716);
|
return strunicode(0xf716);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F20") ) {
|
if( STREQ(key, "f20") ) {
|
||||||
return strunicode(0xf717);
|
return strunicode(0xf717);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F21") ) {
|
if( STREQ(key, "f21") ) {
|
||||||
return strunicode(0xf718);
|
return strunicode(0xf718);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F22") ) {
|
if( STREQ(key, "f22") ) {
|
||||||
return strunicode(0xf719);
|
return strunicode(0xf719);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F23") ) {
|
if( STREQ(key, "f23") ) {
|
||||||
return strunicode(0xf71a);
|
return strunicode(0xf71a);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F24") ) {
|
if( STREQ(key, "f24") ) {
|
||||||
return strunicode(0xf71b);
|
return strunicode(0xf71b);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F25") ) {
|
if( STREQ(key, "f25") ) {
|
||||||
return strunicode(0xf71c);
|
return strunicode(0xf71c);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F26") ) {
|
if( STREQ(key, "f26") ) {
|
||||||
return strunicode(0xf71d);
|
return strunicode(0xf71d);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F27") ) {
|
if( STREQ(key, "f27") ) {
|
||||||
return strunicode(0xf71e);
|
return strunicode(0xf71e);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F28") ) {
|
if( STREQ(key, "f28") ) {
|
||||||
return strunicode(0xf71f);
|
return strunicode(0xf71f);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F29") ) {
|
if( STREQ(key, "f29") ) {
|
||||||
return strunicode(0xf720);
|
return strunicode(0xf720);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F30") ) {
|
if( STREQ(key, "f30") ) {
|
||||||
return strunicode(0xf721);
|
return strunicode(0xf721);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F31") ) {
|
if( STREQ(key, "f31") ) {
|
||||||
return strunicode(0xf722);
|
return strunicode(0xf722);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F32") ) {
|
if( STREQ(key, "f32") ) {
|
||||||
return strunicode(0xf723);
|
return strunicode(0xf723);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F33") ) {
|
if( STREQ(key, "f33") ) {
|
||||||
return strunicode(0xf724);
|
return strunicode(0xf724);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F34") ) {
|
if( STREQ(key, "f34") ) {
|
||||||
return strunicode(0xf725);
|
return strunicode(0xf725);
|
||||||
}
|
}
|
||||||
if( STREQ(key, "F35") ) {
|
if( STREQ(key, "f35") ) {
|
||||||
return strunicode(0xf726);
|
return strunicode(0xf726);
|
||||||
}
|
}
|
||||||
// if( STREQ(key, "Insert") ) {
|
// if( STREQ(key, "Insert") ) {
|
||||||
@@ -336,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
|||||||
// if( STREQ(key, "ScrollLock") ) {
|
// if( STREQ(key, "ScrollLock") ) {
|
||||||
// return strunicode(0xf72f);
|
// return strunicode(0xf72f);
|
||||||
// }
|
// }
|
||||||
if( STREQ(key, "NumLock") ) {
|
if( STREQ(key, "numLock") ) {
|
||||||
return strunicode(0xf739);
|
return strunicode(0xf739);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,20 +508,21 @@ unsigned long parseModifiers(const char **modifiers) {
|
|||||||
const char *thisModifier = modifiers[0];
|
const char *thisModifier = modifiers[0];
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while( thisModifier != NULL ) {
|
while( thisModifier != NULL ) {
|
||||||
|
|
||||||
// Determine flags
|
// Determine flags
|
||||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
if( STREQ(thisModifier, "optionoralt") ) {
|
||||||
result |= NSEventModifierFlagOption;
|
result |= NSEventModifierFlagOption;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Shift") ) {
|
if( STREQ(thisModifier, "shift") ) {
|
||||||
result |= NSEventModifierFlagShift;
|
result |= NSEventModifierFlagShift;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Super") ) {
|
if( STREQ(thisModifier, "super") ) {
|
||||||
result |= NSEventModifierFlagCommand;
|
result |= NSEventModifierFlagCommand;
|
||||||
}
|
}
|
||||||
if( STREQ(thisModifier, "Control") ) {
|
if( STREQ(thisModifier, "ctrl") ) {
|
||||||
result |= NSEventModifierFlagControl;
|
result |= NSEventModifierFlagControl;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
@@ -575,34 +576,7 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage) {
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||||
id item = ALLOC("NSMenuItem");
|
|
||||||
|
|
||||||
// Create a MenuItemCallbackData
|
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
|
||||||
|
|
||||||
id key = processAcceleratorKey(acceleratorkey);
|
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
|
||||||
s("menuItemCallback:"), key);
|
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
|
||||||
msg(item, s("setToolTip:"), str(tooltip));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process image
|
|
||||||
if( image != NULL && strlen(image) > 0) {
|
|
||||||
id data = ALLOC("NSData");
|
|
||||||
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
|
||||||
id nsimage = ALLOC("NSImage");
|
|
||||||
msg(nsimage, s("initWithData:"), imageData);
|
|
||||||
if( templateImage ) {
|
|
||||||
msg(nsimage, s("template"), YES);
|
|
||||||
}
|
|
||||||
msg(item, s("setImage:"), nsimage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Menu Item attributes
|
// Process Menu Item attributes
|
||||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
@@ -653,19 +627,61 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
|
|
||||||
id attributedString = ALLOC("NSMutableAttributedString");
|
id attributedString = ALLOC("NSMutableAttributedString");
|
||||||
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||||
msg(dictionary, s("release"));
|
|
||||||
|
|
||||||
msg(item, s("setAttributedTitle:"), attributedString);
|
|
||||||
msg(attributedString, s("autorelease"));
|
msg(attributedString, s("autorelease"));
|
||||||
|
msg(dictionary, s("release"));
|
||||||
|
return attributedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate) {
|
||||||
|
id item = ALLOC("NSMenuItem");
|
||||||
|
|
||||||
|
// Create a MenuItemCallbackData
|
||||||
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
||||||
|
|
||||||
|
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||||
|
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||||
|
|
||||||
|
if( !alternate ) {
|
||||||
|
id key = processAcceleratorKey(acceleratorkey);
|
||||||
|
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||||
|
s("menuItemCallback:"), key);
|
||||||
|
} else {
|
||||||
|
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg(item, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process image
|
||||||
|
if( image != NULL && strlen(image) > 0) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
||||||
|
id nsimage = ALLOC("NSImage");
|
||||||
|
msg(nsimage, s("initWithData:"), imageData);
|
||||||
|
if( templateImage ) {
|
||||||
|
msg(nsimage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
msg(item, s("setImage:"), nsimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
||||||
|
msg(item, s("setAttributedTitle:"), attributedString);
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg(item, s("setEnabled:"), !disabled);
|
||||||
msg(item, s("autorelease"));
|
msg(item, s("autorelease"));
|
||||||
|
|
||||||
// Process modifiers
|
// Process modifiers
|
||||||
if( modifiers != NULL ) {
|
if( modifiers != NULL && !alternate) {
|
||||||
unsigned long modifierFlags = parseModifiers(modifiers);
|
unsigned long modifierFlags = parseModifiers(modifiers);
|
||||||
msg(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
msg(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alternate
|
||||||
|
if( alternate ) {
|
||||||
|
msg(item, s("setAlternate:"), true);
|
||||||
|
msg(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
||||||
|
}
|
||||||
msg(parentMenu, s("addItem:"), item);
|
msg(parentMenu, s("addItem:"), item);
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@@ -726,6 +742,11 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
label = "(empty)";
|
label = "(empty)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Is this an alternate menu item?
|
||||||
|
bool alternate = false;
|
||||||
|
getJSONBool(item, "MacAlternate", &alternate);
|
||||||
|
|
||||||
const char *menuid = getJSONString(item, "ID");
|
const char *menuid = getJSONString(item, "ID");
|
||||||
if ( menuid == NULL) {
|
if ( menuid == NULL) {
|
||||||
menuid = "";
|
menuid = "";
|
||||||
@@ -746,7 +767,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
bool templateImage = false;
|
bool templateImage = false;
|
||||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||||
|
|
||||||
int fontSize = 12;
|
int fontSize = 0;
|
||||||
getJSONInt(item, "FontSize", &fontSize);
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
@@ -781,7 +802,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
if( type != NULL ) {
|
if( type != NULL ) {
|
||||||
|
|
||||||
if( STREQ(type->string_, "Text")) {
|
if( STREQ(type->string_, "Text")) {
|
||||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate);
|
||||||
}
|
}
|
||||||
else if ( STREQ(type->string_, "Separator")) {
|
else if ( STREQ(type->string_, "Separator")) {
|
||||||
addSeparator(parentMenu);
|
addSeparator(parentMenu);
|
||||||
|
|||||||
@@ -105,10 +105,12 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
|||||||
|
|
||||||
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage);
|
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate);
|
||||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||||
|
|
||||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) ;
|
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||||
id GetMenu(Menu *menu);
|
id GetMenu(Menu *menu);
|
||||||
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
||||||
|
|
||||||
#endif //ASSETS_C_MENU_DARWIN_H
|
#endif //ASSETS_C_MENU_DARWIN_H
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "traymenu_darwin.h"
|
#include "traymenu_darwin.h"
|
||||||
#include "trayicons.h"
|
#include "trayicons.h"
|
||||||
|
|
||||||
|
extern Class trayMenuDelegateClass;
|
||||||
|
|
||||||
// A cache for all our tray menu icons
|
// A cache for all our tray menu icons
|
||||||
// Global because it's a singleton
|
// Global because it's a singleton
|
||||||
struct hashmap_s trayIconCache;
|
struct hashmap_s trayIconCache;
|
||||||
@@ -29,12 +31,23 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
|||||||
|
|
||||||
result->ID = mustJSONString(processedJSON, "ID");
|
result->ID = mustJSONString(processedJSON, "ID");
|
||||||
result->label = mustJSONString(processedJSON, "Label");
|
result->label = mustJSONString(processedJSON, "Label");
|
||||||
result->icon = mustJSONString(processedJSON, "Icon");
|
result->icon = mustJSONString(processedJSON, "Image");
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
result->fontName = getJSONString(processedJSON, "FontName");
|
||||||
|
result->RGBA = getJSONString(processedJSON, "RGBA");
|
||||||
|
getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage);
|
||||||
|
result->fontSize = 0;
|
||||||
|
getJSONInt(processedJSON, "FontSize", &result->fontSize);
|
||||||
|
result->tooltip = NULL;
|
||||||
|
result->tooltip = getJSONString(processedJSON, "Tooltip");
|
||||||
|
result->disabled = false;
|
||||||
|
getJSONBool(processedJSON, "Disabled", &result->disabled);
|
||||||
|
|
||||||
// Create the menu
|
// Create the menu
|
||||||
|
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||||
result->menu = NewMenu(processedMenu);
|
result->menu = NewMenu(processedMenu);
|
||||||
|
|
||||||
|
result->delegate = NULL;
|
||||||
|
|
||||||
// Init tray status bar item
|
// Init tray status bar item
|
||||||
result->statusbaritem = NULL;
|
result->statusbaritem = NULL;
|
||||||
|
|
||||||
@@ -50,7 +63,7 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled) {
|
||||||
|
|
||||||
// Exit early if NULL
|
// Exit early if NULL
|
||||||
if( trayMenu->label == NULL ) {
|
if( trayMenu->label == NULL ) {
|
||||||
@@ -58,7 +71,15 @@ void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
|||||||
}
|
}
|
||||||
// Update button label
|
// Update button label
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||||
msg(statusBarButton, s("setTitle:"), str(label));
|
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||||
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg(statusBarButton, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
msg(statusBarButton, s("setEnabled:"), !disabled);
|
||||||
|
|
||||||
|
msg(statusBarButton, s("setAttributedTitle:"), attributedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||||
@@ -78,12 +99,24 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
||||||
|
|
||||||
|
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
||||||
|
if (trayImage == NULL) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(trayMenu->icon), 0);
|
||||||
|
trayImage = ALLOC("NSImage");
|
||||||
|
msg(trayImage, s("initWithData:"), imageData);
|
||||||
|
|
||||||
|
if( trayMenu->templateImage ) {
|
||||||
|
msg(trayImage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||||
msg(statusBarButton, s("setImage:"), trayImage);
|
msg(statusBarButton, s("setImage:"), trayImage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ShowTrayMenu(TrayMenu* trayMenu) {
|
void ShowTrayMenu(TrayMenu* trayMenu) {
|
||||||
|
|
||||||
// Create a status bar item if we don't have one
|
// Create a status bar item if we don't have one
|
||||||
@@ -91,7 +124,6 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||||
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||||
msg(trayMenu->statusbaritem, s("retain"));
|
msg(trayMenu->statusbaritem, s("retain"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||||
@@ -101,10 +133,20 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
UpdateTrayIcon(trayMenu);
|
UpdateTrayIcon(trayMenu);
|
||||||
|
|
||||||
// Update the label if needed
|
// Update the label if needed
|
||||||
UpdateTrayLabel(trayMenu, trayMenu->label);
|
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled);
|
||||||
|
|
||||||
// Update the menu
|
// Update the menu
|
||||||
id menu = GetMenu(trayMenu->menu);
|
id menu = GetMenu(trayMenu->menu);
|
||||||
|
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create delegate
|
||||||
|
id trayMenuDelegate = msg((id)trayMenuDelegateClass, s("new"));
|
||||||
|
msg(menu, s("setDelegate:"), trayMenuDelegate);
|
||||||
|
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create menu delegate
|
||||||
|
trayMenu->delegate = trayMenuDelegate;
|
||||||
|
|
||||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +195,10 @@ void DeleteTrayMenu(TrayMenu* trayMenu) {
|
|||||||
trayMenu->statusbaritem = NULL;
|
trayMenu->statusbaritem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( trayMenu->delegate != NULL ) {
|
||||||
|
msg(trayMenu->delegate, s("release"));
|
||||||
|
}
|
||||||
|
|
||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
MEMFREE(trayMenu);
|
MEMFREE(trayMenu);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ typedef struct {
|
|||||||
const char *label;
|
const char *label;
|
||||||
const char *icon;
|
const char *icon;
|
||||||
const char *ID;
|
const char *ID;
|
||||||
|
const char *tooltip;
|
||||||
|
|
||||||
|
bool templateImage;
|
||||||
|
const char *fontName;
|
||||||
|
int fontSize;
|
||||||
|
const char *RGBA;
|
||||||
|
|
||||||
|
bool disabled;
|
||||||
|
|
||||||
Menu* menu;
|
Menu* menu;
|
||||||
|
|
||||||
@@ -21,6 +29,8 @@ typedef struct {
|
|||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
|
id delegate;
|
||||||
|
|
||||||
} TrayMenu;
|
} TrayMenu;
|
||||||
|
|
||||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||||
@@ -28,7 +38,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
|||||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char*);
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled);
|
||||||
|
|
||||||
void LoadTrayIcons();
|
void LoadTrayIcons();
|
||||||
void UnloadTrayIcons();
|
void UnloadTrayIcons();
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ TrayMenuStore* NewTrayMenuStore() {
|
|||||||
ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!");
|
ABORT("[NewTrayMenuStore] Not enough memory to allocate trayMenuMap!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&result->lock, NULL) != 0) {
|
||||||
|
printf("\n mutex init has failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,15 +30,19 @@ int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DumpTrayMenuStore(TrayMenuStore* store) {
|
void DumpTrayMenuStore(TrayMenuStore* store) {
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
||||||
|
|
||||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
//TODO: check if there is already an entry for this menu
|
//TODO: check if there is already an entry for this menu
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||||
@@ -43,12 +52,13 @@ int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
||||||
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||||
DeleteTrayMenu(e->data);
|
DeleteTrayMenu(e->data);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -65,22 +75,39 @@ void DeleteTrayMenuStore(TrayMenuStore *store) {
|
|||||||
|
|
||||||
// Destroy tray menu map
|
// Destroy tray menu map
|
||||||
hashmap_destroy(&store->trayMenuMap);
|
hashmap_destroy(&store->trayMenuMap);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&store->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||||
// Get the current menu
|
// Get the current menu
|
||||||
return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
pthread_mutex_lock(&store->lock);
|
||||||
|
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||||
// Get the current menu
|
// Get the current menu
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
if (result == NULL ) {
|
if (result == NULL ) {
|
||||||
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* ID) {
|
||||||
|
|
||||||
|
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
|
hashmap_remove(&store->trayMenuMap, ID, strlen(ID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
DeleteTrayMenu(menu);
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
||||||
// Parse the JSON
|
// Parse the JSON
|
||||||
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
||||||
@@ -91,7 +118,17 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
|
|
||||||
// Check we have this menu
|
// Check we have this menu
|
||||||
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||||
UpdateTrayLabel(menu, Label);
|
|
||||||
|
const char *fontName = getJSONString(parsedUpdate, "FontName");
|
||||||
|
const char *RGBA = getJSONString(parsedUpdate, "RGBA");
|
||||||
|
int fontSize = 0;
|
||||||
|
getJSONInt(parsedUpdate, "FontSize", &fontSize);
|
||||||
|
const char *tooltip = getJSONString(parsedUpdate, "Tooltip");
|
||||||
|
bool disabled = false;
|
||||||
|
getJSONBool(parsedUpdate, "Disabled", &disabled);
|
||||||
|
|
||||||
|
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +142,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// If we don't have a menu, we create one
|
// If we don't have a menu, we create one
|
||||||
if ( currentMenu == NULL ) {
|
if ( currentMenu == NULL ) {
|
||||||
// Store the new menu
|
// Store the new menu
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Show it
|
// Show it
|
||||||
ShowTrayMenu(newMenu);
|
ShowTrayMenu(newMenu);
|
||||||
@@ -116,7 +155,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// Save the status bar reference
|
// Save the status bar reference
|
||||||
newMenu->statusbaritem = currentMenu->statusbaritem;
|
newMenu->statusbaritem = currentMenu->statusbaritem;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Delete the current menu
|
// Delete the current menu
|
||||||
DeleteMenu(currentMenu->menu);
|
DeleteMenu(currentMenu->menu);
|
||||||
@@ -125,9 +166,10 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
|||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
MEMFREE(currentMenu);
|
MEMFREE(currentMenu);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&store->lock);
|
||||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||||
|
pthread_mutex_unlock(&store->lock);
|
||||||
|
|
||||||
// Show the updated menu
|
// Show the updated menu
|
||||||
ShowTrayMenu(newMenu);
|
ShowTrayMenu(newMenu);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
#ifndef TRAYMENUSTORE_DARWIN_H
|
#ifndef TRAYMENUSTORE_DARWIN_H
|
||||||
#define TRAYMENUSTORE_DARWIN_H
|
#define TRAYMENUSTORE_DARWIN_H
|
||||||
|
|
||||||
|
#include "traymenu_darwin.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
int dummy;
|
int dummy;
|
||||||
@@ -13,6 +17,8 @@ typedef struct {
|
|||||||
// It maps tray IDs to TrayMenu*
|
// It maps tray IDs to TrayMenu*
|
||||||
struct hashmap_s trayMenuMap;
|
struct hashmap_s trayMenuMap;
|
||||||
|
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
|
||||||
} TrayMenuStore;
|
} TrayMenuStore;
|
||||||
|
|
||||||
TrayMenuStore* NewTrayMenuStore();
|
TrayMenuStore* NewTrayMenuStore();
|
||||||
@@ -22,6 +28,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON);
|
|||||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||||
|
|
||||||
|
TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID);
|
||||||
|
|
||||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
||||||
|
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
||||||
|
|
||||||
#endif //TRAYMENUSTORE_DARWIN_H
|
#endif //TRAYMENUSTORE_DARWIN_H
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalDirectory gets the caller's file directory
|
// LocalDirectory gets the caller's file directory
|
||||||
@@ -20,45 +18,11 @@ func LocalDirectory() string {
|
|||||||
return filepath.Dir(thisFile)
|
return filepath.Dir(thisFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RelativeToCwd returns an absolute path based on the cwd
|
|
||||||
// and the given relative path
|
|
||||||
func RelativeToCwd(relativePath string) (string, error) {
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Join(cwd, relativePath), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mkdir will create the given directory
|
// Mkdir will create the given directory
|
||||||
func Mkdir(dirname string) error {
|
func Mkdir(dirname string) error {
|
||||||
return os.Mkdir(dirname, 0755)
|
return os.Mkdir(dirname, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MkDirs creates the given nested directories.
|
|
||||||
// Returns error on failure
|
|
||||||
func MkDirs(fullPath string, mode ...os.FileMode) error {
|
|
||||||
var perms os.FileMode
|
|
||||||
perms = 0700
|
|
||||||
if len(mode) == 1 {
|
|
||||||
perms = mode[0]
|
|
||||||
}
|
|
||||||
return os.MkdirAll(fullPath, perms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MoveFile attempts to move the source file to the target
|
|
||||||
// Target is a fully qualified path to a file *name*, not a
|
|
||||||
// directory
|
|
||||||
func MoveFile(source string, target string) error {
|
|
||||||
return os.Rename(source, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteFile will delete the given file
|
|
||||||
func DeleteFile(filename string) error {
|
|
||||||
return os.Remove(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFile from source to target
|
// CopyFile from source to target
|
||||||
func CopyFile(source string, target string) error {
|
func CopyFile(source string, target string) error {
|
||||||
s, err := os.Open(source)
|
s, err := os.Open(source)
|
||||||
@@ -180,100 +144,3 @@ func fatal(message ...string) {
|
|||||||
}
|
}
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubdirectories returns a list of subdirectories for the given root directory
|
|
||||||
func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
|
||||||
var result slicer.StringSlicer
|
|
||||||
|
|
||||||
// Iterate root dir
|
|
||||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// If we have a directory, save it
|
|
||||||
if info.IsDir() {
|
|
||||||
result.Add(path)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return &result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func DirIsEmpty(dir string) (bool, error) {
|
|
||||||
|
|
||||||
if !DirExists(dir) {
|
|
||||||
return false, fmt.Errorf("DirIsEmpty called with a non-existant directory: %s", dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
|
||||||
f, err := os.Open(dir)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
|
||||||
if err == io.EOF {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
return false, err // Either not empty or error, suits both cases
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04
|
|
||||||
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
|
|
||||||
// Source directory must exist, destination directory must *not* exist.
|
|
||||||
// Symlinks are ignored and skipped.
|
|
||||||
func CopyDir(src string, dst string) (err error) {
|
|
||||||
src = filepath.Clean(src)
|
|
||||||
dst = filepath.Clean(dst)
|
|
||||||
|
|
||||||
si, err := os.Stat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !si.IsDir() {
|
|
||||||
return fmt.Errorf("source is not a directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = os.Stat(dst)
|
|
||||||
if err != nil && !os.IsNotExist(err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
return fmt.Errorf("destination already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(dst, si.Mode())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
entries, err := ioutil.ReadDir(src)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
srcPath := filepath.Join(src, entry.Name())
|
|
||||||
dstPath := filepath.Join(dst, entry.Name())
|
|
||||||
|
|
||||||
if entry.IsDir() {
|
|
||||||
err = CopyDir(srcPath, dstPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Skip symlinks.
|
|
||||||
if entry.Mode()&os.ModeSymlink != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = CopyFile(srcPath, dstPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ package html
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/tdewolff/minify"
|
|
||||||
"github.com/tdewolff/minify/js"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type assetTypes struct {
|
type assetTypes struct {
|
||||||
@@ -67,10 +63,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
result = strings.ReplaceAll(result, "\n", "")
|
result = strings.ReplaceAll(result, "\n", "")
|
||||||
result = strings.ReplaceAll(result, "\r\n", "")
|
result = strings.ReplaceAll(result, "\r\n", "")
|
||||||
result = strings.ReplaceAll(result, "\n", "")
|
result = strings.ReplaceAll(result, "\n", "")
|
||||||
|
|
||||||
// Inject wailsloader code
|
|
||||||
result = strings.Replace(result, `</body>`, `<script id='wailsloader'>window.wailsloader = { html: true, runtime: false, userjs: false, usercss: false };var self=document.querySelector('#wailsloader');self.parentNode.removeChild(self);</script></body>`, 1)
|
|
||||||
|
|
||||||
url := url.URL{Path: result}
|
url := url.URL{Path: result}
|
||||||
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
||||||
|
|
||||||
@@ -92,16 +84,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
result = strings.ReplaceAll(result, ` {`, `{`)
|
result = strings.ReplaceAll(result, ` {`, `{`)
|
||||||
result = strings.ReplaceAll(result, `: `, `:`)
|
result = strings.ReplaceAll(result, `: `, `:`)
|
||||||
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
||||||
|
|
||||||
case AssetTypes.JS:
|
|
||||||
m := minify.New()
|
|
||||||
m.AddFunc("application/javascript", js.Minify)
|
|
||||||
var err error
|
|
||||||
dataString, err = m.String("application/javascript", a.Data+";")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
a.Data = dataString
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get byte data of the string
|
// Get byte data of the string
|
||||||
@@ -121,7 +103,6 @@ func (a *Asset) AsCHexData() string {
|
|||||||
return cdata.String()
|
return cdata.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump will output the asset to the terminal
|
|
||||||
func (a *Asset) Dump() {
|
func (a *Asset) Dump() {
|
||||||
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
buf := bytes.NewBufferString(htmldata)
|
buf := bytes.NewBufferString(htmldata)
|
||||||
tokenizer := html.NewTokenizer(buf)
|
tokenizer := html.NewTokenizer(buf)
|
||||||
|
|
||||||
paths := slicer.String()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
//get the next token type
|
//get the next token type
|
||||||
tokenType := tokenizer.Next()
|
tokenType := tokenizer.Next()
|
||||||
@@ -102,29 +100,19 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
if attr.Key == "href" {
|
if attr.Key == "href" {
|
||||||
asset.Path = attr.Val
|
asset.Path = attr.Val
|
||||||
}
|
}
|
||||||
// standard stylesheet
|
// stylesheet
|
||||||
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
||||||
asset.Type = AssetTypes.CSS
|
asset.Type = AssetTypes.CSS
|
||||||
}
|
}
|
||||||
if attr.Key == "as" && attr.Val == "style" {
|
|
||||||
asset.Type = AssetTypes.CSS
|
|
||||||
}
|
|
||||||
if attr.Key == "as" && attr.Val == "script" {
|
|
||||||
asset.Type = AssetTypes.JS
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err := asset.Load(a.basedirectory)
|
||||||
// Ensure we don't include duplicates
|
if err != nil {
|
||||||
if !paths.Contains(asset.Path) {
|
return err
|
||||||
err := asset.Load(a.basedirectory)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.assets = append(a.assets, asset)
|
|
||||||
paths.Add(asset.Path)
|
|
||||||
}
|
}
|
||||||
|
a.assets = append(a.assets, asset)
|
||||||
}
|
}
|
||||||
if "script" == token.Data {
|
if "script" == token.Data {
|
||||||
|
|
||||||
tokenType = tokenizer.Next()
|
tokenType = tokenizer.Next()
|
||||||
//just make sure it's actually a text token
|
//just make sure it's actually a text token
|
||||||
asset := &Asset{Type: AssetTypes.JS}
|
asset := &Asset{Type: AssetTypes.JS}
|
||||||
@@ -134,14 +122,11 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !paths.Contains(asset.Path) {
|
err := asset.Load(a.basedirectory)
|
||||||
err := asset.Load(a.basedirectory)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.assets = append(a.assets, asset)
|
|
||||||
paths.Add(asset.Path)
|
|
||||||
}
|
}
|
||||||
|
a.assets = append(a.assets, asset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,7 +141,7 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
var cdata strings.Builder
|
var cdata strings.Builder
|
||||||
|
|
||||||
// Write header
|
// Write header
|
||||||
header := `// assets.h
|
header := `// assets.c
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||||
// This file was auto-generated. DO NOT MODIFY.
|
// This file was auto-generated. DO NOT MODIFY.
|
||||||
|
|
||||||
@@ -168,10 +153,6 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
assetVariables := slicer.String()
|
assetVariables := slicer.String()
|
||||||
var variableName string
|
var variableName string
|
||||||
for index, asset := range a.assets {
|
for index, asset := range a.assets {
|
||||||
// For desktop we ignore the favicon
|
|
||||||
if asset.Type == AssetTypes.FAVICON {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
||||||
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
||||||
cdata.WriteString(assetCdata)
|
cdata.WriteString(assetCdata)
|
||||||
@@ -179,13 +160,13 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if assetVariables.Length() > 0 {
|
if assetVariables.Length() > 0 {
|
||||||
cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
cdata.WriteString(fmt.Sprintf("\nconst char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||||
} else {
|
} else {
|
||||||
cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };")
|
cdata.WriteString("\nconst char *assets[] = { 0x00 };")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save file
|
// Save file
|
||||||
assetsFile := filepath.Join(targetDir, "assets.h")
|
assetsFile := filepath.Join(targetDir, "assets.c")
|
||||||
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -196,17 +177,16 @@ func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
|||||||
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
||||||
// the items in the AssetBundle
|
// the items in the AssetBundle
|
||||||
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
||||||
theassetdb := assetdb.NewAssetDB()
|
assetdb := assetdb.NewAssetDB()
|
||||||
|
|
||||||
// Loop over the Assets
|
// Loop over the Assets
|
||||||
for _, asset := range a.assets {
|
for _, asset := range a.assets {
|
||||||
theassetdb.AddAsset(asset.Path, []byte(asset.Data))
|
assetdb.AddAsset(asset.Path, []byte(asset.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return theassetdb, nil
|
return assetdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump will output the assets to the terminal
|
|
||||||
func (a *AssetBundle) Dump() {
|
func (a *AssetBundle) Dump() {
|
||||||
println("Assets:")
|
println("Assets:")
|
||||||
for _, asset := range a.assets {
|
for _, asset := range a.assets {
|
||||||
|
|||||||
@@ -2,30 +2,31 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CustomLogger defines what a user can do with a logger
|
// CustomLogger defines what a user can do with a logger
|
||||||
type CustomLogger interface {
|
type CustomLogger interface {
|
||||||
// Writeln writes directly to the output with no log level plus line ending
|
// Writeln writes directly to the output with no log level plus line ending
|
||||||
Writeln(message string)
|
Writeln(message string) error
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
Write(message string)
|
Write(message string) error
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
Trace(format string, args ...interface{})
|
Trace(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
Debug(format string, args ...interface{})
|
Debug(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
Info(format string, args ...interface{})
|
Info(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
Warning(format string, args ...interface{})
|
Warning(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
Error(format string, args ...interface{})
|
Error(format string, args ...interface{}) error
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
Fatal(format string, args ...interface{})
|
Fatal(format string, args ...interface{})
|
||||||
@@ -49,48 +50,49 @@ func newcustomLogger(logger *Logger, name string) *customLogger {
|
|||||||
|
|
||||||
// Writeln writes directly to the output with no log level
|
// Writeln writes directly to the output with no log level
|
||||||
// Appends a carriage return to the message
|
// Appends a carriage return to the message
|
||||||
func (l *customLogger) Writeln(message string) {
|
func (l *customLogger) Writeln(message string) error {
|
||||||
l.logger.Writeln(message)
|
return l.logger.Writeln(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
func (l *customLogger) Write(message string) {
|
func (l *customLogger) Write(message string) error {
|
||||||
l.logger.Write(message)
|
return l.logger.Write(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Trace(format string, args ...interface{}) {
|
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Trace(format, args...)
|
return l.logger.processLogMessage(TRACE, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Debug(format string, args ...interface{}) {
|
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Debug(format, args...)
|
return l.logger.processLogMessage(DEBUG, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Info(format string, args ...interface{}) {
|
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Info(format, args...)
|
return l.logger.processLogMessage(INFO, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Warning(format string, args ...interface{}) {
|
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Warning(format, args...)
|
return l.logger.processLogMessage(WARNING, format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Error(format string, args ...interface{}) {
|
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Error(format, args...)
|
return l.logger.processLogMessage(ERROR, format, args...)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
||||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||||
l.logger.Fatal(format, args...)
|
l.logger.processLogMessage(FATAL, format, args...)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,32 +2,37 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel is an alias for the public LogLevel
|
|
||||||
type LogLevel = logger.LogLevel
|
|
||||||
|
|
||||||
// Logger is a utlility to log messages to a number of destinations
|
// Logger is a utlility to log messages to a number of destinations
|
||||||
type Logger struct {
|
type Logger struct {
|
||||||
output logger.Logger
|
writers []io.Writer
|
||||||
logLevel LogLevel
|
logLevel uint8
|
||||||
showLevelInLog bool
|
showLevelInLog bool
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
||||||
// are the targets for the logs
|
// are the targets for the logs
|
||||||
func New(output logger.Logger) *Logger {
|
func New(writers ...io.Writer) *Logger {
|
||||||
result := &Logger{
|
result := &Logger{
|
||||||
logLevel: logger.INFO,
|
logLevel: INFO,
|
||||||
showLevelInLog: true,
|
showLevelInLog: true,
|
||||||
output: output,
|
}
|
||||||
|
for _, writer := range writers {
|
||||||
|
result.AddOutput(writer)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writers gets the log writers
|
||||||
|
func (l *Logger) Writers() []io.Writer {
|
||||||
|
return l.writers
|
||||||
|
}
|
||||||
|
|
||||||
// CustomLogger creates a new custom logger that prints out a name/id
|
// CustomLogger creates a new custom logger that prints out a name/id
|
||||||
// before the messages
|
// before the messages
|
||||||
func (l *Logger) CustomLogger(name string) CustomLogger {
|
func (l *Logger) CustomLogger(name string) CustomLogger {
|
||||||
@@ -40,66 +45,99 @@ func (l *Logger) HideLogLevel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetLogLevel sets the minimum level of logs that will be output
|
// SetLogLevel sets the minimum level of logs that will be output
|
||||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
func (l *Logger) SetLogLevel(level uint8) {
|
||||||
l.logLevel = level
|
l.logLevel = level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddOutput adds the given `io.Writer` to the list of destinations
|
||||||
|
// that get logged to
|
||||||
|
func (l *Logger) AddOutput(writer io.Writer) {
|
||||||
|
l.writers = append(l.writers, writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) write(loglevel uint8, message string) error {
|
||||||
|
|
||||||
|
// Don't print logs lower than the current log level
|
||||||
|
if loglevel < l.logLevel {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show log level text if enabled
|
||||||
|
if l.showLevelInLog {
|
||||||
|
message = mapLogLevel[loglevel] + message
|
||||||
|
}
|
||||||
|
|
||||||
|
// write out the logs
|
||||||
|
l.lock.Lock()
|
||||||
|
for _, writer := range l.writers {
|
||||||
|
_, err := writer.Write([]byte(message))
|
||||||
|
if err != nil {
|
||||||
|
l.lock.Unlock() // Because defer is slow
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
l.lock.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeln appends a newline character to the message before writing
|
||||||
|
func (l *Logger) writeln(loglevel uint8, message string) error {
|
||||||
|
return l.write(loglevel, message+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
// Writeln writes directly to the output with no log level
|
// Writeln writes directly to the output with no log level
|
||||||
// Appends a carriage return to the message
|
// Appends a carriage return to the message
|
||||||
func (l *Logger) Writeln(message string) {
|
func (l *Logger) Writeln(message string) error {
|
||||||
l.output.Print(message)
|
return l.write(BYPASS, message+"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes directly to the output with no log level
|
// Write writes directly to the output with no log level
|
||||||
func (l *Logger) Write(message string) {
|
func (l *Logger) Write(message string) error {
|
||||||
l.output.Print(message)
|
return l.write(BYPASS, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print writes directly to the output with no log level
|
// processLogMessage formats the given message before writing it out
|
||||||
// Appends a carriage return to the message
|
func (l *Logger) processLogMessage(loglevel uint8, format string, args ...interface{}) error {
|
||||||
func (l *Logger) Print(message string) {
|
message := fmt.Sprintf(format, args...)
|
||||||
l.Write(message)
|
return l.writeln(loglevel, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trace level logging. Works like Sprintf.
|
// Trace level logging. Works like Sprintf.
|
||||||
func (l *Logger) Trace(format string, args ...interface{}) {
|
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.TRACE {
|
return l.processLogMessage(TRACE, format, args...)
|
||||||
l.output.Trace(fmt.Sprintf(format, args...))
|
}
|
||||||
|
|
||||||
|
// CustomTrace returns a custom Logging function that will insert the given name before the message
|
||||||
|
func (l *Logger) CustomTrace(name string) func(format string, args ...interface{}) {
|
||||||
|
return func(format string, args ...interface{}) {
|
||||||
|
format = name + " | " + format
|
||||||
|
l.processLogMessage(TRACE, format, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug level logging. Works like Sprintf.
|
// Debug level logging. Works like Sprintf.
|
||||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.DEBUG {
|
return l.processLogMessage(DEBUG, format, args...)
|
||||||
l.output.Debug(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info level logging. Works like Sprintf.
|
// Info level logging. Works like Sprintf.
|
||||||
func (l *Logger) Info(format string, args ...interface{}) {
|
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.INFO {
|
return l.processLogMessage(INFO, format, args...)
|
||||||
l.output.Info(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning level logging. Works like Sprintf.
|
// Warning level logging. Works like Sprintf.
|
||||||
func (l *Logger) Warning(format string, args ...interface{}) {
|
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.WARNING {
|
return l.processLogMessage(WARNING, format, args...)
|
||||||
l.output.Warning(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error level logging. Works like Sprintf.
|
// Error level logging. Works like Sprintf.
|
||||||
func (l *Logger) Error(format string, args ...interface{}) {
|
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||||
if l.logLevel <= logger.ERROR {
|
return l.processLogMessage(ERROR, format, args...)
|
||||||
l.output.Error(fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal level logging. Works like Sprintf.
|
// Fatal level logging. Works like Sprintf.
|
||||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||||
l.output.Fatal(fmt.Sprintf(format, args...))
|
l.processLogMessage(FATAL, format, args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|||||||
34
v2/internal/logger/logger.go
Normal file
34
v2/internal/logger/logger.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TRACE level
|
||||||
|
TRACE uint8 = 0
|
||||||
|
|
||||||
|
// DEBUG level logging
|
||||||
|
DEBUG uint8 = 1
|
||||||
|
|
||||||
|
// INFO level logging
|
||||||
|
INFO uint8 = 2
|
||||||
|
|
||||||
|
// WARNING level logging
|
||||||
|
WARNING uint8 = 4
|
||||||
|
|
||||||
|
// ERROR level logging
|
||||||
|
ERROR uint8 = 8
|
||||||
|
|
||||||
|
// FATAL level logging
|
||||||
|
FATAL uint8 = 16
|
||||||
|
|
||||||
|
// BYPASS level logging - does not use a log level
|
||||||
|
BYPASS uint8 = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
var mapLogLevel = map[uint8]string{
|
||||||
|
TRACE: "TRACE | ",
|
||||||
|
DEBUG: "DEBUG | ",
|
||||||
|
INFO: "INFO | ",
|
||||||
|
WARNING: "WARN | ",
|
||||||
|
ERROR: "ERROR | ",
|
||||||
|
FATAL: "FATAL | ",
|
||||||
|
BYPASS: "",
|
||||||
|
}
|
||||||
202
v2/internal/logger/logger_test.go
Normal file
202
v2/internal/logger/logger_test.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestByteBufferLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(TRACE)
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "TRACE | I am a message!\n",
|
||||||
|
DEBUG: "DEBUG | I am a message!\n",
|
||||||
|
WARNING: "WARN | I am a message!\n",
|
||||||
|
INFO: "INFO | I am a message!\n",
|
||||||
|
ERROR: "ERROR | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: myLogger.Trace,
|
||||||
|
DEBUG: myLogger.Debug,
|
||||||
|
WARNING: myLogger.Warning,
|
||||||
|
INFO: myLogger.Info,
|
||||||
|
ERROR: myLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level, expected := range tests {
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestCustomLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(TRACE)
|
||||||
|
customLogger := myLogger.CustomLogger("Test")
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "TRACE | Test | I am a message!\n",
|
||||||
|
DEBUG: "DEBUG | Test | I am a message!\n",
|
||||||
|
WARNING: "WARN | Test | I am a message!\n",
|
||||||
|
INFO: "INFO | Test | I am a message!\n",
|
||||||
|
ERROR: "ERROR | Test | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: customLogger.Trace,
|
||||||
|
DEBUG: customLogger.Debug,
|
||||||
|
WARNING: customLogger.Warning,
|
||||||
|
INFO: customLogger.Info,
|
||||||
|
ERROR: customLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level, expected := range tests {
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestWriteln(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(DEBUG)
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := myLogger.Writeln("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "I am a message!\n")
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err = myLogger.Write("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual = buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "I am a message!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogLevel(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
myLogger := New(&buf)
|
||||||
|
myLogger.SetLogLevel(ERROR)
|
||||||
|
|
||||||
|
tests := map[uint8]string{
|
||||||
|
TRACE: "",
|
||||||
|
DEBUG: "",
|
||||||
|
WARNING: "",
|
||||||
|
INFO: "",
|
||||||
|
ERROR: "ERROR | I am a message!\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
methods := map[uint8]func(string, ...interface{}) error{
|
||||||
|
TRACE: myLogger.Trace,
|
||||||
|
DEBUG: myLogger.Debug,
|
||||||
|
WARNING: myLogger.Warning,
|
||||||
|
INFO: myLogger.Info,
|
||||||
|
ERROR: myLogger.Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
for level := range tests {
|
||||||
|
|
||||||
|
method := methods[level]
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err := method("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
actual := buf.String()
|
||||||
|
|
||||||
|
is.Equal(actual, "ERROR | I am a message!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileLogger(t *testing.T) {
|
||||||
|
|
||||||
|
is := is.New(t)
|
||||||
|
|
||||||
|
// Create new byte buffer logger
|
||||||
|
file, err := ioutil.TempFile(".", "wailsv2test")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
|
myLogger := New(file)
|
||||||
|
myLogger.SetLogLevel(DEBUG)
|
||||||
|
|
||||||
|
// Write message
|
||||||
|
err = myLogger.Info("I am a message!")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
actual, err := ioutil.ReadFile(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
is.Equal(string(actual), "INFO | I am a message!\n")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ type ProcessedMenuItem struct {
|
|||||||
// Image - base64 image data
|
// Image - base64 image data
|
||||||
Image string `json:",omitempty"`
|
Image string `json:",omitempty"`
|
||||||
MacTemplateImage bool `json:", omitempty"`
|
MacTemplateImage bool `json:", omitempty"`
|
||||||
|
MacAlternate bool `json:", omitempty"`
|
||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
Tooltip string `json:",omitempty"`
|
Tooltip string `json:",omitempty"`
|
||||||
@@ -60,6 +61,7 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
|
|||||||
FontName: menuItem.FontName,
|
FontName: menuItem.FontName,
|
||||||
Image: menuItem.Image,
|
Image: menuItem.Image,
|
||||||
MacTemplateImage: menuItem.MacTemplateImage,
|
MacTemplateImage: menuItem.MacTemplateImage,
|
||||||
|
MacAlternate: menuItem.MacAlternate,
|
||||||
Tooltip: menuItem.Tooltip,
|
Tooltip: menuItem.Tooltip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,29 +3,39 @@ package menumanager
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var trayMenuID int
|
var trayMenuID int
|
||||||
var trayMenuIDMutex sync.Mutex
|
var trayMenuIDMutex sync.Mutex
|
||||||
|
|
||||||
func generateTrayID() string {
|
func generateTrayID() string {
|
||||||
|
var idStr string
|
||||||
trayMenuIDMutex.Lock()
|
trayMenuIDMutex.Lock()
|
||||||
result := fmt.Sprintf("%d", trayMenuID)
|
idStr = strconv.Itoa(trayMenuID)
|
||||||
trayMenuID++
|
trayMenuID++
|
||||||
trayMenuIDMutex.Unlock()
|
trayMenuIDMutex.Unlock()
|
||||||
return result
|
return idStr
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrayMenu struct {
|
type TrayMenu struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string
|
||||||
Icon string
|
FontSize int
|
||||||
menuItemMap *MenuItemMap
|
FontName string
|
||||||
menu *menu.Menu
|
Disabled bool
|
||||||
ProcessedMenu *WailsMenu
|
Tooltip string `json:",omitempty"`
|
||||||
|
Image string
|
||||||
|
MacTemplateImage bool
|
||||||
|
RGBA string
|
||||||
|
menuItemMap *MenuItemMap
|
||||||
|
menu *menu.Menu
|
||||||
|
ProcessedMenu *WailsMenu
|
||||||
|
trayMenu *menu.TrayMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrayMenu) AsJSON() (string, error) {
|
func (t *TrayMenu) AsJSON() (string, error) {
|
||||||
@@ -39,10 +49,17 @@ func (t *TrayMenu) AsJSON() (string, error) {
|
|||||||
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
||||||
|
|
||||||
result := &TrayMenu{
|
result := &TrayMenu{
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
Icon: trayMenu.Icon,
|
FontName: trayMenu.FontName,
|
||||||
menu: trayMenu.Menu,
|
FontSize: trayMenu.FontSize,
|
||||||
menuItemMap: NewMenuItemMap(),
|
Disabled: trayMenu.Disabled,
|
||||||
|
Tooltip: trayMenu.Tooltip,
|
||||||
|
Image: trayMenu.Image,
|
||||||
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
|
menu: trayMenu.Menu,
|
||||||
|
RGBA: trayMenu.RGBA,
|
||||||
|
menuItemMap: NewMenuItemMap(),
|
||||||
|
trayMenu: trayMenu,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.menuItemMap.AddMenu(trayMenu.Menu)
|
result.menuItemMap.AddMenu(trayMenu.Menu)
|
||||||
@@ -51,6 +68,28 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuOpen(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnOpen == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuClose(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnClose == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnClose()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
newTrayMenu := NewTrayMenu(trayMenu)
|
newTrayMenu := NewTrayMenu(trayMenu)
|
||||||
|
|
||||||
@@ -65,6 +104,14 @@ func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
return newTrayMenu.AsJSON()
|
return newTrayMenu.AsJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetTrayID(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
|
trayID, exists := m.trayMenuPointers[trayMenu]
|
||||||
|
if !exists {
|
||||||
|
return "", fmt.Errorf("Unable to find menu ID for tray menu!")
|
||||||
|
}
|
||||||
|
return trayID, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetTrayMenu updates or creates a menu
|
// SetTrayMenu updates or creates a menu
|
||||||
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
|
trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
|
||||||
@@ -102,13 +149,27 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LabelUpdate struct {
|
type LabelUpdate struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string
|
||||||
|
FontName string
|
||||||
|
FontSize int
|
||||||
|
RGBA string
|
||||||
|
Disabled bool
|
||||||
|
Tooltip string
|
||||||
|
Image string
|
||||||
|
MacTemplateImage bool
|
||||||
}
|
}
|
||||||
|
|
||||||
update := &LabelUpdate{
|
update := &LabelUpdate{
|
||||||
ID: trayID,
|
ID: trayID,
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
|
FontName: trayMenu.FontName,
|
||||||
|
FontSize: trayMenu.FontSize,
|
||||||
|
Disabled: trayMenu.Disabled,
|
||||||
|
Tooltip: trayMenu.Tooltip,
|
||||||
|
Image: trayMenu.Image,
|
||||||
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
|
RGBA: trayMenu.RGBA,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(update)
|
data, err := json.Marshal(update)
|
||||||
|
|||||||
@@ -6,37 +6,19 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client defines what a frontend client can do
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
Quit()
|
Quit()
|
||||||
NotifyEvent(message string)
|
NotifyEvent(message string)
|
||||||
CallResult(message string)
|
CallResult(message string)
|
||||||
OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string)
|
SaveFileDialog(title string) string
|
||||||
SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string)
|
OpenFileDialog(title string) string
|
||||||
MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string)
|
OpenDirectoryDialog(title string) string
|
||||||
WindowSetTitle(title string)
|
WindowSetTitle(title string)
|
||||||
WindowShow()
|
|
||||||
WindowHide()
|
|
||||||
WindowCenter()
|
|
||||||
WindowMaximise()
|
|
||||||
WindowUnmaximise()
|
|
||||||
WindowMinimise()
|
|
||||||
WindowUnminimise()
|
|
||||||
WindowPosition(x int, y int)
|
|
||||||
WindowSize(width int, height int)
|
|
||||||
WindowSetMinSize(width int, height int)
|
|
||||||
WindowSetMaxSize(width int, height int)
|
|
||||||
WindowFullscreen()
|
WindowFullscreen()
|
||||||
WindowUnFullscreen()
|
WindowUnFullscreen()
|
||||||
WindowSetColour(colour int)
|
WindowSetColour(colour string) bool
|
||||||
DarkModeEnabled(callbackID string)
|
|
||||||
SetApplicationMenu(menuJSON string)
|
|
||||||
SetTrayMenu(trayMenuJSON string)
|
|
||||||
UpdateTrayMenuLabel(JSON string)
|
|
||||||
UpdateContextMenu(contextMenuJSON string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DispatchClient is what the frontends use to interface with the
|
// DispatchClient is what the frontends use to interface with the
|
||||||
@@ -71,7 +53,7 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
|||||||
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
||||||
parsedMessage, err := message.Parse(incomingMessage)
|
parsedMessage, err := message.Parse(incomingMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.logger.Error(err.Error())
|
d.logger.Trace("Error: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +62,14 @@ func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
|||||||
|
|
||||||
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
||||||
|
|
||||||
|
// Check error
|
||||||
|
if err != nil {
|
||||||
|
d.logger.Trace("Error: " + err.Error())
|
||||||
|
// Hrm... what do we do with this?
|
||||||
|
d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Publish the parsed message
|
// Publish the parsed message
|
||||||
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CallMessage struct {
|
type CallMessage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Args []json.RawMessage `json:"args"`
|
Args []interface{} `json:"args"`
|
||||||
CallbackID string `json:"callbackID,omitempty"`
|
CallbackID string `json:"callbackID,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// callMessageParser does what it says on the tin!
|
// callMessageParser does what it says on the tin!
|
||||||
@@ -22,7 +22,6 @@ func callMessageParser(message string) (*parsedMessage, error) {
|
|||||||
callMessage := new(CallMessage)
|
callMessage := new(CallMessage)
|
||||||
|
|
||||||
m := message[1:]
|
m := message[1:]
|
||||||
|
|
||||||
err := json.Unmarshal([]byte(m), callMessage)
|
err := json.Unmarshal([]byte(m), callMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
println(err.Error())
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package message
|
package message
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
import "encoding/json"
|
||||||
)
|
|
||||||
|
|
||||||
type EventMessage struct {
|
type EventMessage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -13,7 +12,6 @@ type EventMessage struct {
|
|||||||
type OnEventMessage struct {
|
type OnEventMessage struct {
|
||||||
Name string
|
Name string
|
||||||
Callback func(optionalData ...interface{})
|
Callback func(optionalData ...interface{})
|
||||||
Counter int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// eventMessageParser does what it says on the tin!
|
// eventMessageParser does what it says on the tin!
|
||||||
@@ -29,6 +27,8 @@ func eventMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// Switch the event type (with or without data)
|
// Switch the event type (with or without data)
|
||||||
switch message[0] {
|
switch message[0] {
|
||||||
|
case 'e':
|
||||||
|
eventMessage.Name = message[2:]
|
||||||
case 'E':
|
case 'E':
|
||||||
m := message[2:]
|
m := message[2:]
|
||||||
err := json.Unmarshal([]byte(m), eventMessage)
|
err := json.Unmarshal([]byte(m), eventMessage)
|
||||||
|
|||||||
@@ -3,14 +3,11 @@ package message
|
|||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
var logMessageMap = map[byte]string{
|
var logMessageMap = map[byte]string{
|
||||||
'P': "log:print",
|
|
||||||
'T': "log:trace",
|
|
||||||
'D': "log:debug",
|
'D': "log:debug",
|
||||||
'I': "log:info",
|
'I': "log:info",
|
||||||
'W': "log:warning",
|
'W': "log:warning",
|
||||||
'E': "log:error",
|
'E': "log:error",
|
||||||
'F': "log:fatal",
|
'F': "log:fatal",
|
||||||
'S': "log:setlevel",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// logMessageParser does what it says on the tin!
|
// logMessageParser does what it says on the tin!
|
||||||
@@ -26,7 +23,7 @@ func logMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// If the type is invalid, raise error
|
// If the type is invalid, raise error
|
||||||
if messageTopic == "" {
|
if messageTopic == "" {
|
||||||
return nil, fmt.Errorf("log message type '%c' invalid", message[1])
|
return nil, fmt.Errorf("log message type '%b' invalid", message[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new parsed message struct
|
// Create a new parsed message struct
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ func menuMessageParser(message string) (*parsedMessage, error) {
|
|||||||
callbackid := message[2:]
|
callbackid := message[2:]
|
||||||
topic = "menu:clicked"
|
topic = "menu:clicked"
|
||||||
data = callbackid
|
data = callbackid
|
||||||
|
case 'o':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayopen"
|
||||||
|
data = callbackid
|
||||||
|
case 'c':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayclose"
|
||||||
|
data = callbackid
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid menu message: %s", message)
|
return nil, fmt.Errorf("invalid menu message: %s", message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,25 +14,17 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
|||||||
'L': logMessageParser,
|
'L': logMessageParser,
|
||||||
'R': runtimeMessageParser,
|
'R': runtimeMessageParser,
|
||||||
'E': eventMessageParser,
|
'E': eventMessageParser,
|
||||||
|
'e': eventMessageParser,
|
||||||
'C': callMessageParser,
|
'C': callMessageParser,
|
||||||
'W': windowMessageParser,
|
'W': windowMessageParser,
|
||||||
'D': dialogMessageParser,
|
|
||||||
'S': systemMessageParser,
|
|
||||||
'M': menuMessageParser,
|
|
||||||
'T': trayMessageParser,
|
|
||||||
'X': contextMenusMessageParser,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse will attempt to parse the given message
|
// Parse will attempt to parse the given message
|
||||||
func Parse(message string) (*parsedMessage, error) {
|
func Parse(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
if len(message) == 0 {
|
|
||||||
return nil, fmt.Errorf("MessageParser received blank message");
|
|
||||||
}
|
|
||||||
|
|
||||||
parseMethod := messageParsers[message[0]]
|
parseMethod := messageParsers[message[0]]
|
||||||
if parseMethod == nil {
|
if parseMethod == nil {
|
||||||
return nil, fmt.Errorf("message type '%c' invalid", message[0])
|
return nil, fmt.Errorf("message type '%b' invalid", message[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseMethod(message)
|
return parseMethod(message)
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ func runtimeMessageParser(message string) (*parsedMessage, error) {
|
|||||||
|
|
||||||
// processBrowserMessage expects messages of the following format:
|
// processBrowserMessage expects messages of the following format:
|
||||||
// RB<METHOD><DATA>
|
// RB<METHOD><DATA>
|
||||||
// O = Open
|
|
||||||
func processBrowserMessage(message string) (*parsedMessage, error) {
|
func processBrowserMessage(message string) (*parsedMessage, error) {
|
||||||
method := message[2]
|
method := message[2]
|
||||||
switch method {
|
switch method {
|
||||||
case 'O':
|
case 'U':
|
||||||
// Open URL
|
// Open URL
|
||||||
target := message[3:]
|
url := message[3:]
|
||||||
return &parsedMessage{Topic: "runtime:browser:open", Data: target}, nil
|
return &parsedMessage{Topic: "runtime:browser:openurl", Data: url}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("unknown browser message: %s", message)
|
return nil, fmt.Errorf("unknown browser message: %s", message)
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func systemMessageParser(message string) (*parsedMessage, error) {
|
|||||||
// This is our startup hook - the frontend is now ready
|
// This is our startup hook - the frontend is now ready
|
||||||
case 'S':
|
case 'S':
|
||||||
topic := "hooks:startup"
|
topic := "hooks:startup"
|
||||||
responseMessage = &parsedMessage{Topic: topic, Data: nil}
|
startupURL := message[1:]
|
||||||
|
responseMessage = &parsedMessage{Topic: topic, Data: startupURL}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||||
}
|
}
|
||||||
|
|||||||
20
v2/internal/messagedispatcher/message/url.go
Normal file
20
v2/internal/messagedispatcher/message/url.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package message
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// urlMessageParser does what it says on the tin!
|
||||||
|
func urlMessageParser(message string) (*parsedMessage, error) {
|
||||||
|
|
||||||
|
// Sanity check: URL messages must be at least 2 bytes
|
||||||
|
if len(message) < 2 {
|
||||||
|
return nil, fmt.Errorf("log message was an invalid length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch on the log type
|
||||||
|
switch message[1] {
|
||||||
|
case 'C':
|
||||||
|
return &parsedMessage{Topic: "url:handler", Data: message[2:]}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("url message type '%c' invalid", message[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,66 +22,6 @@ func windowMessageParser(message string) (*parsedMessage, error) {
|
|||||||
parsedMessage.Topic = "quit"
|
parsedMessage.Topic = "quit"
|
||||||
parsedMessage.Data = "Window Closed"
|
parsedMessage.Data = "Window Closed"
|
||||||
|
|
||||||
// Center window
|
|
||||||
case 'c':
|
|
||||||
parsedMessage.Topic = "window:center"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Hide window
|
|
||||||
case 'H':
|
|
||||||
parsedMessage.Topic = "window:hide"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Show window
|
|
||||||
case 'S':
|
|
||||||
parsedMessage.Topic = "window:show"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Position window
|
|
||||||
case 'p':
|
|
||||||
parsedMessage.Topic = "window:position:" + message[3:]
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Set window size
|
|
||||||
case 's':
|
|
||||||
parsedMessage.Topic = "window:size:" + message[3:]
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Maximise window
|
|
||||||
case 'M':
|
|
||||||
parsedMessage.Topic = "window:maximise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Unmaximise window
|
|
||||||
case 'U':
|
|
||||||
parsedMessage.Topic = "window:unmaximise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Minimise window
|
|
||||||
case 'm':
|
|
||||||
parsedMessage.Topic = "window:minimise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Unminimise window
|
|
||||||
case 'u':
|
|
||||||
parsedMessage.Topic = "window:unminimise"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Fullscreen window
|
|
||||||
case 'F':
|
|
||||||
parsedMessage.Topic = "window:fullscreen"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// UnFullscreen window
|
|
||||||
case 'f':
|
|
||||||
parsedMessage.Topic = "window:unfullscreen"
|
|
||||||
parsedMessage.Data = ""
|
|
||||||
|
|
||||||
// Set Title
|
|
||||||
case 'T':
|
|
||||||
parsedMessage.Topic = "window:settitle"
|
|
||||||
parsedMessage.Data = message[2:]
|
|
||||||
|
|
||||||
// Unknown event type
|
// Unknown event type
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown message: %s", message)
|
return nil, fmt.Errorf("unknown message: %s", message)
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
package messagedispatcher
|
package messagedispatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/crypto"
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
@@ -23,8 +19,7 @@ type Dispatcher struct {
|
|||||||
eventChannel <-chan *servicebus.Message
|
eventChannel <-chan *servicebus.Message
|
||||||
windowChannel <-chan *servicebus.Message
|
windowChannel <-chan *servicebus.Message
|
||||||
dialogChannel <-chan *servicebus.Message
|
dialogChannel <-chan *servicebus.Message
|
||||||
systemChannel <-chan *servicebus.Message
|
running bool
|
||||||
menuChannel <-chan *servicebus.Message
|
|
||||||
|
|
||||||
servicebus *servicebus.ServiceBus
|
servicebus *servicebus.ServiceBus
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
@@ -32,13 +27,6 @@ type Dispatcher struct {
|
|||||||
// Clients
|
// Clients
|
||||||
clients map[string]*DispatchClient
|
clients map[string]*DispatchClient
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
// Context for cancellation
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
// internal wait group
|
|
||||||
wg sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New dispatcher. Needs a service bus to send to.
|
// New dispatcher. Needs a service bus to send to.
|
||||||
@@ -73,19 +61,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
systemChannel, err := servicebus.Subscribe("system:")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
menuChannel, err := servicebus.Subscribe("menufrontend:")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create context
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
result := &Dispatcher{
|
result := &Dispatcher{
|
||||||
servicebus: servicebus,
|
servicebus: servicebus,
|
||||||
eventChannel: eventChannel,
|
eventChannel: eventChannel,
|
||||||
@@ -95,10 +70,6 @@ func New(servicebus *servicebus.ServiceBus, logger *logger.Logger) (*Dispatcher,
|
|||||||
quitChannel: quitChannel,
|
quitChannel: quitChannel,
|
||||||
windowChannel: windowChannel,
|
windowChannel: windowChannel,
|
||||||
dialogChannel: dialogChannel,
|
dialogChannel: dialogChannel,
|
||||||
systemChannel: systemChannel,
|
|
||||||
menuChannel: menuChannel,
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -109,18 +80,15 @@ func (d *Dispatcher) Start() error {
|
|||||||
|
|
||||||
d.logger.Trace("Starting")
|
d.logger.Trace("Starting")
|
||||||
|
|
||||||
d.wg.Add(1)
|
d.running = true
|
||||||
|
|
||||||
// Spin off a go routine
|
// Spin off a go routine
|
||||||
go func() {
|
go func() {
|
||||||
defer d.logger.Trace("Shutdown")
|
for d.running {
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
|
||||||
d.wg.Done()
|
|
||||||
return
|
|
||||||
case <-d.quitChannel:
|
case <-d.quitChannel:
|
||||||
d.processQuit()
|
d.processQuit()
|
||||||
|
d.running = false
|
||||||
case resultMessage := <-d.resultChannel:
|
case resultMessage := <-d.resultChannel:
|
||||||
d.processCallResult(resultMessage)
|
d.processCallResult(resultMessage)
|
||||||
case eventMessage := <-d.eventChannel:
|
case eventMessage := <-d.eventChannel:
|
||||||
@@ -129,12 +97,11 @@ func (d *Dispatcher) Start() error {
|
|||||||
d.processWindowMessage(windowMessage)
|
d.processWindowMessage(windowMessage)
|
||||||
case dialogMessage := <-d.dialogChannel:
|
case dialogMessage := <-d.dialogChannel:
|
||||||
d.processDialogMessage(dialogMessage)
|
d.processDialogMessage(dialogMessage)
|
||||||
case systemMessage := <-d.systemChannel:
|
|
||||||
d.processSystemMessage(systemMessage)
|
|
||||||
case menuMessage := <-d.menuChannel:
|
|
||||||
d.processMenuMessage(menuMessage)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call shutdown
|
||||||
|
d.shutdown()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -148,6 +115,10 @@ func (d *Dispatcher) processQuit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) shutdown() {
|
||||||
|
d.logger.Trace("Shutdown")
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterClient will register the given callback with the dispatcher
|
// RegisterClient will register the given callback with the dispatcher
|
||||||
// and return a DispatchClient that the caller can use to send messages
|
// and return a DispatchClient that the caller can use to send messages
|
||||||
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
func (d *Dispatcher) RegisterClient(client Client) *DispatchClient {
|
||||||
@@ -195,35 +166,12 @@ func (d *Dispatcher) processCallResult(result *servicebus.Message) {
|
|||||||
if client == nil {
|
if client == nil {
|
||||||
// This is fatal - unknown target!
|
// This is fatal - unknown target!
|
||||||
d.logger.Fatal("Unknown target for call result: %+v", result)
|
d.logger.Fatal("Unknown target for call result: %+v", result)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string))
|
d.logger.Trace("Sending message to client %s: R%s", target, result.Data().(string))
|
||||||
client.frontend.CallResult(result.Data().(string))
|
client.frontend.CallResult(result.Data().(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
// processSystem
|
|
||||||
func (d *Dispatcher) processSystemMessage(result *servicebus.Message) {
|
|
||||||
|
|
||||||
d.logger.Trace("Got system in message dispatcher: %+v", result)
|
|
||||||
|
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
|
||||||
command := splitTopic[1]
|
|
||||||
callbackID := splitTopic[2]
|
|
||||||
switch command {
|
|
||||||
case "isdarkmode":
|
|
||||||
d.lock.RLock()
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.DarkModeEnabled(callbackID)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d.lock.RUnlock()
|
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown system command: %s", command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processEvent will
|
// processEvent will
|
||||||
func (d *Dispatcher) processEvent(result *servicebus.Message) {
|
func (d *Dispatcher) processEvent(result *servicebus.Message) {
|
||||||
|
|
||||||
@@ -282,7 +230,7 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
client.frontend.WindowUnFullscreen()
|
client.frontend.WindowUnFullscreen()
|
||||||
}
|
}
|
||||||
case "setcolour":
|
case "setcolour":
|
||||||
colour, ok := result.Data().(int)
|
colour, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
|
d.logger.Error("Invalid colour for 'window:setcolour' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
@@ -291,105 +239,6 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.WindowSetColour(colour)
|
client.frontend.WindowSetColour(colour)
|
||||||
}
|
}
|
||||||
case "show":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowShow()
|
|
||||||
}
|
|
||||||
case "hide":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowHide()
|
|
||||||
}
|
|
||||||
case "center":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowCenter()
|
|
||||||
}
|
|
||||||
case "maximise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowMaximise()
|
|
||||||
}
|
|
||||||
case "unmaximise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowUnmaximise()
|
|
||||||
}
|
|
||||||
case "minimise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowMinimise()
|
|
||||||
}
|
|
||||||
case "unminimise":
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowUnminimise()
|
|
||||||
}
|
|
||||||
case "position":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:position' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
x, err1 := strconv.Atoi(splitTopic[2])
|
|
||||||
y, err2 := strconv.Atoi(splitTopic[3])
|
|
||||||
if err1 != nil || err2 != nil {
|
|
||||||
d.logger.Error("Invalid integer parameters for 'window:position' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notify clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowPosition(x, y)
|
|
||||||
}
|
|
||||||
case "size":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:size' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w, err1 := strconv.Atoi(splitTopic[2])
|
|
||||||
h, err2 := strconv.Atoi(splitTopic[3])
|
|
||||||
if err1 != nil || err2 != nil {
|
|
||||||
d.logger.Error("Invalid integer parameters for 'window:size' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notifh clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowSize(w, h)
|
|
||||||
}
|
|
||||||
case "minsize":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:minsize' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w, err1 := strconv.Atoi(splitTopic[2])
|
|
||||||
h, err2 := strconv.Atoi(splitTopic[3])
|
|
||||||
if err1 != nil || err2 != nil {
|
|
||||||
d.logger.Error("Invalid integer parameters for 'window:minsize' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notifh clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowSetMinSize(w, h)
|
|
||||||
}
|
|
||||||
case "maxsize":
|
|
||||||
// We need 2 arguments
|
|
||||||
if len(splitTopic) != 4 {
|
|
||||||
d.logger.Error("Invalid number of parameters for 'window:maxsize' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w, err1 := strconv.Atoi(splitTopic[2])
|
|
||||||
h, err2 := strconv.Atoi(splitTopic[3])
|
|
||||||
if err1 != nil || err2 != nil {
|
|
||||||
d.logger.Error("Invalid integer parameters for 'window:maxsize' : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Notifh clients
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.WindowSetMaxSize(w, h)
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown window command: %s", command)
|
d.logger.Error("Unknown window command: %s", command)
|
||||||
}
|
}
|
||||||
@@ -400,6 +249,7 @@ func (d *Dispatcher) processWindowMessage(result *servicebus.Message) {
|
|||||||
// processDialogMessage processes dialog messages
|
// processDialogMessage processes dialog messages
|
||||||
func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
splitTopic := strings.Split(result.Topic(), ":")
|
||||||
|
|
||||||
if len(splitTopic) < 4 {
|
if len(splitTopic) < 4 {
|
||||||
d.logger.Error("Invalid dialog message : %#v", result.Data())
|
d.logger.Error("Invalid dialog message : %#v", result.Data())
|
||||||
return
|
return
|
||||||
@@ -409,131 +259,61 @@ func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
|||||||
switch command {
|
switch command {
|
||||||
case "select":
|
case "select":
|
||||||
dialogType := splitTopic[2]
|
dialogType := splitTopic[2]
|
||||||
|
title := splitTopic[3]
|
||||||
switch dialogType {
|
switch dialogType {
|
||||||
case "open":
|
case "file":
|
||||||
dialogOptions, ok := result.Data().(*dialog.OpenDialog)
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:open' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:file' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening File dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.OpenDialog(dialogOptions, callbackID)
|
result = client.frontend.OpenFileDialog(title)
|
||||||
}
|
}
|
||||||
case "save":
|
|
||||||
dialogOptions, ok := result.Data().(*dialog.SaveDialog)
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
case "filesave":
|
||||||
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:save' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:filesave' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening Save File dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.SaveDialog(dialogOptions, callbackID)
|
result = client.frontend.SaveFileDialog(title)
|
||||||
}
|
}
|
||||||
case "message":
|
|
||||||
dialogOptions, ok := result.Data().(*dialog.MessageDialog)
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
case "directory":
|
||||||
|
responseTopic, ok := result.Data().(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
d.logger.Error("Invalid data for 'dialog:select:message' : %#v", result.Data())
|
d.logger.Error("Invalid responseTopic for 'dialog:select:directory' : %#v", result.Data())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is hardcoded in the sender too
|
d.logger.Info("Opening Directory dialog! responseTopic = %s", responseTopic)
|
||||||
callbackID := splitTopic[3]
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
// TODO: Work out what we mean in a multi window environment...
|
||||||
// For now we will just pick the first one
|
// For now we will just pick the first one
|
||||||
|
var result string
|
||||||
for _, client := range d.clients {
|
for _, client := range d.clients {
|
||||||
client.frontend.MessageDialog(dialogOptions, callbackID)
|
result = client.frontend.OpenDirectoryDialog(title)
|
||||||
}
|
}
|
||||||
|
// Send dummy response
|
||||||
|
d.servicebus.Publish(responseTopic, result)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
d.logger.Error("Unknown dialog type: %s", dialogType)
|
d.logger.Error("Unknown dialog command: %s", command)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown dialog command: %s", command)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
|
||||||
splitTopic := strings.Split(result.Topic(), ":")
|
|
||||||
if len(splitTopic) < 2 {
|
|
||||||
d.logger.Error("Invalid menu message : %#v", result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
command := splitTopic[1]
|
|
||||||
switch command {
|
|
||||||
case "updateappmenu":
|
|
||||||
|
|
||||||
updatedMenu, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updateappmenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.SetApplicationMenu(updatedMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "settraymenu":
|
|
||||||
trayMenuJSON, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:settraymenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.SetTrayMenu(trayMenuJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "updatecontextmenu":
|
|
||||||
updatedContextMenu, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updatecontextmenu' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.UpdateContextMenu(updatedContextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
case "updatetraymenulabel":
|
|
||||||
updatedTrayMenuLabel, ok := result.Data().(string)
|
|
||||||
if !ok {
|
|
||||||
d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v",
|
|
||||||
result.Data())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Work out what we mean in a multi window environment...
|
|
||||||
// For now we will just pick the first one
|
|
||||||
for _, client := range d.clients {
|
|
||||||
client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) Close() {
|
|
||||||
d.cancel()
|
|
||||||
d.wg.Wait()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"golang.org/x/tools/go/packages"
|
"golang.org/x/tools/go/packages"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var internalMethods = slicer.String([]string{"WailsInit", "Wails Shutdown"})
|
||||||
|
|
||||||
var structCache = make(map[string]*ParsedStruct)
|
var structCache = make(map[string]*ParsedStruct)
|
||||||
var boundStructs = make(map[string]*ParsedStruct)
|
var boundStructs = make(map[string]*ParsedStruct)
|
||||||
var boundMethods = []string{}
|
var boundMethods = []string{}
|
||||||
@@ -47,7 +49,7 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo}
|
cfg := &packages.Config{Mode: packages.NeedFiles | packages.NeedSyntax | packages.NeedTypesInfo}
|
||||||
pkgs, err := packages.Load(cfg, projectPath)
|
pkgs, err := packages.Load(cfg, projectPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "load: %v\n", err)
|
fmt.Fprintf(os.Stderr, "load: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
if packages.PrintErrors(pkgs) > 0 {
|
if packages.PrintErrors(pkgs) > 0 {
|
||||||
@@ -63,6 +65,7 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
var wailsPkgVar = ""
|
var wailsPkgVar = ""
|
||||||
|
|
||||||
ast.Inspect(file, func(n ast.Node) bool {
|
ast.Inspect(file, func(n ast.Node) bool {
|
||||||
|
var s string
|
||||||
switch x := n.(type) {
|
switch x := n.(type) {
|
||||||
// Parse import declarations
|
// Parse import declarations
|
||||||
case *ast.ImportSpec:
|
case *ast.ImportSpec:
|
||||||
@@ -201,6 +204,10 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
// This is a struct pointer method
|
// This is a struct pointer method
|
||||||
i, ok := se.X.(*ast.Ident)
|
i, ok := se.X.(*ast.Ident)
|
||||||
if ok {
|
if ok {
|
||||||
|
// We want to ignore Internal functions
|
||||||
|
if internalMethods.Contains(x.Name.Name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// If we haven't already found this struct,
|
// If we haven't already found this struct,
|
||||||
// Create a placeholder in the cache
|
// Create a placeholder in the cache
|
||||||
parsedStruct := structCache[i.Name]
|
parsedStruct := structCache[i.Name]
|
||||||
@@ -431,6 +438,4 @@ func ParseProject(projectPath string) (BoundStructs, error) {
|
|||||||
println()
|
println()
|
||||||
println("}")
|
println("}")
|
||||||
println()
|
println()
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,26 @@
|
|||||||
package process
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process defines a process that can be executed
|
// Process defines a process that can be executed
|
||||||
type Process struct {
|
type Process struct {
|
||||||
logger *clilogger.CLILogger
|
logger *logger.Logger
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
exitChannel chan bool
|
exitChannel chan bool
|
||||||
Running bool
|
Running bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcess creates a new process struct
|
// NewProcess creates a new process struct
|
||||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
func NewProcess(logger *logger.Logger, cmd string, args ...string) *Process {
|
||||||
result := &Process{
|
return &Process{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
cmd: exec.Command(cmd, args...),
|
cmd: exec.Command(cmd, args...),
|
||||||
exitChannel: make(chan bool, 1),
|
exitChannel: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
result.cmd.Stdout = os.Stdout
|
|
||||||
result.cmd.Stderr = os.Stderr
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the process
|
// Start the process
|
||||||
@@ -37,10 +33,10 @@ func (p *Process) Start() error {
|
|||||||
|
|
||||||
p.Running = true
|
p.Running = true
|
||||||
|
|
||||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
go func(cmd *exec.Cmd, running *bool, logger *logger.Logger, exitChannel chan bool) {
|
||||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
logger.Info("Starting process (PID: %d)", cmd.Process.Pid)
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
logger.Info("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||||
*running = false
|
*running = false
|
||||||
exitChannel <- true
|
exitChannel <- true
|
||||||
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ type Project struct {
|
|||||||
// The path to the project directory
|
// The path to the project directory
|
||||||
Path string
|
Path string
|
||||||
|
|
||||||
// Assets directory
|
|
||||||
AssetsDir string `json:"assetsdir"`
|
|
||||||
|
|
||||||
// The output filename
|
// The output filename
|
||||||
OutputFilename string `json:"outputfilename"`
|
OutputFilename string `json:"outputfilename"`
|
||||||
|
|
||||||
@@ -36,15 +33,6 @@ type Project struct {
|
|||||||
|
|
||||||
// The platform to target
|
// The platform to target
|
||||||
Platform string
|
Platform string
|
||||||
|
|
||||||
// The application author
|
|
||||||
Author Author
|
|
||||||
}
|
|
||||||
|
|
||||||
// Author stores details about the application author
|
|
||||||
type Author struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the project from the current working directory
|
// Load the project from the current working directory
|
||||||
@@ -75,11 +63,6 @@ func Load(projectPath string) (*Project, error) {
|
|||||||
result.Name = "wailsapp"
|
result.Name = "wailsapp"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set default assets directory if none given
|
|
||||||
if result.AssetsDir == "" {
|
|
||||||
result.AssetsDir = filepath.Join(result.Path, "assets")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix up OutputFilename
|
// Fix up OutputFilename
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
|
|||||||
36
v2/internal/runtime/goruntime/browser.go
Normal file
36
v2/internal/runtime/goruntime/browser.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Browser defines all browser related operations
|
||||||
|
type Browser interface {
|
||||||
|
Open(url string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type browser struct{}
|
||||||
|
|
||||||
|
// Open a url / file using the system default application
|
||||||
|
// Credit: https://gist.github.com/hyg/9c4afcd91fe24316cbf0
|
||||||
|
func (b *browser) Open(url string) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
err = exec.Command("xdg-open", url).Start()
|
||||||
|
case "windows":
|
||||||
|
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||||
|
case "darwin":
|
||||||
|
err = exec.Command("open", url).Start()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unsupported platform")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBrowser() *browser {
|
||||||
|
return &browser{}
|
||||||
|
}
|
||||||
16
v2/internal/runtime/goruntime/browser_test.go
Normal file
16
v2/internal/runtime/goruntime/browser_test.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrowserOpen(t *testing.T) {
|
||||||
|
mylogger := logger.New(os.Stdout)
|
||||||
|
myServiceBus := servicebus.New(mylogger)
|
||||||
|
myRuntime := New(myServiceBus)
|
||||||
|
myRuntime.Browser.Open("http://www.google.com")
|
||||||
|
}
|
||||||
105
v2/internal/runtime/goruntime/dialog.go
Normal file
105
v2/internal/runtime/goruntime/dialog.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/internal/crypto"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dialog defines all Dialog related operations
|
||||||
|
type Dialog interface {
|
||||||
|
SaveFile(title string) string
|
||||||
|
SelectFile(title string) string
|
||||||
|
SelectDirectory(title string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// dialog exposes the Dialog interface
|
||||||
|
type dialog struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newDialogs creates a new Dialogs struct
|
||||||
|
func newDialog(bus *servicebus.ServiceBus) Dialog {
|
||||||
|
return &dialog{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectFile prompts the user to select a file
|
||||||
|
func (r *dialog) SelectFile(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:fileselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:file:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
result := <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveFile prompts the user to select a file to save to
|
||||||
|
func (r *dialog) SaveFile(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:filesaveselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:filesave:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
result := <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectDirectory prompts the user to select a file
|
||||||
|
func (r *dialog) SelectDirectory(title string) string {
|
||||||
|
|
||||||
|
// Create unique dialog callback
|
||||||
|
uniqueCallback := crypto.RandomID()
|
||||||
|
|
||||||
|
// Subscribe to the respose channel
|
||||||
|
responseTopic := "dialog:directoryselected:" + uniqueCallback
|
||||||
|
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish dialog request
|
||||||
|
// TODO: Add title to request
|
||||||
|
r.bus.Publish("dialog:select:directory:"+title, responseTopic)
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
var result *servicebus.Message = <-dialogResponseChannel
|
||||||
|
|
||||||
|
// Delete subscription to response topic
|
||||||
|
r.bus.UnSubscribe(responseTopic)
|
||||||
|
|
||||||
|
return result.Data().(string)
|
||||||
|
}
|
||||||
43
v2/internal/runtime/goruntime/events.go
Normal file
43
v2/internal/runtime/goruntime/events.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Events defines all events related operations
|
||||||
|
type Events interface {
|
||||||
|
On(eventName string, callback func(optionalData ...interface{}))
|
||||||
|
Emit(eventName string, optionalData ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// event exposes the events interface
|
||||||
|
type event struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEvents creates a new Events struct
|
||||||
|
func newEvents(bus *servicebus.ServiceBus) Events {
|
||||||
|
return &event{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On pass through
|
||||||
|
func (r *event) On(eventName string, callback func(optionalData ...interface{})) {
|
||||||
|
eventMessage := &message.OnEventMessage{
|
||||||
|
Name: eventName,
|
||||||
|
Callback: callback,
|
||||||
|
}
|
||||||
|
r.bus.Publish("event:on", eventMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit pass through
|
||||||
|
func (r *event) Emit(eventName string, optionalData ...interface{}) {
|
||||||
|
eventMessage := &message.EventMessage{
|
||||||
|
Name: eventName,
|
||||||
|
Data: optionalData,
|
||||||
|
}
|
||||||
|
|
||||||
|
r.bus.Publish("event:emit:from:g", eventMessage)
|
||||||
|
}
|
||||||
28
v2/internal/runtime/goruntime/runtime.go
Normal file
28
v2/internal/runtime/goruntime/runtime.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
|
||||||
|
// Runtime is a means for the user to interact with the application at runtime
|
||||||
|
type Runtime struct {
|
||||||
|
Browser Browser
|
||||||
|
Events Events
|
||||||
|
Window Window
|
||||||
|
Dialog Dialog
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new runtime
|
||||||
|
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||||
|
return &Runtime{
|
||||||
|
Browser: newBrowser(),
|
||||||
|
Events: newEvents(serviceBus),
|
||||||
|
Window: newWindow(serviceBus),
|
||||||
|
Dialog: newDialog(serviceBus),
|
||||||
|
bus: serviceBus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit the application
|
||||||
|
func (r *Runtime) Quit() {
|
||||||
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
|
}
|
||||||
54
v2/internal/runtime/goruntime/window.go
Normal file
54
v2/internal/runtime/goruntime/window.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package goruntime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Window defines all Window related operations
|
||||||
|
type Window interface {
|
||||||
|
Close()
|
||||||
|
SetTitle(title string)
|
||||||
|
Fullscreen()
|
||||||
|
UnFullscreen()
|
||||||
|
SetColour(colour string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window exposes the Windows interface
|
||||||
|
type window struct {
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
|
}
|
||||||
|
|
||||||
|
// newWindow creates a new window struct
|
||||||
|
func newWindow(bus *servicebus.ServiceBus) Window {
|
||||||
|
return &window{
|
||||||
|
bus: bus,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the Window
|
||||||
|
// DISCUSSION:
|
||||||
|
// Should we even be doing this now we have a server build?
|
||||||
|
// Runtime.Quit() makes more sense than closing a window...
|
||||||
|
func (w *window) Close() {
|
||||||
|
w.bus.Publish("quit", "runtime.Close()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTitle sets the title of the window
|
||||||
|
func (w *window) SetTitle(title string) {
|
||||||
|
w.bus.Publish("window:settitle", title)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fullscreen makes the window fullscreen
|
||||||
|
func (w *window) Fullscreen() {
|
||||||
|
w.bus.Publish("window:fullscreen", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnFullscreen makes the window UnFullscreen
|
||||||
|
func (w *window) UnFullscreen() {
|
||||||
|
w.bus.Publish("window:unfullscreen", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetColour sets the window colour to the given string
|
||||||
|
func (w *window) SetColour(colour string) {
|
||||||
|
w.bus.Publish("window:setcolour", colour)
|
||||||
|
}
|
||||||
@@ -65,7 +65,7 @@ export function SetBindings(bindingsMap) {
|
|||||||
window.backend[packageName][structName][methodName] = function () {
|
window.backend[packageName][structName][methodName] = function () {
|
||||||
|
|
||||||
// No timeout by default
|
// No timeout by default
|
||||||
let timeout = 0;
|
var timeout = 0;
|
||||||
|
|
||||||
// Actual function
|
// Actual function
|
||||||
function dynamic() {
|
function dynamic() {
|
||||||
@@ -89,3 +89,19 @@ export function SetBindings(bindingsMap) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// /**
|
||||||
|
// * Determines if the given identifier is valid Javascript
|
||||||
|
// *
|
||||||
|
// * @param {boolean} name
|
||||||
|
// * @returns
|
||||||
|
// */
|
||||||
|
// function isValidIdentifier(name) {
|
||||||
|
// // Don't xss yourself :-)
|
||||||
|
// try {
|
||||||
|
// new Function('var ' + name);
|
||||||
|
// return true;
|
||||||
|
// } catch (e) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,23 @@ The lightweight framework for web-like apps
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the given URL / filename in the system browser
|
* Opens the given URL in the system browser
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} target
|
* @param {string} url
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function Open(target) {
|
export function OpenURL(url) {
|
||||||
return SendMessage('RBO' + target);
|
return SendMessage('RBU' + url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function OpenFile(filename) {
|
||||||
|
return SendMessage('runtime:browser:openfile', filename);
|
||||||
|
}
|
||||||
|
|||||||
@@ -151,7 +151,6 @@ export function Callback(incomingMessage) {
|
|||||||
* @param {any[]=} data
|
* @param {any[]=} data
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function SystemCall(method) {
|
export function SystemCall(method, data) {
|
||||||
var data = [].slice.apply(arguments).slice(1);
|
|
||||||
return Call('.wails.' + method, data);
|
return Call('.wails.' + method, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,16 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import { SetBindings } from './bindings';
|
import { SetBindings } from './bindings';
|
||||||
import { Init } from './main';
|
import { Init } from './main';
|
||||||
import {RaiseError} from '../desktop/darwin';
|
|
||||||
|
|
||||||
// Setup global error handler
|
// Setup global error handler
|
||||||
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
window.onerror = function (/*msg, url, lineNo, columnNo, error*/) {
|
||||||
const errorMessage = {
|
// window.wails.Log.Error('**** Caught Unhandled Error ****');
|
||||||
message: msg,
|
// window.wails.Log.Error('Message: ' + msg);
|
||||||
url: url,
|
// window.wails.Log.Error('URL: ' + url);
|
||||||
line: lineNo,
|
// window.wails.Log.Error('Line No: ' + lineNo);
|
||||||
column: columnNo,
|
// window.wails.Log.Error('Column No: ' + columnNo);
|
||||||
error: JSON.stringify(error),
|
// window.wails.Log.Error('error: ' + error);
|
||||||
stack: function() { return JSON.stringify(new Error().stack); }(),
|
(function () { window.wails.Log.Error(new Error().stack); })();
|
||||||
};
|
|
||||||
RaiseError(errorMessage);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialise the Runtime
|
// Initialise the Runtime
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { Error } from './log';
|
|||||||
import { SendMessage } from 'ipc';
|
import { SendMessage } from 'ipc';
|
||||||
|
|
||||||
// Defines a single listener with a maximum number of times to callback
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Listener class defines a listener! :-)
|
* The Listener class defines a listener! :-)
|
||||||
*
|
*
|
||||||
@@ -44,7 +43,7 @@ class Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let eventListeners = {};
|
var eventListeners = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
@@ -72,17 +71,6 @@ export function On(eventName, callback) {
|
|||||||
OnMultiple(eventName, callback);
|
OnMultiple(eventName, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers listeners for when the system theme changes from light/dark. A bool is
|
|
||||||
* sent to the listener, true if it is dark mode.
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {function} callback
|
|
||||||
*/
|
|
||||||
export function OnThemeChange(callback) {
|
|
||||||
On('wails:system:themechange', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked once then destroyed
|
* Registers an event listener that will be invoked once then destroyed
|
||||||
*
|
*
|
||||||
@@ -94,43 +82,11 @@ export function Once(eventName, callback) {
|
|||||||
OnMultiple(eventName, callback, 1);
|
OnMultiple(eventName, callback, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyListeners(eventData) {
|
|
||||||
|
|
||||||
// Get the event name
|
|
||||||
let eventName = eventData.name;
|
|
||||||
|
|
||||||
// Check if we have any listeners for this event
|
|
||||||
if (eventListeners[eventName]) {
|
|
||||||
|
|
||||||
// Keep a list of listener indexes to destroy
|
|
||||||
const newEventListenerList = eventListeners[eventName].slice();
|
|
||||||
|
|
||||||
// Iterate listeners
|
|
||||||
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
|
||||||
|
|
||||||
// Get next listener
|
|
||||||
const listener = eventListeners[eventName][count];
|
|
||||||
|
|
||||||
let data = eventData.data;
|
|
||||||
|
|
||||||
// Do the callback
|
|
||||||
const destroy = listener.Callback(data);
|
|
||||||
if (destroy) {
|
|
||||||
// if the listener indicated to destroy itself, add it to the destroy list
|
|
||||||
newEventListenerList.splice(count, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update callbacks with new list of listeners
|
|
||||||
eventListeners[eventName] = newEventListenerList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify informs frontend listeners that an event was emitted with the given data
|
* Notify informs frontend listeners that an event was emitted with the given data
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} notifyMessage - encoded notification message
|
* @param {string} encoded notification message
|
||||||
|
|
||||||
*/
|
*/
|
||||||
export function Notify(notifyMessage) {
|
export function Notify(notifyMessage) {
|
||||||
@@ -144,7 +100,33 @@ export function Notify(notifyMessage) {
|
|||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners(message);
|
var eventName = message.name;
|
||||||
|
|
||||||
|
// Check if we have any listeners for this event
|
||||||
|
if (eventListeners[eventName]) {
|
||||||
|
|
||||||
|
// Keep a list of listener indexes to destroy
|
||||||
|
const newEventListenerList = eventListeners[eventName].slice();
|
||||||
|
|
||||||
|
// Iterate listeners
|
||||||
|
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
||||||
|
|
||||||
|
// Get next listener
|
||||||
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
|
var data = message.data;
|
||||||
|
|
||||||
|
// Do the callback
|
||||||
|
const destroy = listener.Callback(data);
|
||||||
|
if (destroy) {
|
||||||
|
// if the listener indicated to destroy itself, add it to the destroy list
|
||||||
|
newEventListenerList.splice(count, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update callbacks with new list of listners
|
||||||
|
eventListeners[eventName] = newEventListenerList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,15 +137,66 @@ export function Notify(notifyMessage) {
|
|||||||
*/
|
*/
|
||||||
export function Emit(eventName) {
|
export function Emit(eventName) {
|
||||||
|
|
||||||
const payload = {
|
// Calculate the data
|
||||||
name: eventName,
|
if (arguments.length > 1) {
|
||||||
data: [].slice.apply(arguments).slice(1),
|
// Notify backend
|
||||||
};
|
const payload = {
|
||||||
|
name: eventName,
|
||||||
// Notify JS listeners
|
data: [].slice.apply(arguments).slice(1),
|
||||||
notifyListeners(payload);
|
};
|
||||||
|
SendMessage('Ej' + JSON.stringify(payload));
|
||||||
// Notify Go listeners
|
} else {
|
||||||
SendMessage('Ej' + JSON.stringify(payload));
|
SendMessage('ej' + eventName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callbacks for the heartbeat calls
|
||||||
|
const heartbeatCallbacks = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
|
|
||||||
|
// Declare interval variable
|
||||||
|
let interval = null;
|
||||||
|
|
||||||
|
// Setup callback
|
||||||
|
function dynamicCallback() {
|
||||||
|
// Kill interval
|
||||||
|
clearInterval(interval);
|
||||||
|
// Callback
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register callback
|
||||||
|
heartbeatCallbacks[eventName] = dynamicCallback;
|
||||||
|
|
||||||
|
// Start emitting the event
|
||||||
|
interval = setInterval(function () {
|
||||||
|
Emit(eventName);
|
||||||
|
}, timeInMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
export function Acknowledge(eventName) {
|
||||||
|
// If we are waiting for acknowledgement for this event type
|
||||||
|
if (heartbeatCallbacks[eventName]) {
|
||||||
|
// Acknowledge!
|
||||||
|
heartbeatCallbacks[eventName]();
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot acknowledge unknown heartbeat '${eventName}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,26 +25,6 @@ function sendLogMessage(level, message) {
|
|||||||
SendMessage('L' + level + message);
|
SendMessage('L' + level + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given trace message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function Trace(message) {
|
|
||||||
sendLogMessage('T', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function Print(message) {
|
|
||||||
sendLogMessage('P', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log the given debug message with the backend
|
* Log the given debug message with the backend
|
||||||
*
|
*
|
||||||
@@ -94,22 +74,3 @@ export function Error(message) {
|
|||||||
export function Fatal(message) {
|
export function Fatal(message) {
|
||||||
sendLogMessage('F', message);
|
sendLogMessage('F', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Log level to the given log level
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {number} loglevel
|
|
||||||
*/
|
|
||||||
export function SetLogLevel(loglevel) {
|
|
||||||
sendLogMessage('S', loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log levels
|
|
||||||
export const Level = {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -10,15 +10,11 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
import * as Log from './log';
|
import * as Log from './log';
|
||||||
import * as Browser from './browser';
|
import * as Browser from './browser';
|
||||||
import * as Window from './window';
|
import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
|
||||||
import * as Dialog from './dialog';
|
import { Callback } from './calls';
|
||||||
import { On, Once, OnMultiple, Emit, Notify } from './events';
|
import { AddScript, InjectCSS } from './utils';
|
||||||
import { Callback, SystemCall } from './calls';
|
|
||||||
import { AddScript, InjectCSS, DisableDefaultContextMenu } from './utils';
|
|
||||||
import { AddIPCListener } from 'ipc';
|
import { AddIPCListener } from 'ipc';
|
||||||
import * as Platform from 'platform';
|
import * as Platform from 'platform';
|
||||||
import * as Store from './store';
|
|
||||||
import * as Tray from './tray';
|
|
||||||
|
|
||||||
export function Init() {
|
export function Init() {
|
||||||
// Backend is where the Go struct wrappers get bound to
|
// Backend is where the Go struct wrappers get bound to
|
||||||
@@ -29,37 +25,23 @@ export function Init() {
|
|||||||
System: Platform.System,
|
System: Platform.System,
|
||||||
Log,
|
Log,
|
||||||
Browser,
|
Browser,
|
||||||
Window,
|
|
||||||
Tray,
|
|
||||||
Dialog,
|
|
||||||
Events: {
|
Events: {
|
||||||
On,
|
On,
|
||||||
Once,
|
|
||||||
OnMultiple,
|
OnMultiple,
|
||||||
Emit,
|
Emit,
|
||||||
|
Heartbeat,
|
||||||
|
Acknowledge,
|
||||||
},
|
},
|
||||||
_: {
|
_: {
|
||||||
Callback,
|
Callback,
|
||||||
Notify,
|
Notify,
|
||||||
AddScript,
|
AddScript,
|
||||||
InjectCSS,
|
InjectCSS,
|
||||||
DisableDefaultContextMenu,
|
Init,
|
||||||
// Init,
|
AddIPCListener
|
||||||
AddIPCListener,
|
}
|
||||||
SystemCall,
|
|
||||||
},
|
|
||||||
Store,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup system. Store uses window.wails so needs to be setup after that
|
|
||||||
window.wails.System = {
|
|
||||||
IsDarkMode: Store.New('wails:isdarkmode'),
|
|
||||||
LogLevel: Store.New('wails:loglevel'),
|
|
||||||
AppConfig: Store.New('wails:appconfig'),
|
|
||||||
};
|
|
||||||
// Copy platform specific information into it
|
|
||||||
Object.assign(window.wails.System, Platform.System);
|
|
||||||
|
|
||||||
// Do platform specific Init
|
// Do platform specific Init
|
||||||
Platform.Init();
|
Platform.Init();
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,3 @@ export function InjectCSS(css) {
|
|||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DisableDefaultContextMenu() {
|
|
||||||
window.disableWailsDefaultContextMenu = true;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const System = {
|
export const System = {
|
||||||
...common,
|
Platform: "linux",
|
||||||
Platform: () => "linux",
|
AppType: "desktop"
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SendMessage(message) {
|
export function SendMessage(message) {
|
||||||
@@ -27,7 +27,7 @@ export function Init() {
|
|||||||
// Setup drag handler
|
// Setup drag handler
|
||||||
// Based on code from: https://github.com/patr0nus/DeskGap
|
// Based on code from: https://github.com/patr0nus/DeskGap
|
||||||
window.addEventListener('mousedown', function (e) {
|
window.addEventListener('mousedown', function (e) {
|
||||||
let currentElement = e.target;
|
var currentElement = e.target;
|
||||||
while (currentElement != null) {
|
while (currentElement != null) {
|
||||||
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
if (currentElement.hasAttribute('data-wails-no-drag')) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
6
v2/internal/runtime/js/package-lock.json
generated
6
v2/internal/runtime/js/package-lock.json
generated
@@ -1923,9 +1923,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001171",
|
"version": "1.0.30001040",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001171.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001040.tgz",
|
||||||
"integrity": "sha512-5Alrh8TTYPG9IH4UkRqEBZoEToWRLvPbSQokvzSz0lii8/FOWKG4keO1HoYfPWs8IF/NH/dyNPg1cmJGvV3Zlg==",
|
"integrity": "sha512-Ep0tEPeI5wCvmJNrXjE3etgfI+lkl1fTDU6Y3ZH1mhrjkPlVI9W4pcKbMo+BQLpEWKVYYp2EmYaRsqpPC3k7lQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"chalk": {
|
"chalk": {
|
||||||
|
|||||||
@@ -39,6 +39,5 @@
|
|||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"webpack": "^4.41.5",
|
"webpack": "^4.41.5",
|
||||||
"webpack-cli": "^3.3.10"
|
"webpack-cli": "^3.3.10"
|
||||||
},
|
}
|
||||||
"dependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
7a2c438e79cf603ba763055e515650be
|
77a32d4461a2cad598edfd551fe64dcd
|
||||||
@@ -1 +1 @@
|
|||||||
src
|
bridge.js
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Wails Runtime
|
# Wails Runtime
|
||||||
|
|
||||||
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) V2 project, not a standalone module.
|
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) project, not a standalone module.
|
||||||
|
|||||||
@@ -620,7 +620,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Menubar **/
|
/** Menubar **/
|
||||||
const menuVisible = writable(true);
|
const menuVisible = writable(false);
|
||||||
|
|
||||||
/** Trays **/
|
/** Trays **/
|
||||||
|
|
||||||
@@ -649,6 +649,18 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteTrayMenu(id) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === id);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
return log("ERROR: Attempted to delete tray index ")
|
||||||
|
}
|
||||||
|
current.splice(index, 1);
|
||||||
|
return current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let selectedMenu = writable(null);
|
let selectedMenu = writable(null);
|
||||||
|
|
||||||
function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
|
function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
|
||||||
@@ -1220,11 +1232,11 @@
|
|||||||
|
|
||||||
function get_each_context$1(ctx, list, i) {
|
function get_each_context$1(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[8] = list[i];
|
child_ctx[9] = list[i];
|
||||||
return child_ctx;
|
return child_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// (29:0) {#if $menuVisible }
|
// (38:0) {#if $menuVisible }
|
||||||
function create_if_block$3(ctx) {
|
function create_if_block$3(ctx) {
|
||||||
let div;
|
let div;
|
||||||
let span1;
|
let span1;
|
||||||
@@ -1336,11 +1348,11 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// (32:4) {#each $trays as tray}
|
// (41:4) {#each $trays as tray}
|
||||||
function create_each_block$1(ctx) {
|
function create_each_block$1(ctx) {
|
||||||
let traymenu;
|
let traymenu;
|
||||||
let current;
|
let current;
|
||||||
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[8] } });
|
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[9] } });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
@@ -1352,7 +1364,7 @@
|
|||||||
},
|
},
|
||||||
p(ctx, dirty) {
|
p(ctx, dirty) {
|
||||||
const traymenu_changes = {};
|
const traymenu_changes = {};
|
||||||
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[8];
|
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[9];
|
||||||
traymenu.$set(traymenu_changes);
|
traymenu.$set(traymenu_changes);
|
||||||
},
|
},
|
||||||
i(local) {
|
i(local) {
|
||||||
@@ -1373,6 +1385,8 @@
|
|||||||
function create_fragment$3(ctx) {
|
function create_fragment$3(ctx) {
|
||||||
let if_block_anchor;
|
let if_block_anchor;
|
||||||
let current;
|
let current;
|
||||||
|
let mounted;
|
||||||
|
let dispose;
|
||||||
let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx);
|
let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -1384,6 +1398,11 @@
|
|||||||
if (if_block) if_block.m(target, anchor);
|
if (if_block) if_block.m(target, anchor);
|
||||||
insert(target, if_block_anchor, anchor);
|
insert(target, if_block_anchor, anchor);
|
||||||
current = true;
|
current = true;
|
||||||
|
|
||||||
|
if (!mounted) {
|
||||||
|
dispose = listen(window, "keydown", /*handleKeydown*/ ctx[3]);
|
||||||
|
mounted = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
p(ctx, [dirty]) {
|
p(ctx, [dirty]) {
|
||||||
if (/*$menuVisible*/ ctx[1]) {
|
if (/*$menuVisible*/ ctx[1]) {
|
||||||
@@ -1421,6 +1440,8 @@
|
|||||||
d(detaching) {
|
d(detaching) {
|
||||||
if (if_block) if_block.d(detaching);
|
if (if_block) if_block.d(detaching);
|
||||||
if (detaching) detach(if_block_anchor);
|
if (detaching) detach(if_block_anchor);
|
||||||
|
mounted = false;
|
||||||
|
dispose();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1440,7 +1461,7 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
const interval = setInterval(
|
const interval = setInterval(
|
||||||
() => {
|
() => {
|
||||||
$$invalidate(3, time = new Date());
|
$$invalidate(4, time = new Date());
|
||||||
},
|
},
|
||||||
1000
|
1000
|
||||||
);
|
);
|
||||||
@@ -1450,33 +1471,52 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
// Backtick toggle
|
||||||
|
if (e.keyCode == 192) {
|
||||||
|
menuVisible.update(current => {
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$$self.$$.update = () => {
|
$$self.$$.update = () => {
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(4, day = time.toLocaleString("default", { weekday: "short" }));
|
$$invalidate(5, day = time.toLocaleString("default", { weekday: "short" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(5, dom = time.getDate());
|
$$invalidate(6, dom = time.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(6, mon = time.toLocaleString("default", { month: "short" }));
|
$$invalidate(7, mon = time.toLocaleString("default", { month: "short" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*time*/ 8) {
|
if ($$self.$$.dirty & /*time*/ 16) {
|
||||||
$$invalidate(7, currentTime = time.toLocaleString("en-US", {
|
$$invalidate(8, currentTime = time.toLocaleString("en-US", {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "numeric",
|
minute: "numeric",
|
||||||
hour12: true
|
hour12: true
|
||||||
}).toLowerCase());
|
}).toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 240) {
|
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 480) {
|
||||||
$$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`);
|
$$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return [dateTimeString, $menuVisible, $trays, time, day, dom, mon, currentTime];
|
return [
|
||||||
|
dateTimeString,
|
||||||
|
$menuVisible,
|
||||||
|
$trays,
|
||||||
|
handleKeydown,
|
||||||
|
time,
|
||||||
|
day,
|
||||||
|
dom,
|
||||||
|
mon,
|
||||||
|
currentTime
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
class Menubar extends SvelteComponent {
|
class Menubar extends SvelteComponent {
|
||||||
@@ -1638,6 +1678,11 @@
|
|||||||
let trayLabelData = JSON.parse(updateTrayLabelJSON);
|
let trayLabelData = JSON.parse(updateTrayLabelJSON);
|
||||||
updateTrayLabel(trayLabelData);
|
updateTrayLabel(trayLabelData);
|
||||||
break
|
break
|
||||||
|
case 'D':
|
||||||
|
// Delete Tray Menu
|
||||||
|
const id = trayMessage.slice(1);
|
||||||
|
deleteTrayMenu(id);
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
log('Unknown tray message: ' + message.data);
|
log('Unknown tray message: ' + message.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,28 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the given URL or Filename in the system browser
|
* Opens the given URL in the system browser
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} target
|
* @param {string} url
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function Open(target) {
|
function OpenURL(url) {
|
||||||
return window.wails.Browser.Open(target);
|
return window.wails.Browser.OpenURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function OpenFile(filename) {
|
||||||
|
return window.wails.Browser.OpenFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OpenURL: OpenURL,
|
||||||
|
OpenFile: OpenFile
|
||||||
|
};
|
||||||
@@ -56,14 +56,28 @@ function Emit(eventName) {
|
|||||||
return window.wails.Events.Emit.apply(null, args);
|
return window.wails.Events.Emit.apply(null, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers listeners for when the system theme changes
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function OnThemeChange(callback) {
|
function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
On('wails:system:themechange', callback);
|
window.wails.Events.Heartbeat(eventName, timeInMilliseconds, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
function Acknowledge(eventName) {
|
||||||
|
return window.wails.Events.Acknowledge(eventName);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -71,5 +85,6 @@ module.exports = {
|
|||||||
On: On,
|
On: On,
|
||||||
Once: Once,
|
Once: Once,
|
||||||
Emit: Emit,
|
Emit: Emit,
|
||||||
OnThemeChange: OnThemeChange,
|
Heartbeat: Heartbeat,
|
||||||
|
Acknowledge: Acknowledge
|
||||||
};
|
};
|
||||||
@@ -9,25 +9,13 @@ The lightweight framework for web-like apps
|
|||||||
*/
|
*/
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
import bridge from './bridge';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ready will execute the callback when Wails has loaded
|
* Initialises the Wails runtime
|
||||||
* and initialised.
|
|
||||||
*
|
*
|
||||||
* @param {function} callback
|
* @param {function} callback
|
||||||
*/
|
*/
|
||||||
function ready(callback) {
|
function Init(callback) {
|
||||||
|
window.wails._.Init(callback);
|
||||||
// If window.wails exists, we are ready
|
|
||||||
if( window.wails ) {
|
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not we need to setup the bridge
|
|
||||||
bridge.InitBridge(callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = Init;
|
||||||
ready: ready,
|
|
||||||
};
|
|
||||||
@@ -11,26 +11,6 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
function Print(message) {
|
|
||||||
window.wails.Log.Print(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given trace message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
function Trace(message) {
|
|
||||||
window.wails.Log.Trace(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log the given debug message with the backend
|
* Log the given debug message with the backend
|
||||||
*
|
*
|
||||||
@@ -74,40 +54,17 @@ function Error(message) {
|
|||||||
/**
|
/**
|
||||||
* Log the given fatal message with the backend
|
* Log the given fatal message with the backend
|
||||||
*
|
*
|
||||||
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
function Fatal(message) {
|
function Fatal(message) {
|
||||||
window.wails.Log.Fatal(message);
|
window.wails.Log.Fatal(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Log level to the given log level
|
|
||||||
*
|
|
||||||
* @param {number} loglevel
|
|
||||||
*/
|
|
||||||
function SetLogLevel(loglevel) {
|
|
||||||
window.wails.Log.SetLogLevel(loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log levels
|
|
||||||
const Level = {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Print: Print,
|
|
||||||
Trace: Trace,
|
|
||||||
Debug: Debug,
|
Debug: Debug,
|
||||||
Info: Info,
|
Info: Info,
|
||||||
Warning: Warning,
|
Warning: Warning,
|
||||||
Error: Error,
|
Error: Error,
|
||||||
Fatal: Fatal,
|
Fatal: Fatal
|
||||||
SetLogLevel: SetLogLevel,
|
|
||||||
Level: Level,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,22 +11,12 @@ The lightweight framework for web-like apps
|
|||||||
|
|
||||||
const Log = require('./log');
|
const Log = require('./log');
|
||||||
const Browser = require('./browser');
|
const Browser = require('./browser');
|
||||||
const Dialog = require('./dialog');
|
|
||||||
const Events = require('./events');
|
const Events = require('./events');
|
||||||
const Init = require('./init');
|
const Init = require('./init');
|
||||||
const System = require('./system');
|
|
||||||
const Store = require('./store');
|
|
||||||
const Window = require('./window');
|
|
||||||
const Tray = require('./tray');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Browser: Browser,
|
|
||||||
Dialog: Dialog,
|
|
||||||
Events: Events,
|
|
||||||
ready: Init.ready,
|
|
||||||
Log: Log,
|
Log: Log,
|
||||||
System: System,
|
Browser: Browser,
|
||||||
Store: Store,
|
Events: Events,
|
||||||
Window: Window,
|
Init: Init
|
||||||
Tray: Tray,
|
|
||||||
};
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@wails/runtime",
|
"name": "@wailsapp/runtime",
|
||||||
"version": "1.3.9",
|
"version": "1.0.10",
|
||||||
"description": "Wails V2 Javascript runtime library",
|
"description": "Wails Javascript runtime library",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"types": "runtime.d.ts",
|
"types": "runtime.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
129
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
129
v2/internal/runtime/js/runtime/runtime.d.ts
vendored
@@ -1,137 +1,26 @@
|
|||||||
export = wailsapp__runtime;
|
export = wailsapp__runtime;
|
||||||
|
|
||||||
interface Store {
|
|
||||||
get(): any;
|
|
||||||
set(value: any): void;
|
|
||||||
subscribe(callback: (newvalue: any) => void): void;
|
|
||||||
update(callback: (currentvalue: any) => any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MacTitleBar {
|
|
||||||
TitleBarAppearsTransparent: boolean; // NSWindow.titleBarAppearsTransparent
|
|
||||||
HideTitle: boolean; // NSWindow.hideTitle
|
|
||||||
HideTitleBar: boolean; // NSWindow.hideTitleBar
|
|
||||||
FullSizeContent: boolean; // Makes the webview portion of the window the full size of the window, even over the titlebar
|
|
||||||
UseToolbar: boolean; // Set true to add a blank toolbar to the window (makes the title bar larger)
|
|
||||||
HideToolbarSeparator: boolean; // Set true to remove the separator between the toolbar and the main content area
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MacAppConfig {
|
|
||||||
TitleBar: MacTitleBar;
|
|
||||||
}
|
|
||||||
interface LinuxAppConfig {
|
|
||||||
}
|
|
||||||
interface WindowsAppConfig {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AppConfig {
|
|
||||||
Title: string; // Application Title
|
|
||||||
Width: number; // Window Width
|
|
||||||
Height: number; // Window Height
|
|
||||||
DisableResize: boolean; // True if resize is disabled
|
|
||||||
Fullscreen: boolean; // App started in fullscreen
|
|
||||||
MinWidth: number; // Window Minimum Width
|
|
||||||
MinHeight: number; // Window Minimum Height
|
|
||||||
MaxWidth: number; // Window Maximum Width
|
|
||||||
MaxHeight: number; // Window Maximum Height
|
|
||||||
StartHidden: boolean; // Start with window hidden
|
|
||||||
DevTools: boolean; // Enables the window devtools
|
|
||||||
RBGA: number; // The initial window colour. Convert to hex then it'll mean 0xRRGGBBAA
|
|
||||||
Mac?: MacAppConfig; // - Configuration when running on Mac
|
|
||||||
Linux?: LinuxAppConfig; // - Configuration when running on Linux
|
|
||||||
Windows?: WindowsAppConfig; // - Configuration when running on Windows
|
|
||||||
Appearance: string; // The default application appearance. Use the values listed here: https://developer.apple.com/documentation/appkit/nsappearance?language=objc
|
|
||||||
WebviewIsTransparent: number; // Makes the background of the webview content transparent. Use this with the Alpha part of the window colour to make parts of your application transparent.
|
|
||||||
WindowBackgroundIsTranslucent: number; // Makes the transparent parts of the application window translucent. Example: https://en.wikipedia.org/wiki/MacOS_Big_Sur#/media/File:MacOS_Big_Sur_-_Safari_Extensions_category_in_App_Store.jpg
|
|
||||||
LogLevel: number; // The initial log level (lower is more verbose)
|
|
||||||
}
|
|
||||||
interface Level {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OpenDialogOptions {
|
|
||||||
DefaultDirectory: string;
|
|
||||||
DefaultFilename: string;
|
|
||||||
Title: string;
|
|
||||||
Filters: string;
|
|
||||||
AllowFiles: boolean;
|
|
||||||
AllowDirectories: boolean;
|
|
||||||
AllowMultiple: boolean;
|
|
||||||
ShowHiddenFiles: boolean;
|
|
||||||
CanCreateDirectories: boolean;
|
|
||||||
ResolvesAliases: boolean;
|
|
||||||
TreatPackagesAsDirectories: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SaveDialogOptions {
|
|
||||||
DefaultDirectory: string;
|
|
||||||
DefaultFilename: string;
|
|
||||||
Title: string;
|
|
||||||
Filters: string;
|
|
||||||
ShowHiddenFiles: boolean;
|
|
||||||
CanCreateDirectories: boolean;
|
|
||||||
TreatPackagesAsDirectories: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DialogType {
|
|
||||||
InfoDialog: 'info',
|
|
||||||
WarningDialog: 'warning',
|
|
||||||
ErrorDialog: 'error',
|
|
||||||
QuestionDialog: 'question',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessageDialogOptions {
|
|
||||||
Type: DialogType;
|
|
||||||
Title: string;
|
|
||||||
Message: string;
|
|
||||||
Buttons: string[];
|
|
||||||
DefaultButton: string;
|
|
||||||
CancelButton: string;
|
|
||||||
Icon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare const wailsapp__runtime: {
|
declare const wailsapp__runtime: {
|
||||||
Browser: {
|
Browser: {
|
||||||
Open(target: string): Promise<any>;
|
OpenFile(filename: string): Promise<any>;
|
||||||
|
OpenURL(url: string): Promise<any>;
|
||||||
};
|
};
|
||||||
Events: {
|
Events: {
|
||||||
Emit(eventName: string, data?: any): void;
|
Acknowledge(eventName: string): void;
|
||||||
On(eventName: string, callback: (data?: any) => void): void;
|
Emit(eventName: string): void;
|
||||||
OnMultiple(eventName: string, callback: (data?: any) => void, maxCallbacks: number): void;
|
Heartbeat(eventName: string, timeInMilliseconds: number, callback: () => void): void;
|
||||||
Once(eventName: string, callback: (data?: any) => void): void;
|
On(eventName: string, callback: () => void): void;
|
||||||
|
OnMultiple(eventName: string, callback: () => void, maxCallbacks: number): void;
|
||||||
|
Once(eventName: string, callback: () => void): void;
|
||||||
};
|
};
|
||||||
// Init(callback: () => void): void;
|
Init(callback: () => void): void;
|
||||||
Log: {
|
Log: {
|
||||||
Debug(message: string): void;
|
Debug(message: string): void;
|
||||||
Error(message: string): void;
|
Error(message: string): void;
|
||||||
Fatal(message: string): void;
|
Fatal(message: string): void;
|
||||||
Info(message: string): void;
|
Info(message: string): void;
|
||||||
Warning(message: string): void;
|
Warning(message: string): void;
|
||||||
Level: Level;
|
|
||||||
};
|
};
|
||||||
System: {
|
|
||||||
DarkModeEnabled(): Promise<boolean>;
|
|
||||||
OnThemeChange(callback: (darkModeEnabled: boolean) => void): void;
|
|
||||||
LogLevel(): Store;
|
|
||||||
Platform(): string;
|
|
||||||
AppType(): string;
|
|
||||||
AppConfig(): AppConfig;
|
|
||||||
};
|
|
||||||
Store: {
|
|
||||||
New(name: string, defaultValue?: any): Store;
|
|
||||||
};
|
|
||||||
Dialog: {
|
|
||||||
Open(options: OpenDialogOptions): Promise<Array<string>>;
|
|
||||||
Save(options: SaveDialogOptions): Promise<string>;
|
|
||||||
Message(options: MessageDialogOptions): Promise<string>;
|
|
||||||
};
|
|
||||||
Tray: {
|
|
||||||
SetIcon(trayIconID: string): void;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,15 @@
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleKeydown(e) {
|
||||||
|
// Backtick toggle
|
||||||
|
if( e.keyCode == 192 ) {
|
||||||
|
menuVisible.update( (current) => {
|
||||||
|
return !current;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $menuVisible }
|
{#if $menuVisible }
|
||||||
@@ -37,6 +46,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<svelte:window on:keydown={handleKeydown}/>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
.tray-menus {
|
.tray-menus {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export function hideOverlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Menubar **/
|
/** Menubar **/
|
||||||
export const menuVisible = writable(true);
|
export const menuVisible = writable(false);
|
||||||
|
|
||||||
export function showMenuBar() {
|
export function showMenuBar() {
|
||||||
menuVisible.set(true);
|
menuVisible.set(true);
|
||||||
@@ -49,4 +49,16 @@ export function updateTrayLabel(tray) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function deleteTrayMenu(id) {
|
||||||
|
trays.update((current) => {
|
||||||
|
// Remove existing if it exists, else add
|
||||||
|
const index = current.findIndex(item => item.ID === id);
|
||||||
|
if ( index === -1 ) {
|
||||||
|
return log("ERROR: Attempted to delete tray index ", id, "but it doesn't exist")
|
||||||
|
}
|
||||||
|
current.splice(index, 1);
|
||||||
|
return current;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export let selectedMenu = writable(null);
|
export let selectedMenu = writable(null);
|
||||||
@@ -10,7 +10,7 @@ The lightweight framework for web-like apps
|
|||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
import {setTray, hideOverlay, showOverlay, updateTrayLabel} from "./store";
|
import {setTray, hideOverlay, showOverlay, updateTrayLabel, deleteTrayMenu} from "./store";
|
||||||
import {log} from "./log";
|
import {log} from "./log";
|
||||||
|
|
||||||
let websocket = null;
|
let websocket = null;
|
||||||
@@ -154,6 +154,11 @@ function handleMessage(message) {
|
|||||||
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
||||||
updateTrayLabel(trayLabelData)
|
updateTrayLabel(trayLabelData)
|
||||||
break
|
break
|
||||||
|
case 'D':
|
||||||
|
// Delete Tray Menu
|
||||||
|
const id = trayMessage.slice(1);
|
||||||
|
deleteTrayMenu(id)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
log('Unknown tray message: ' + message.data);
|
log('Unknown tray message: ' + message.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
mode: 'production',
|
mode: 'production',
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve(__dirname, '..', 'assets'),
|
path: path.resolve(__dirname, '..', 'assets'),
|
||||||
filename: 'desktop_'+platform+'.js',
|
filename: 'desktop.js',
|
||||||
library: 'Wails'
|
library: 'Wails'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Menu interface {
|
|||||||
UpdateApplicationMenu()
|
UpdateApplicationMenu()
|
||||||
UpdateContextMenu(contextMenu *menu.ContextMenu)
|
UpdateContextMenu(contextMenu *menu.ContextMenu)
|
||||||
SetTrayMenu(trayMenu *menu.TrayMenu)
|
SetTrayMenu(trayMenu *menu.TrayMenu)
|
||||||
|
DeleteTrayMenu(trayMenu *menu.TrayMenu)
|
||||||
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
|
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,3 +40,7 @@ func (m *menuRuntime) SetTrayMenu(trayMenu *menu.TrayMenu) {
|
|||||||
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
|
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
|
||||||
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
|
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *menuRuntime) DeleteTrayMenu(trayMenu *menu.TrayMenu) {
|
||||||
|
m.bus.Publish("menu:deletetraymenu", trayMenu)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,20 +6,19 @@ import (
|
|||||||
|
|
||||||
// Runtime is a means for the user to interact with the application at runtime
|
// Runtime is a means for the user to interact with the application at runtime
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Browser Browser
|
Browser Browser
|
||||||
Events Events
|
Events Events
|
||||||
Window Window
|
Window Window
|
||||||
Dialog Dialog
|
Dialog Dialog
|
||||||
System System
|
System System
|
||||||
Menu Menu
|
Menu Menu
|
||||||
Store *StoreProvider
|
Store *StoreProvider
|
||||||
Log Log
|
Log Log
|
||||||
bus *servicebus.ServiceBus
|
bus *servicebus.ServiceBus
|
||||||
shutdownCallback func()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new runtime
|
// New creates a new runtime
|
||||||
func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||||
result := &Runtime{
|
result := &Runtime{
|
||||||
Browser: newBrowser(),
|
Browser: newBrowser(),
|
||||||
Events: newEvents(serviceBus),
|
Events: newEvents(serviceBus),
|
||||||
@@ -36,11 +35,6 @@ func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
|||||||
|
|
||||||
// Quit the application
|
// Quit the application
|
||||||
func (r *Runtime) Quit() {
|
func (r *Runtime) Quit() {
|
||||||
// Call back to user's shutdown method if defined
|
|
||||||
if r.shutdownCallback != nil {
|
|
||||||
r.shutdownCallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start shutdown of Wails
|
// Start shutdown of Wails
|
||||||
r.bus.Publish("quit", "runtime.Quit()")
|
r.bus.Publish("quit", "runtime.Quit()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package servicebus
|
package servicebus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -13,26 +12,23 @@ import (
|
|||||||
type ServiceBus struct {
|
type ServiceBus struct {
|
||||||
listeners map[string][]chan *Message
|
listeners map[string][]chan *Message
|
||||||
messageQueue chan *Message
|
messageQueue chan *Message
|
||||||
|
quitChannel chan struct{}
|
||||||
|
wg sync.WaitGroup
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
closed bool
|
closed bool
|
||||||
debug bool
|
debug bool
|
||||||
logger logger.CustomLogger
|
logger logger.CustomLogger
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new ServiceBus
|
// New creates a new ServiceBus
|
||||||
// The internal message queue is set to 100 messages
|
// The internal message queue is set to 100 messages
|
||||||
// Listener queues are set to 10
|
// Listener queues are set to 10
|
||||||
func New(logger *logger.Logger) *ServiceBus {
|
func New(logger *logger.Logger) *ServiceBus {
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
return &ServiceBus{
|
return &ServiceBus{
|
||||||
listeners: make(map[string][]chan *Message),
|
listeners: make(map[string][]chan *Message),
|
||||||
messageQueue: make(chan *Message, 100),
|
messageQueue: make(chan *Message, 100),
|
||||||
|
quitChannel: make(chan struct{}, 1),
|
||||||
logger: logger.CustomLogger("Service Bus"),
|
logger: logger.CustomLogger("Service Bus"),
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,22 +63,23 @@ func (s *ServiceBus) Debug() {
|
|||||||
// Start the service bus
|
// Start the service bus
|
||||||
func (s *ServiceBus) Start() error {
|
func (s *ServiceBus) Start() error {
|
||||||
|
|
||||||
|
s.logger.Trace("Starting")
|
||||||
|
|
||||||
// Prevent starting when closed
|
// Prevent starting when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return fmt.Errorf("cannot call start on closed servicebus")
|
return fmt.Errorf("cannot call start on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
s.logger.Trace("Starting")
|
// We run in a different thread
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer s.logger.Trace("Stopped")
|
|
||||||
|
quit := false
|
||||||
|
s.wg.Add(1)
|
||||||
|
|
||||||
// Loop until we get a quit message
|
// Loop until we get a quit message
|
||||||
for {
|
for !quit {
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.ctx.Done():
|
|
||||||
return
|
|
||||||
|
|
||||||
// Listen for messages
|
// Listen for messages
|
||||||
case message := <-s.messageQueue:
|
case message := <-s.messageQueue:
|
||||||
@@ -93,9 +90,16 @@ func (s *ServiceBus) Start() error {
|
|||||||
}
|
}
|
||||||
// Dispatch message
|
// Dispatch message
|
||||||
s.dispatchMessage(message)
|
s.dispatchMessage(message)
|
||||||
|
|
||||||
|
// Listen for quit messages
|
||||||
|
case <-s.quitChannel:
|
||||||
|
quit = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indicate we have shut down
|
||||||
|
s.wg.Done()
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -112,7 +116,10 @@ func (s *ServiceBus) Stop() error {
|
|||||||
s.closed = true
|
s.closed = true
|
||||||
|
|
||||||
// Send quit message
|
// Send quit message
|
||||||
s.cancel()
|
s.quitChannel <- struct{}{}
|
||||||
|
|
||||||
|
// Wait for dispatcher to stop
|
||||||
|
s.wg.Wait()
|
||||||
|
|
||||||
// Close down subscriber channels
|
// Close down subscriber channels
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
@@ -127,6 +134,7 @@ func (s *ServiceBus) Stop() error {
|
|||||||
// Close message queue
|
// Close message queue
|
||||||
close(s.messageQueue)
|
close(s.messageQueue)
|
||||||
|
|
||||||
|
s.logger.Trace("Stopped")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,22 +168,25 @@ func (s *ServiceBus) Subscribe(topic string) (<-chan *Message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Publish sends the given message on the service bus
|
// Publish sends the given message on the service bus
|
||||||
func (s *ServiceBus) Publish(topic string, data interface{}) {
|
func (s *ServiceBus) Publish(topic string, data interface{}) error {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return
|
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := NewMessage(topic, data)
|
message := NewMessage(topic, data)
|
||||||
s.messageQueue <- message
|
s.messageQueue <- message
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublishForTarget sends the given message on the service bus for the given target
|
// PublishForTarget sends the given message on the service bus for the given target
|
||||||
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) {
|
func (s *ServiceBus) PublishForTarget(topic string, data interface{}, target string) error {
|
||||||
// Prevent publish when closed
|
// Prevent publish when closed
|
||||||
if s.closed {
|
if s.closed {
|
||||||
return
|
return fmt.Errorf("cannot call publish on closed servicebus")
|
||||||
}
|
}
|
||||||
|
|
||||||
message := NewMessageForTarget(topic, data, target)
|
message := NewMessageForTarget(topic, data, target)
|
||||||
s.messageQueue <- message
|
s.messageQueue <- message
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package signal
|
package signal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"os"
|
"os"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
@@ -22,29 +20,24 @@ type Manager struct {
|
|||||||
// signalChannel
|
// signalChannel
|
||||||
signalchannel chan os.Signal
|
signalchannel chan os.Signal
|
||||||
|
|
||||||
// ctx
|
// Quit channel
|
||||||
ctx context.Context
|
quitChannel <-chan *servicebus.Message
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
// The shutdown callback to notify the user's app that a shutdown
|
|
||||||
// has started
|
|
||||||
shutdownCallback func()
|
|
||||||
|
|
||||||
// Parent waitgroup
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager creates a new signal manager
|
// NewManager creates a new signal manager
|
||||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger, shutdownCallback func()) (*Manager, error) {
|
func NewManager(bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||||
|
|
||||||
|
// Register quit channel
|
||||||
|
quitChannel, err := bus.Subscribe("quit")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
result := &Manager{
|
result := &Manager{
|
||||||
bus: bus,
|
bus: bus,
|
||||||
logger: logger.CustomLogger("Event Manager"),
|
logger: logger.CustomLogger("Event Manager"),
|
||||||
signalchannel: make(chan os.Signal, 2),
|
signalchannel: make(chan os.Signal, 2),
|
||||||
ctx: ctx,
|
quitChannel: quitChannel,
|
||||||
cancel: cancel,
|
|
||||||
shutdownCallback: shutdownCallback,
|
|
||||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -56,28 +49,20 @@ func (m *Manager) Start() {
|
|||||||
// Hook into interrupts
|
// Hook into interrupts
|
||||||
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
gosignal.Notify(m.signalchannel, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
||||||
m.wg.Add(1)
|
// Spin off signal listener
|
||||||
|
|
||||||
// Spin off signal listener and wait for either a cancellation
|
|
||||||
// or signal
|
|
||||||
go func() {
|
go func() {
|
||||||
select {
|
running := true
|
||||||
case <-m.signalchannel:
|
for running {
|
||||||
println()
|
select {
|
||||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
case <-m.signalchannel:
|
||||||
m.bus.Publish("quit", "ctrl-c pressed")
|
println()
|
||||||
|
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||||
// Shutdown app first
|
m.bus.Publish("quit", "ctrl-c pressed")
|
||||||
if m.shutdownCallback != nil {
|
case <-m.quitChannel:
|
||||||
m.shutdownCallback()
|
running = false
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start shutdown of Wails
|
|
||||||
m.cancel()
|
|
||||||
|
|
||||||
case <-m.ctx.Done():
|
|
||||||
}
|
}
|
||||||
m.logger.Trace("Shutdown")
|
m.logger.Trace("Shutdown")
|
||||||
m.wg.Done()
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user