mirror of
https://github.com/taigrr/wails.git
synced 2026-04-04 06:02:43 -07:00
Compare commits
1 Commits
v2.0.0-alp
...
feature/v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29715b2d57 |
@@ -40,5 +40,4 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
||||
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
||||
* [misitebao](https://github.com/misitebao)
|
||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||
* [Amaury Tobias Quiroz](https://github.com/amaury-tobias)
|
||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "**** Checking if Wails passes unit tests ****"
|
||||
if ! go test ./lib/... ./runtime/... ./cmd/...
|
||||
if ! go test ./...
|
||||
then
|
||||
echo ""
|
||||
echo "ERROR: Unit tests failed!"
|
||||
|
||||
@@ -3,10 +3,8 @@ package build
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
@@ -24,6 +22,10 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
|
||||
command := app.NewSubCommand("build", "Builds the application")
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
// Setup production flag
|
||||
production := false
|
||||
command.BoolFlag("production", "Build in production mode", &production)
|
||||
@@ -35,41 +37,28 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
compress := false
|
||||
command.BoolFlag("compress", "Compress final binary", &compress)
|
||||
|
||||
// Setup Platform flag
|
||||
platform := runtime.GOOS
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Verbosity
|
||||
verbosity := 1
|
||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
||||
// Quiet Build
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
|
||||
// ldflags to pass to `go`
|
||||
ldflags := ""
|
||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||
|
||||
// tags to pass to `go`
|
||||
tags := ""
|
||||
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
|
||||
// Log to file
|
||||
logFile := ""
|
||||
command.StringFlag("l", "Log to file", &logFile)
|
||||
|
||||
// Retain assets
|
||||
keepAssets := false
|
||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
||||
|
||||
// Retain assets
|
||||
outputFilename := ""
|
||||
command.StringFlag("o", "Output filename", &outputFilename)
|
||||
|
||||
// Clean build directory
|
||||
cleanBuildDirectory := false
|
||||
command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
quiet := verbosity == 0
|
||||
|
||||
// Create logger
|
||||
logger := clilogger.New(w)
|
||||
logger.Mute(quiet)
|
||||
@@ -83,92 +72,28 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
||||
app.PrintBanner()
|
||||
}
|
||||
|
||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
||||
logger.Println(task)
|
||||
logger.Println(strings.Repeat("-", len(task)))
|
||||
|
||||
// Setup mode
|
||||
mode := build.Debug
|
||||
if production {
|
||||
mode = build.Production
|
||||
}
|
||||
|
||||
// Check platform
|
||||
validPlatformArch := slicer.String([]string{
|
||||
"darwin",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"darwin/universal",
|
||||
//"linux/amd64",
|
||||
//"linux/arm-7",
|
||||
//"windows/amd64",
|
||||
})
|
||||
if !validPlatformArch.Contains(platform) {
|
||||
return fmt.Errorf("platform %s is not supported", platform)
|
||||
}
|
||||
|
||||
if compress && platform == "darwin/universal" {
|
||||
println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
||||
compress = false
|
||||
}
|
||||
|
||||
// Tags
|
||||
userTags := []string{}
|
||||
for _, tag := range strings.Split(tags, " ") {
|
||||
thisTag := strings.TrimSpace(tag)
|
||||
if thisTag != "" {
|
||||
userTags = append(userTags, thisTag)
|
||||
}
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
OutputFile: outputFilename,
|
||||
CleanBuildDirectory: cleanBuildDirectory,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: keepAssets,
|
||||
Verbosity: verbosity,
|
||||
Compress: compress,
|
||||
UserTags: userTags,
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
Platform: platform,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
KeepAssets: keepAssets,
|
||||
}
|
||||
|
||||
// Calculate platform and arch
|
||||
platformSplit := strings.Split(platform, "/")
|
||||
buildOptions.Platform = platformSplit[0]
|
||||
buildOptions.Arch = runtime.GOARCH
|
||||
if len(platformSplit) == 2 {
|
||||
buildOptions.Arch = platformSplit[1]
|
||||
}
|
||||
|
||||
// Start a new tabwriter
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
||||
|
||||
buildModeText := "debug"
|
||||
if production {
|
||||
buildModeText = "production"
|
||||
}
|
||||
|
||||
// Write out the system information
|
||||
fmt.Fprintf(w, "\n")
|
||||
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
||||
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
||||
fmt.Fprintf(w, "Compiler: \t%s\n", buildOptions.Compiler)
|
||||
fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
||||
fmt.Fprintf(w, "Build Mode: \t%s\n", buildModeText)
|
||||
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
||||
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
||||
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
||||
fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
|
||||
if len(buildOptions.OutputFile) > 0 {
|
||||
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
w.Flush()
|
||||
|
||||
return doBuild(buildOptions)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,41 +1,30 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/wailsapp/wails/v2/internal/process"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
||||
)
|
||||
|
||||
func LogGreen(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Green(text))
|
||||
}
|
||||
|
||||
func LogRed(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.Red(text))
|
||||
}
|
||||
|
||||
func LogDarkYellow(message string, args ...interface{}) {
|
||||
text := fmt.Sprintf(message, args...)
|
||||
println(colour.DarkYellow(text))
|
||||
}
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
@@ -53,13 +42,6 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
extensions := "go"
|
||||
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||
|
||||
// extensions to trigger rebuilds
|
||||
showWarnings := false
|
||||
command.BoolFlag("w", "Show warnings", &showWarnings)
|
||||
|
||||
loglevel := ""
|
||||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Create logger
|
||||
@@ -67,215 +49,262 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
app.PrintBanner()
|
||||
|
||||
// TODO: Check you are in a project directory
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
var debugBinaryProcess *process.Process = nil
|
||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
||||
|
||||
// Setup signal handler
|
||||
quitChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
|
||||
debounceQuit := make(chan bool, 1)
|
||||
|
||||
// Do initial build
|
||||
logger.Println("Building application for development...")
|
||||
newProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||
if newProcess != nil {
|
||||
debugBinaryProcess = newProcess
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
||||
// logger.Println("event: %+v", event)
|
||||
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
logger.Fatal("%s", err.Error())
|
||||
}
|
||||
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
if showWarnings {
|
||||
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// Try and build the app
|
||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
||||
if err != nil {
|
||||
fmt.Printf("Error during build: %s", err.Error())
|
||||
return
|
||||
}
|
||||
// If we have a new process, save it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
projectDir, err := os.Getwd()
|
||||
reloader, err := NewReloader(logger, extensionsThatTriggerARebuild, ldflags, compilerCommand)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(projectDir)
|
||||
// Start
|
||||
err = reloader.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
println("ERRRRRRRRRR: %+v", err)
|
||||
}
|
||||
|
||||
LogGreen("Watching (sub)/directory: %s", projectDir)
|
||||
logger.Println("\nDevelopment mode exited")
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
// Ignore build directory
|
||||
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
||||
return
|
||||
}
|
||||
err = watcher.Add(dir)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Wait until we get a quit signal
|
||||
quit := false
|
||||
for quit == false {
|
||||
select {
|
||||
case <-quitChannel:
|
||||
LogGreen("\nCaught quit")
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- true
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
err := debugBinaryProcess.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
LogGreen("Development mode exited")
|
||||
|
||||
return nil
|
||||
return err
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
|
||||
func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
|
||||
var item fsnotify.Event
|
||||
timer := time.NewTimer(interval)
|
||||
exit:
|
||||
type Reloader struct {
|
||||
|
||||
// Main context
|
||||
ctx context.Context
|
||||
|
||||
// Signal context
|
||||
signalContext context.Context
|
||||
|
||||
// notify
|
||||
watcher *fsnotify.Watcher
|
||||
|
||||
// Logger
|
||||
logger *clilogger.CLILogger
|
||||
|
||||
// Extensions to listen for
|
||||
extensionsThatTriggerARebuild []string
|
||||
|
||||
// The binary we are running
|
||||
binary *process.Process
|
||||
|
||||
// options
|
||||
ldflags string
|
||||
compiler string
|
||||
}
|
||||
|
||||
func NewReloader(logger *clilogger.CLILogger, extensionsThatTriggerARebuild []string, ldFlags string, compiler string) (*Reloader, error) {
|
||||
var result Reloader
|
||||
|
||||
// Create context
|
||||
result.ctx = context.Background()
|
||||
|
||||
// Signal context (we don't need cancel)
|
||||
signalContext, _ := signal.NotifyContext(result.ctx, os.Interrupt, os.Kill, syscall.SIGTERM)
|
||||
result.signalContext = signalContext
|
||||
|
||||
// Create watcher
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.watcher = watcher
|
||||
|
||||
// Logger
|
||||
result.logger = logger
|
||||
|
||||
// Extensions
|
||||
result.extensionsThatTriggerARebuild = extensionsThatTriggerARebuild
|
||||
|
||||
// Options
|
||||
result.ldflags = ldFlags
|
||||
result.compiler = compiler
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (r *Reloader) Start() error {
|
||||
|
||||
err := r.rebuildBinary()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
r.logger.Println("Watching (sub)directories: %s", dir)
|
||||
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
err = r.watcher.Add(dir)
|
||||
if err != nil {
|
||||
r.logger.Fatal(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
// Main loop
|
||||
for {
|
||||
select {
|
||||
case item = <-input:
|
||||
timer.Reset(interval)
|
||||
case <-timer.C:
|
||||
if item.Name != "" {
|
||||
cb(item)
|
||||
case <-r.signalContext.Done():
|
||||
if r.binary != nil {
|
||||
println("Binary is not nil - kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
return nil
|
||||
case event := <-r.watcher.Events:
|
||||
err := r.processWatcherEvent(event)
|
||||
if err != nil {
|
||||
println("error from processWatcherEvent. Calling cancel()")
|
||||
println("Calling kill")
|
||||
return r.binary.Kill()
|
||||
}
|
||||
case <-quitChannel:
|
||||
break exit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string) (*process.Process, error) {
|
||||
func (r *Reloader) processWatcherEvent(event fsnotify.Event) error {
|
||||
|
||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||
println()
|
||||
if err != nil {
|
||||
LogRed("Build error - continuing to run current version")
|
||||
LogDarkYellow(err.Error())
|
||||
return nil, nil
|
||||
// Check for new directories
|
||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
||||
// If this is a folder, add it to our watch list
|
||||
if fs.DirExists(event.Name) {
|
||||
if !strings.Contains(event.Name, "node_modules") {
|
||||
err := r.watcher.Add(event.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill existing binary if need be
|
||||
if debugBinaryProcess != nil {
|
||||
killError := debugBinaryProcess.Kill()
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
if killError != nil {
|
||||
logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
var rebuild bool
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range r.extensionsThatTriggerARebuild {
|
||||
if strings.HasSuffix(event.Name, pattern) {
|
||||
rebuild = true
|
||||
}
|
||||
}
|
||||
|
||||
debugBinaryProcess = nil
|
||||
if !rebuild {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.logger.Println("\n%s[Build triggered] %s %s", ctc.ForegroundGreen|ctc.ForegroundBright, event.Name, ctc.Reset)
|
||||
|
||||
return r.rebuildBinary()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reloader) rebuildBinary() error {
|
||||
|
||||
// rebuild binary
|
||||
binary, err := r.buildApp()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Generate `backend.js`
|
||||
// Kill current binary if running
|
||||
if r.binary != nil {
|
||||
err = r.binary.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start up new binary
|
||||
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
||||
newProcess := process.NewProcess(r.ctx, r.logger, binary)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
deleteError := fs.DeleteFile(appBinary)
|
||||
if deleteError != nil {
|
||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
}
|
||||
logger.Fatal("Unable to start application: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return newProcess, nil
|
||||
// Ensure process runs correctly
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
||||
//func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process) (*process.Process, error) {
|
||||
//
|
||||
// appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
||||
// println()
|
||||
// if err != nil {
|
||||
// logger.Fatal(err.Error())
|
||||
// return nil, errors.Wrap(err, "Build Failed:")
|
||||
// }
|
||||
// logger.Println("Build new binary: %s", appBinary)
|
||||
//
|
||||
// // Kill existing binary if need be
|
||||
// if debugBinaryProcess != nil {
|
||||
// killError := debugBinaryProcess.Kill()
|
||||
//
|
||||
// if killError != nil {
|
||||
// logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
||||
// }
|
||||
//
|
||||
// debugBinaryProcess = nil
|
||||
// }
|
||||
//
|
||||
// // TODO: Generate `backend.js`
|
||||
//
|
||||
// // Start up new binary
|
||||
// newProcess := process.NewProcess(logger, appBinary)
|
||||
// err = newProcess.Start()
|
||||
// if err != nil {
|
||||
// // Remove binary
|
||||
// deleteError := fs.DeleteFile(appBinary)
|
||||
// if deleteError != nil {
|
||||
// logger.Fatal("Unable to delete app binary: " + appBinary)
|
||||
// }
|
||||
// logger.Fatal("Unable to start application: %s", err.Error())
|
||||
// }
|
||||
//
|
||||
// // Check if port is open
|
||||
// timeout := time.Second
|
||||
// conn, err := net.DialTimeout("tcp", net.JoinHostPort("host", port), timeout)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// newProcess.Running
|
||||
// return newProcess, nil
|
||||
//}
|
||||
|
||||
// buildapp attempts to compile the application
|
||||
// It returns the path to the new binary or an error
|
||||
func (r *Reloader) buildApp() (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
||||
outputFile := fmt.Sprintf("debug-%d", time.Now().Unix())
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Logger: r.logger,
|
||||
OutputType: "dev",
|
||||
Mode: build.Debug,
|
||||
Pack: false,
|
||||
Platform: runtime.GOOS,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
LDFlags: r.ldflags,
|
||||
Compiler: r.compiler,
|
||||
OutputFile: outputFile,
|
||||
IgnoreFrontend: true,
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Exit early if PM not found
|
||||
if info.PM == nil {
|
||||
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
|
||||
w.Flush()
|
||||
println()
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `generate` command for the Wails application
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
||||
@@ -19,7 +19,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
// Quiet Init
|
||||
quiet := false
|
||||
backendAPI.BoolFlag("q", "Suppress output to console", &quiet)
|
||||
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
|
||||
|
||||
backendAPI.Action(func() error {
|
||||
|
||||
@@ -85,4 +85,7 @@ func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
||||
}
|
||||
}
|
||||
logger.Println("")
|
||||
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
"github.com/wailsapp/wails/v2/pkg/git"
|
||||
)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,39 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create application with options
|
||||
app := NewBasic()
|
||||
|
||||
err := wails.Run(&options.App{
|
||||
Title: "{{.ProjectName}}",
|
||||
Width: 800,
|
||||
Height: 600,
|
||||
DisableResize: true,
|
||||
Mac: &mac.Options{
|
||||
WebviewIsTransparent: true,
|
||||
WindowBackgroundIsTranslucent: true,
|
||||
TitleBar: mac.TitleBarHiddenInset(),
|
||||
Menu: menu.DefaultMacMenu(),
|
||||
},
|
||||
LogLevel: logger.DEBUG,
|
||||
Startup: app.startup,
|
||||
Shutdown: app.shutdown,
|
||||
Bind: []interface{}{
|
||||
app,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
templates, err := List()
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found these templates:")
|
||||
for _, template := range templates {
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortname(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
template, err := getTemplateByShortname("vanilla")
|
||||
is2.NoErr(err)
|
||||
|
||||
println("Found this template:")
|
||||
fmt.Printf("%+v\n", template)
|
||||
}
|
||||
|
||||
func TestInstall(t *testing.T) {
|
||||
|
||||
is2 := is.New(t)
|
||||
|
||||
options := &Options{
|
||||
ProjectName: "test",
|
||||
TemplateName: "vanilla",
|
||||
AuthorName: "Lea Anthony",
|
||||
AuthorEmail: "lea.anthony@gmail.com",
|
||||
}
|
||||
|
||||
err := Install(options)
|
||||
is2.NoErr(err)
|
||||
}
|
||||
@@ -18,14 +18,14 @@ import (
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
||||
|
||||
command := app.NewSubCommand("update", "Update the Wails CLI")
|
||||
command.LongDescription(`This command allows you to update your version of the Wails CLI.`)
|
||||
command.LongDescription(`This command allows you to update your version of Wails.`)
|
||||
|
||||
// Setup flags
|
||||
var prereleaseRequired bool
|
||||
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
||||
command.BoolFlag("pre", "Update to latest Prerelease", &prereleaseRequired)
|
||||
|
||||
var specificVersion string
|
||||
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
||||
command.StringFlag("version", "Install a specific version (Overrides other flags)", &specificVersion)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
@@ -143,7 +143,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
||||
logger.Print("Installing Wails " + desiredVersion + "...")
|
||||
|
||||
// Run command in non module directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
@@ -158,7 +158,7 @@ func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.Semantic
|
||||
return err
|
||||
}
|
||||
fmt.Println()
|
||||
logger.Println("Wails CLI updated to " + desiredVersion)
|
||||
logger.Println("Wails updated to " + desiredVersion)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/colour"
|
||||
"github.com/wzshiming/ctc"
|
||||
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
||||
|
||||
@@ -22,15 +22,27 @@ func fatal(message string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func banner(_ *clir.Cli) string {
|
||||
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
||||
func col(colour ctc.Color, text string) string {
|
||||
return fmt.Sprintf("%s%s%s", colour, text, ctc.Reset)
|
||||
}
|
||||
|
||||
func Yellow(str string) string {
|
||||
return col(ctc.ForegroundBrightYellow, str)
|
||||
}
|
||||
|
||||
func Red(str string) string {
|
||||
return col(ctc.ForegroundBrightRed, str)
|
||||
}
|
||||
|
||||
func banner(cli *clir.Cli) string {
|
||||
return fmt.Sprintf("%s %s - Go/HTML Application Framework", Yellow("Wails"), Red(version))
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var err error
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
app.SetBannerFunction(banner)
|
||||
|
||||
@@ -67,6 +79,5 @@ func main() {
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
println("\n\nERROR: " + err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v2.0.0-alpha.65"
|
||||
var version = "v2.0.0-alpha.26"
|
||||
|
||||
@@ -4,15 +4,14 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/jackmordaunt/icns v1.0.0
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/debme v1.1.1
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
||||
github.com/leaanthony/gosod v1.0.1
|
||||
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
|
||||
@@ -21,9 +20,10 @@ require (
|
||||
github.com/tdewolff/minify v2.3.6+incompatible
|
||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
||||
github.com/tdewolff/test v1.0.6 // indirect
|
||||
github.com/wzshiming/ctc v1.2.3
|
||||
github.com/wzshiming/ctc v1.2.3 // indirect
|
||||
github.com/xyproto/xpm v1.2.1
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
||||
nhooyr.io/websocket v1.8.6
|
||||
|
||||
11
v2/go.sum
11
v2/go.sum
@@ -1,6 +1,7 @@
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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=
|
||||
@@ -41,12 +42,8 @@ github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eT
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/debme v1.1.1 h1:2CQjJkfrjr4b2VgpDmkq2aghem5R2bNbg1Yg5cKQGBQ=
|
||||
github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
||||
github.com/leaanthony/gosod v1.0.1 h1:F+4c3DmEBfigi7oAswCV2RpQ+k4DcNbhuCZUGdBHacQ=
|
||||
github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU=
|
||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
@@ -99,6 +96,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
|
||||
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -20,10 +20,10 @@ func (a *App) Init() error {
|
||||
}
|
||||
|
||||
// Set log levels
|
||||
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
greeting := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
||||
flag.Parse()
|
||||
if len(*loglevel) > 0 {
|
||||
switch strings.ToLower(*loglevel) {
|
||||
if len(*greeting) > 0 {
|
||||
switch strings.ToLower(*greeting) {
|
||||
case "trace":
|
||||
a.logger.SetLogLevel(logger.TRACE)
|
||||
case "info":
|
||||
|
||||
@@ -35,7 +35,6 @@ type App struct {
|
||||
//binding *subsystem.Binding
|
||||
call *subsystem.Call
|
||||
menu *subsystem.Menu
|
||||
url *subsystem.URL
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
menuManager *menumanager.Manager
|
||||
@@ -118,6 +117,14 @@ func (a *App) Run() error {
|
||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||
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
|
||||
a.servicebus.Debug()
|
||||
err = a.servicebus.Start()
|
||||
@@ -125,7 +132,7 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -161,19 +168,6 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.options.Mac.URLHandlers != nil {
|
||||
// Start the url handler subsystem
|
||||
url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.url = url
|
||||
err = a.url.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start the eventing subsystem
|
||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
@@ -213,14 +207,6 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup signal handler
|
||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signalsubsystem
|
||||
a.signal.Start()
|
||||
|
||||
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
if err != nil {
|
||||
@@ -245,10 +231,5 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown callback
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(appoptions.Logger)
|
||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
||||
|
||||
// Create the menu manager
|
||||
menuManager := menumanager.NewManager()
|
||||
@@ -118,6 +119,14 @@ func (a *App) Run() error {
|
||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
||||
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
|
||||
a.servicebus.Debug()
|
||||
err = a.servicebus.Start()
|
||||
@@ -125,7 +134,7 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -203,14 +212,6 @@ func (a *App) Run() error {
|
||||
// Generate backend.js
|
||||
a.bindings.GenerateBackendJS()
|
||||
|
||||
// Setup signal handler
|
||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signalsubsystem
|
||||
a.signal.Start()
|
||||
|
||||
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
||||
a.logger.Trace("Bridge.Run() exited")
|
||||
if err != nil {
|
||||
@@ -235,10 +236,6 @@ func (a *App) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown callback
|
||||
if a.shutdownCallback != nil {
|
||||
a.shutdownCallback()
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,30 @@ import (
|
||||
"github.com/leaanthony/slicer"
|
||||
)
|
||||
|
||||
const _comment = `
|
||||
|
||||
const backend = {
|
||||
main: {
|
||||
"xbarApp": {
|
||||
"GetCategories": () => {
|
||||
window.backend.main.xbarApp.GetCategories.call(arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} arg1
|
||||
*/
|
||||
"InstallPlugin": (arg1) => {
|
||||
window.backend.main.xbarApp.InstallPlugin.call(arguments);
|
||||
},
|
||||
"GetPlugins": () => {
|
||||
window.backend.main.xbarApp.GetPlugins.call(arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default backend;`
|
||||
|
||||
//go:embed assets/package.json
|
||||
var packageJSON []byte
|
||||
|
||||
@@ -34,7 +58,7 @@ const backend = {`)
|
||||
sortedPackageNames.Add(packageName)
|
||||
}
|
||||
sortedPackageNames.Sort()
|
||||
sortedPackageNames.Each(func(packageName string) {
|
||||
for _, packageName := range sortedPackageNames.AsSlice() {
|
||||
packages := store[packageName]
|
||||
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
||||
output.WriteString("\n")
|
||||
@@ -43,8 +67,7 @@ const backend = {`)
|
||||
sortedStructNames.Add(structName)
|
||||
}
|
||||
sortedStructNames.Sort()
|
||||
|
||||
sortedStructNames.Each(func(structName string) {
|
||||
for _, structName := range sortedStructNames.AsSlice() {
|
||||
structs := packages[structName]
|
||||
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
||||
output.WriteString("\n")
|
||||
@@ -55,7 +78,7 @@ const backend = {`)
|
||||
}
|
||||
sortedMethodNames.Sort()
|
||||
|
||||
sortedMethodNames.Each(func(methodName string) {
|
||||
for _, methodName := range sortedMethodNames.AsSlice() {
|
||||
methodDetails := structs[methodName]
|
||||
output.WriteString(" /**\n")
|
||||
output.WriteString(" * " + methodName + "\n")
|
||||
@@ -76,8 +99,6 @@ const backend = {`)
|
||||
}
|
||||
returnType += ">"
|
||||
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
||||
} else {
|
||||
returnType = "Promise<void>"
|
||||
}
|
||||
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
||||
output.WriteString(" */\n")
|
||||
@@ -88,29 +109,25 @@ const backend = {`)
|
||||
output.WriteString("\n")
|
||||
output.WriteString(fmt.Sprintf(" },"))
|
||||
output.WriteString("\n")
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
output.WriteString(fmt.Sprintf(" }"))
|
||||
output.WriteString("\n")
|
||||
})
|
||||
|
||||
}
|
||||
output.WriteString(fmt.Sprintf(" }\n"))
|
||||
output.WriteString("\n")
|
||||
})
|
||||
}
|
||||
|
||||
output.WriteString(`};
|
||||
export default backend;`)
|
||||
output.WriteString("\n")
|
||||
|
||||
// TODO: Make this configurable in wails.json
|
||||
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if !fs.DirExists(dirname) {
|
||||
err := fs.MkDirs(dirname)
|
||||
err := fs.Mkdir(dirname)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -11,10 +11,6 @@ type BridgeClient struct {
|
||||
messageCache chan string
|
||||
}
|
||||
|
||||
func (b BridgeClient) DeleteTrayMenuByID(id string) {
|
||||
b.session.sendMessage("TD" + id)
|
||||
}
|
||||
|
||||
func NewBridgeClient() *BridgeClient {
|
||||
return &BridgeClient{
|
||||
messageCache: make(chan string, 100),
|
||||
|
||||
@@ -19,9 +19,6 @@ type DialogClient struct {
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func (d *DialogClient) DeleteTrayMenuByID(id string) {
|
||||
}
|
||||
|
||||
func NewDialogClient(log *logger.Logger) *DialogClient {
|
||||
return &DialogClient{
|
||||
log: log,
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
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()
|
||||
}
|
||||
@@ -5,6 +5,10 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#define OBJC_OLD_DISPATCH_PROTOTYPES 1
|
||||
#include <objc/objc-runtime.h>
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include "string.h"
|
||||
|
||||
@@ -82,10 +82,10 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
||||
FREE_AND_SET(contextMenu->contextMenuData, contextMenuData);
|
||||
|
||||
// Grab the content view and show the menu
|
||||
id contentView = msg_reg(mainWindow, s("contentView"));
|
||||
id contentView = msg(mainWindow, s("contentView"));
|
||||
|
||||
// Get the triggering event
|
||||
id menuEvent = msg_reg(mainWindow, s("currentEvent"));
|
||||
id menuEvent = msg(mainWindow, s("currentEvent"));
|
||||
|
||||
if( contextMenu->nsmenu == NULL ) {
|
||||
// GetMenu creates the NSMenu
|
||||
@@ -93,7 +93,7 @@ void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *context
|
||||
}
|
||||
|
||||
// Show popup
|
||||
((id(*)(id, SEL, id, id, id))objc_msgSend)(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
||||
msg(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ extern void DarkModeEnabled(struct Application*, char *callbackID);
|
||||
extern void SetApplicationMenu(struct Application*, const char *);
|
||||
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
||||
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
||||
extern void DeleteTrayMenuByID(struct Application*, const char *id);
|
||||
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
||||
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
||||
|
||||
@@ -208,7 +208,3 @@ func (c *Client) UpdateTrayMenuLabel(JSON string) {
|
||||
func (c *Client) UpdateContextMenu(contextMenuJSON string) {
|
||||
C.UpdateContextMenu(c.app.app, c.app.string2CString(contextMenuJSON))
|
||||
}
|
||||
|
||||
func (c *Client) DeleteTrayMenuByID(id string) {
|
||||
C.DeleteTrayMenuByID(c.app.app, c.app.string2CString(id))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,9 +48,6 @@ func (a *Application) processPlatformSettings() error {
|
||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||
}
|
||||
|
||||
// Set activation policy
|
||||
C.SetActivationPolicy(a.app, C.int(mac.ActivationPolicy))
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
@@ -90,10 +87,5 @@ func (a *Application) processPlatformSettings() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Process URL Handlers
|
||||
if a.config.Mac.URLHandlers != nil {
|
||||
C.HasURLHandlers(a.app)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,51 +11,24 @@
|
||||
#include "hashmap.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
typedef struct {
|
||||
long maj;
|
||||
long min;
|
||||
long patch;
|
||||
} OSVersion;
|
||||
|
||||
// Macros to make it slightly more sane
|
||||
#define msg objc_msgSend
|
||||
#define msg_reg ((id(*)(id, SEL))objc_msgSend)
|
||||
#define msg_id ((id(*)(id, SEL, id))objc_msgSend)
|
||||
#define msg_id_id ((id(*)(id, SEL, id, id))objc_msgSend)
|
||||
#define msg_bool ((id(*)(id, SEL, BOOL))objc_msgSend)
|
||||
#define msg_int ((id(*)(id, SEL, int))objc_msgSend)
|
||||
#define msg_uint ((id(*)(id, SEL, unsigned int))objc_msgSend)
|
||||
#define msg_float ((id(*)(id, SEL, float))objc_msgSend)
|
||||
#define kInternetEventClass 'GURL'
|
||||
#define kAEGetURL 'GURL'
|
||||
#define keyDirectObject '----'
|
||||
|
||||
#define c(str) (id)objc_getClass(str)
|
||||
#define s(str) sel_registerName(str)
|
||||
#define u(str) sel_getUid(str)
|
||||
#define str(input) ((id(*)(id, SEL, const char *))objc_msgSend)(c("NSString"), s("stringWithUTF8String:"), input)
|
||||
#define strunicode(input) ((id(*)(id, SEL, id, unsigned short))objc_msgSend)(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
||||
#define cstr(input) (const char *)msg_reg(input, s("UTF8String"))
|
||||
#define url(input) msg_id(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||
#define ALLOC(classname) msg_reg(c(classname), s("alloc"))
|
||||
#define ALLOC_INIT(classname) msg_reg(msg_reg(c(classname), s("alloc")), s("init"))
|
||||
|
||||
#if defined (__aarch64__)
|
||||
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("frame"))
|
||||
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("bounds"))
|
||||
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend)(processInfo, s("operatingSystemVersion"));
|
||||
#endif
|
||||
|
||||
#if defined (__x86_64__)
|
||||
#define str(input) msg(c("NSString"), s("stringWithUTF8String:"), input)
|
||||
#define strunicode(input) msg(c("NSString"), s("stringWithFormat:"), str("%C"), (unsigned short)input)
|
||||
#define cstr(input) (const char *)msg(input, s("UTF8String"))
|
||||
#define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input))
|
||||
#define ALLOC(classname) msg(c(classname), s("alloc"))
|
||||
#define ALLOC_INIT(classname) msg(msg(c(classname), s("alloc")), s("init"))
|
||||
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
|
||||
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds"))
|
||||
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend_stret)(processInfo, s("operatingSystemVersion"));
|
||||
#endif
|
||||
|
||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))
|
||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))msg)(receiver, s("backingScaleFactor"))
|
||||
|
||||
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
|
||||
#define MAIN_WINDOW_CALL(str) msg_reg(app->mainWindow, s((str)))
|
||||
#define MAIN_WINDOW_CALL(str) msg(app->mainWindow, s((str)))
|
||||
|
||||
#define NSBackingStoreBuffered 2
|
||||
|
||||
@@ -93,10 +66,6 @@ typedef struct {
|
||||
#define NSControlStateValueOff 0
|
||||
#define NSControlStateValueOn 1
|
||||
|
||||
#define NSApplicationActivationPolicyRegular 0
|
||||
#define NSApplicationActivationPolicyAccessory 1
|
||||
#define NSApplicationActivationPolicyProhibited 2
|
||||
|
||||
// Unbelievably, if the user swaps their button preference
|
||||
// then right buttons are reported as left buttons
|
||||
#define NSEventMaskLeftMouseDown 1 << 1
|
||||
@@ -125,8 +94,6 @@ typedef struct {
|
||||
#define NSAlertSecondButtonReturn 1001
|
||||
#define NSAlertThirdButtonReturn 1002
|
||||
|
||||
#define BrokenImage "iVBORw0KGgoAAAANSUhEUgAAABAAAAASCAMAAABl5a5YAAABj1BMVEWopan///+koqSWk5P9/v3///////////+AgACMiovz8/PB0fG9z+3i4+WysbGBfX1Erh80rACLiYqBxolEsDhHlDEbqQDDx+CNho7W1tj4+/bw+O3P5Mn4/f/W1tbK6sX////b2dn////////////8/Pz6+vro6Ojj4+P////G1PL////EzNydmp2cmZnd3eDF1PHs8v/o8P/Q3vrS3vfE0vCdmpqZkpr19/3N2vXI1vPH1fOgnqDg6frP3PbCytvHx8irqq6HhIZtuGtjnlZetU1Xs0NWskBNsi7w9v/d6P7w9P3S4Pzr8Pvl7PrY5PrU4PjQ3fjD1Ozo6Om30NjGzNi7ubm34K+UxKmbnaWXlJeUjpSPi4tppF1TtjxSsTf2+f7L2PTr7e3H2+3V7+q+0uXg4OPg4eLR1uG7z+Hg4ODGzODV2N7V1trP5dmxzs65vcfFxMWq0cKxxr+/vr+0s7apxbWaxrCv2qao05+dlp2Uuo2Dn4F8vIB6xnyAoHmAym9zqGpctENLryNFsgoblJpnAAAAKnRSTlP+hP7+5ZRmYgL+/f39/f39/f38/Pz8/Pv69+7j083My8GocnBPTTMWEgjxeITOAAABEklEQVQY0y3KZXuCYBiG4ceYuu7u3nyVAaKOMBBQ7O5Yd3f3fvheDnd9u8/jBkGwNxP6sjOWVQvY/ftrzfT6bd3yEhCnYZqiaYoKiwX/gXkFiHySTcUTLJMsZ9v8nQvgssWYOEKedKpcOO6CUXD5IlGEY5hLUbyDAAZ6HRf1bnkoavOsFQibg+Q4nuNYL+ON5PHD5nBaraRVyxnzGf6BJzUi2QQCQgMyk8tleL7dg1owpJ17D5IkvV100EingeOopPyo6vfAuXF+9hbDTknZCIaUoeK4efKwG4iT6xDewd7imGlid7gGwv37b6Oh9jwaTdOf/Tc1qH7UZVmuP6G5qZfBr9cAGNy4KiDd4tXIs7tS+QO9aUKvPAIKuQAAAABJRU5ErkJggg=="
|
||||
|
||||
struct Application;
|
||||
int releaseNSObject(void *const context, struct hashmap_element_s *const e);
|
||||
void TitlebarAppearsTransparent(struct Application* app);
|
||||
@@ -143,12 +110,6 @@ void SetTray(struct Application* app, const char *, const char *, const char *);
|
||||
//void SetContextMenus(struct Application* app, const char *);
|
||||
void AddTrayMenu(struct Application* app, const char *);
|
||||
|
||||
void SetActivationPolicy(struct Application* app, int policy);
|
||||
|
||||
void* lookupStringConstant(id constantName);
|
||||
|
||||
void HasURLHandlers(struct Application* app);
|
||||
|
||||
id createImageFromBase64Data(const char *data, bool isTemplateImage);
|
||||
|
||||
#endif
|
||||
@@ -1,78 +0,0 @@
|
||||
|
||||
typedef struct {
|
||||
} Application;
|
||||
|
||||
struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose) {
|
||||
}
|
||||
void SetMinWindowSize(struct Application* app, int minWidth, int minHeight) {
|
||||
}
|
||||
void SetMaxWindowSize(struct Application* app, int maxWidth, int maxHeight) {
|
||||
}
|
||||
void Run(struct Application* app, int argc, char **argv) {
|
||||
}
|
||||
void DestroyApplication(struct Application* app) {
|
||||
}
|
||||
void SetDebug(struct Application* app, int flag) {
|
||||
}
|
||||
void SetBindings(struct Application* app, const char *bindings) {
|
||||
}
|
||||
void ExecJS(struct Application* app, const char *script) {
|
||||
}
|
||||
void Hide(struct Application* app) {
|
||||
}
|
||||
void Show(struct Application* app) {
|
||||
}
|
||||
void Center(struct Application* app) {
|
||||
}
|
||||
void Maximise(struct Application* app) {
|
||||
}
|
||||
void Unmaximise(struct Application* app) {
|
||||
}
|
||||
void ToggleMaximise(struct Application* app) {
|
||||
}
|
||||
void Minimise(struct Application* app) {
|
||||
}
|
||||
void Unminimise(struct Application* app) {
|
||||
}
|
||||
void ToggleMinimise(struct Application* app) {
|
||||
}
|
||||
void SetColour(struct Application* app, int red, int green, int blue, int alpha) {
|
||||
}
|
||||
void SetSize(struct Application* app, int width, int height) {
|
||||
}
|
||||
void SetPosition(struct Application* app, int x, int y) {
|
||||
}
|
||||
void Quit(struct Application* app) {
|
||||
}
|
||||
void SetTitle(struct Application* app, const char *title) {
|
||||
}
|
||||
void Fullscreen(struct Application* app) {
|
||||
}
|
||||
void UnFullscreen(struct Application* app) {
|
||||
}
|
||||
void ToggleFullscreen(struct Application* app) {
|
||||
}
|
||||
void DisableFrame(struct Application* app) {
|
||||
}
|
||||
void OpenDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) {
|
||||
}
|
||||
void SaveDialog(struct Application* app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories) {
|
||||
}
|
||||
void MessageDialog(struct Application* app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
||||
}
|
||||
void DarkModeEnabled(struct Application* app, char *callbackID) {
|
||||
}
|
||||
void SetApplicationMenu(struct Application* app, const char *applicationMenuJSON) {
|
||||
}
|
||||
void AddTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
||||
}
|
||||
void SetTrayMenu(struct Application* app, const char *menuTrayJSON) {
|
||||
}
|
||||
void DeleteTrayMenuByID(struct Application* app, const char *id) {
|
||||
}
|
||||
void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
||||
}
|
||||
void AddContextMenu(struct Application* app, char *contextMenuJSON) {
|
||||
}
|
||||
void UpdateContextMenu(struct Application* app, char *contextMenuJSON) {
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
|
||||
#include "ffenestri.h"
|
||||
#include "ffenestri_windows.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
#ifndef _FFENESTRI_WINDOWS_
|
||||
#define _FFENESTRI_WINDOWS_
|
||||
|
||||
#endif
|
||||
@@ -90,7 +90,7 @@ void DeleteMenu(Menu *menu) {
|
||||
|
||||
// Free nsmenu if we have it
|
||||
if ( menu->menu != NULL ) {
|
||||
msg_reg(menu->menu, s("release"));
|
||||
msg(menu->menu, s("release"));
|
||||
}
|
||||
|
||||
free(menu);
|
||||
@@ -120,17 +120,17 @@ const char* createMenuClickedMessage(const char *menuItemID, const char *data, e
|
||||
|
||||
// Callback for text menu items
|
||||
void menuItemCallback(id self, SEL cmd, id sender) {
|
||||
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg_reg(msg_reg(sender, s("representedObject")), s("pointerValue"));
|
||||
MenuItemCallbackData *callbackData = (MenuItemCallbackData *)msg(msg(sender, s("representedObject")), s("pointerValue"));
|
||||
const char *message;
|
||||
|
||||
// Update checkbox / radio item
|
||||
if( callbackData->menuItemType == Checkbox) {
|
||||
// Toggle state
|
||||
bool state = msg_reg(callbackData->menuItem, s("state"));
|
||||
msg_int(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
||||
bool state = msg(callbackData->menuItem, s("state"));
|
||||
msg(callbackData->menuItem, s("setState:"), (state? NSControlStateValueOff : NSControlStateValueOn));
|
||||
} else if( callbackData->menuItemType == Radio ) {
|
||||
// Check the menu items' current state
|
||||
bool selected = (bool)msg_reg(callbackData->menuItem, s("state"));
|
||||
bool selected = msg(callbackData->menuItem, s("state"));
|
||||
|
||||
// If it's already selected, exit early
|
||||
if (selected) return;
|
||||
@@ -142,13 +142,13 @@ void menuItemCallback(id self, SEL cmd, id sender) {
|
||||
id thisMember = members[0];
|
||||
int count = 0;
|
||||
while(thisMember != NULL) {
|
||||
msg_int(thisMember, s("setState:"), NSControlStateValueOff);
|
||||
msg(thisMember, s("setState:"), NSControlStateValueOff);
|
||||
count = count + 1;
|
||||
thisMember = members[count];
|
||||
}
|
||||
|
||||
// check the selected menu item
|
||||
msg_int(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
||||
msg(callbackData->menuItem, s("setState:"), NSControlStateValueOn);
|
||||
}
|
||||
|
||||
const char *menuID = callbackData->menuID;
|
||||
@@ -180,154 +180,151 @@ id processAcceleratorKey(const char *key) {
|
||||
return str("");
|
||||
}
|
||||
|
||||
if( STREQ(key, "backspace") ) {
|
||||
if( STREQ(key, "Backspace") ) {
|
||||
return strunicode(0x0008);
|
||||
}
|
||||
if( STREQ(key, "tab") ) {
|
||||
if( STREQ(key, "Tab") ) {
|
||||
return strunicode(0x0009);
|
||||
}
|
||||
if( STREQ(key, "return") ) {
|
||||
if( STREQ(key, "Return") ) {
|
||||
return strunicode(0x000d);
|
||||
}
|
||||
if( STREQ(key, "enter") ) {
|
||||
return strunicode(0x000d);
|
||||
}
|
||||
if( STREQ(key, "escape") ) {
|
||||
if( STREQ(key, "Escape") ) {
|
||||
return strunicode(0x001b);
|
||||
}
|
||||
if( STREQ(key, "left") ) {
|
||||
if( STREQ(key, "Left") ) {
|
||||
return strunicode(0x001c);
|
||||
}
|
||||
if( STREQ(key, "right") ) {
|
||||
if( STREQ(key, "Right") ) {
|
||||
return strunicode(0x001d);
|
||||
}
|
||||
if( STREQ(key, "up") ) {
|
||||
if( STREQ(key, "Up") ) {
|
||||
return strunicode(0x001e);
|
||||
}
|
||||
if( STREQ(key, "down") ) {
|
||||
if( STREQ(key, "Down") ) {
|
||||
return strunicode(0x001f);
|
||||
}
|
||||
if( STREQ(key, "space") ) {
|
||||
if( STREQ(key, "Space") ) {
|
||||
return strunicode(0x0020);
|
||||
}
|
||||
if( STREQ(key, "delete") ) {
|
||||
if( STREQ(key, "Delete") ) {
|
||||
return strunicode(0x007f);
|
||||
}
|
||||
if( STREQ(key, "home") ) {
|
||||
if( STREQ(key, "Home") ) {
|
||||
return strunicode(0x2196);
|
||||
}
|
||||
if( STREQ(key, "end") ) {
|
||||
if( STREQ(key, "End") ) {
|
||||
return strunicode(0x2198);
|
||||
}
|
||||
if( STREQ(key, "page up") ) {
|
||||
if( STREQ(key, "Page Up") ) {
|
||||
return strunicode(0x21de);
|
||||
}
|
||||
if( STREQ(key, "page down") ) {
|
||||
if( STREQ(key, "Page Down") ) {
|
||||
return strunicode(0x21df);
|
||||
}
|
||||
if( STREQ(key, "f1") ) {
|
||||
if( STREQ(key, "F1") ) {
|
||||
return strunicode(0xf704);
|
||||
}
|
||||
if( STREQ(key, "f2") ) {
|
||||
if( STREQ(key, "F2") ) {
|
||||
return strunicode(0xf705);
|
||||
}
|
||||
if( STREQ(key, "f3") ) {
|
||||
if( STREQ(key, "F3") ) {
|
||||
return strunicode(0xf706);
|
||||
}
|
||||
if( STREQ(key, "f4") ) {
|
||||
if( STREQ(key, "F4") ) {
|
||||
return strunicode(0xf707);
|
||||
}
|
||||
if( STREQ(key, "f5") ) {
|
||||
if( STREQ(key, "F5") ) {
|
||||
return strunicode(0xf708);
|
||||
}
|
||||
if( STREQ(key, "f6") ) {
|
||||
if( STREQ(key, "F6") ) {
|
||||
return strunicode(0xf709);
|
||||
}
|
||||
if( STREQ(key, "f7") ) {
|
||||
if( STREQ(key, "F7") ) {
|
||||
return strunicode(0xf70a);
|
||||
}
|
||||
if( STREQ(key, "f8") ) {
|
||||
if( STREQ(key, "F8") ) {
|
||||
return strunicode(0xf70b);
|
||||
}
|
||||
if( STREQ(key, "f9") ) {
|
||||
if( STREQ(key, "F9") ) {
|
||||
return strunicode(0xf70c);
|
||||
}
|
||||
if( STREQ(key, "f10") ) {
|
||||
if( STREQ(key, "F10") ) {
|
||||
return strunicode(0xf70d);
|
||||
}
|
||||
if( STREQ(key, "f11") ) {
|
||||
if( STREQ(key, "F11") ) {
|
||||
return strunicode(0xf70e);
|
||||
}
|
||||
if( STREQ(key, "f12") ) {
|
||||
if( STREQ(key, "F12") ) {
|
||||
return strunicode(0xf70f);
|
||||
}
|
||||
if( STREQ(key, "f13") ) {
|
||||
if( STREQ(key, "F13") ) {
|
||||
return strunicode(0xf710);
|
||||
}
|
||||
if( STREQ(key, "f14") ) {
|
||||
if( STREQ(key, "F14") ) {
|
||||
return strunicode(0xf711);
|
||||
}
|
||||
if( STREQ(key, "f15") ) {
|
||||
if( STREQ(key, "F15") ) {
|
||||
return strunicode(0xf712);
|
||||
}
|
||||
if( STREQ(key, "f16") ) {
|
||||
if( STREQ(key, "F16") ) {
|
||||
return strunicode(0xf713);
|
||||
}
|
||||
if( STREQ(key, "f17") ) {
|
||||
if( STREQ(key, "F17") ) {
|
||||
return strunicode(0xf714);
|
||||
}
|
||||
if( STREQ(key, "f18") ) {
|
||||
if( STREQ(key, "F18") ) {
|
||||
return strunicode(0xf715);
|
||||
}
|
||||
if( STREQ(key, "f19") ) {
|
||||
if( STREQ(key, "F19") ) {
|
||||
return strunicode(0xf716);
|
||||
}
|
||||
if( STREQ(key, "f20") ) {
|
||||
if( STREQ(key, "F20") ) {
|
||||
return strunicode(0xf717);
|
||||
}
|
||||
if( STREQ(key, "f21") ) {
|
||||
if( STREQ(key, "F21") ) {
|
||||
return strunicode(0xf718);
|
||||
}
|
||||
if( STREQ(key, "f22") ) {
|
||||
if( STREQ(key, "F22") ) {
|
||||
return strunicode(0xf719);
|
||||
}
|
||||
if( STREQ(key, "f23") ) {
|
||||
if( STREQ(key, "F23") ) {
|
||||
return strunicode(0xf71a);
|
||||
}
|
||||
if( STREQ(key, "f24") ) {
|
||||
if( STREQ(key, "F24") ) {
|
||||
return strunicode(0xf71b);
|
||||
}
|
||||
if( STREQ(key, "f25") ) {
|
||||
if( STREQ(key, "F25") ) {
|
||||
return strunicode(0xf71c);
|
||||
}
|
||||
if( STREQ(key, "f26") ) {
|
||||
if( STREQ(key, "F26") ) {
|
||||
return strunicode(0xf71d);
|
||||
}
|
||||
if( STREQ(key, "f27") ) {
|
||||
if( STREQ(key, "F27") ) {
|
||||
return strunicode(0xf71e);
|
||||
}
|
||||
if( STREQ(key, "f28") ) {
|
||||
if( STREQ(key, "F28") ) {
|
||||
return strunicode(0xf71f);
|
||||
}
|
||||
if( STREQ(key, "f29") ) {
|
||||
if( STREQ(key, "F29") ) {
|
||||
return strunicode(0xf720);
|
||||
}
|
||||
if( STREQ(key, "f30") ) {
|
||||
if( STREQ(key, "F30") ) {
|
||||
return strunicode(0xf721);
|
||||
}
|
||||
if( STREQ(key, "f31") ) {
|
||||
if( STREQ(key, "F31") ) {
|
||||
return strunicode(0xf722);
|
||||
}
|
||||
if( STREQ(key, "f32") ) {
|
||||
if( STREQ(key, "F32") ) {
|
||||
return strunicode(0xf723);
|
||||
}
|
||||
if( STREQ(key, "f33") ) {
|
||||
if( STREQ(key, "F33") ) {
|
||||
return strunicode(0xf724);
|
||||
}
|
||||
if( STREQ(key, "f34") ) {
|
||||
if( STREQ(key, "F34") ) {
|
||||
return strunicode(0xf725);
|
||||
}
|
||||
if( STREQ(key, "f35") ) {
|
||||
if( STREQ(key, "F35") ) {
|
||||
return strunicode(0xf726);
|
||||
}
|
||||
// if( STREQ(key, "Insert") ) {
|
||||
@@ -339,7 +336,7 @@ id processAcceleratorKey(const char *key) {
|
||||
// if( STREQ(key, "ScrollLock") ) {
|
||||
// return strunicode(0xf72f);
|
||||
// }
|
||||
if( STREQ(key, "numLock") ) {
|
||||
if( STREQ(key, "NumLock") ) {
|
||||
return strunicode(0xf739);
|
||||
}
|
||||
|
||||
@@ -348,61 +345,61 @@ id processAcceleratorKey(const char *key) {
|
||||
|
||||
|
||||
void addSeparator(id menu) {
|
||||
id item = msg_reg(c("NSMenuItem"), s("separatorItem"));
|
||||
msg_id(menu, s("addItem:"), item);
|
||||
id item = msg(c("NSMenuItem"), s("separatorItem"));
|
||||
msg(menu, s("addItem:"), item);
|
||||
}
|
||||
|
||||
id createMenuItemNoAutorelease( id title, const char *action, const char *key) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
return item;
|
||||
}
|
||||
|
||||
id createMenuItem(id title, const char *action, const char *key) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), title, s(action), str(key));
|
||||
msg(item, s("autorelease"));
|
||||
return item;
|
||||
}
|
||||
|
||||
id addMenuItem(id menu, const char *title, const char *action, const char *key, bool disabled) {
|
||||
id item = createMenuItem(str(title), action, key);
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_id(menu, s("addItem:"), item);
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(menu, s("addItem:"), item);
|
||||
return item;
|
||||
}
|
||||
|
||||
id createMenu(id title) {
|
||||
id menu = ALLOC("NSMenu");
|
||||
msg_id(menu, s("initWithTitle:"), title);
|
||||
msg_bool(menu, s("setAutoenablesItems:"), NO);
|
||||
msg(menu, s("initWithTitle:"), title);
|
||||
msg(menu, s("setAutoenablesItems:"), NO);
|
||||
// msg(menu, s("autorelease"));
|
||||
return menu;
|
||||
}
|
||||
|
||||
void createDefaultAppMenu(id parentMenu) {
|
||||
// App Menu
|
||||
id appName = msg_reg(msg_reg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
||||
id appName = msg(msg(c("NSProcessInfo"), s("processInfo")), s("processName"));
|
||||
id appMenuItem = createMenuItemNoAutorelease(appName, NULL, "");
|
||||
id appMenu = createMenu(appName);
|
||||
|
||||
msg_id(appMenuItem, s("setSubmenu:"), appMenu);
|
||||
msg_id(parentMenu, s("addItem:"), appMenuItem);
|
||||
msg(appMenuItem, s("setSubmenu:"), appMenu);
|
||||
msg(parentMenu, s("addItem:"), appMenuItem);
|
||||
|
||||
id title = msg_id(str("Hide "), s("stringByAppendingString:"), appName);
|
||||
id title = msg(str("Hide "), s("stringByAppendingString:"), appName);
|
||||
id item = createMenuItem(title, "hide:", "h");
|
||||
msg_id(appMenu, s("addItem:"), item);
|
||||
msg(appMenu, s("addItem:"), item);
|
||||
|
||||
id hideOthers = addMenuItem(appMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
|
||||
addMenuItem(appMenu, "Show All", "unhideAllApplications:", "", FALSE);
|
||||
|
||||
addSeparator(appMenu);
|
||||
|
||||
title = msg_id(str("Quit "), s("stringByAppendingString:"), appName);
|
||||
title = msg(str("Quit "), s("stringByAppendingString:"), appName);
|
||||
item = createMenuItem(title, "terminate:", "q");
|
||||
msg_id(appMenu, s("addItem:"), item);
|
||||
msg(appMenu, s("addItem:"), item);
|
||||
}
|
||||
|
||||
void createDefaultEditMenu(id parentMenu) {
|
||||
@@ -410,8 +407,8 @@ void createDefaultEditMenu(id parentMenu) {
|
||||
id editMenuItem = createMenuItemNoAutorelease(str("Edit"), NULL, "");
|
||||
id editMenu = createMenu(str("Edit"));
|
||||
|
||||
msg_id(editMenuItem, s("setSubmenu:"), editMenu);
|
||||
msg_id(parentMenu, s("addItem:"), editMenuItem);
|
||||
msg(editMenuItem, s("setSubmenu:"), editMenu);
|
||||
msg(parentMenu, s("addItem:"), editMenuItem);
|
||||
|
||||
addMenuItem(editMenu, "Undo", "undo:", "z", FALSE);
|
||||
addMenuItem(editMenu, "Redo", "redo:", "y", FALSE);
|
||||
@@ -439,7 +436,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
}
|
||||
if ( STREQ(roleName, "hideothers")) {
|
||||
id hideOthers = addMenuItem(parentMenu, "Hide Others", "hideOtherApplications:", "h", FALSE);
|
||||
msg_int(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
msg(hideOthers, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagCommand));
|
||||
return;
|
||||
}
|
||||
if ( STREQ(roleName, "unhide")) {
|
||||
@@ -476,7 +473,7 @@ void processMenuRole(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
}
|
||||
if( STREQ(roleName, "pasteandmatchstyle")) {
|
||||
id pasteandmatchstyle = addMenuItem(parentMenu, "Paste and Match Style", "pasteandmatchstyle:", "v", FALSE);
|
||||
msg_int(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
||||
msg(pasteandmatchstyle, s("setKeyEquivalentModifierMask:"), (NSEventModifierFlagOption | NSEventModifierFlagShift | NSEventModifierFlagCommand));
|
||||
}
|
||||
if ( STREQ(roleName, "selectall")) {
|
||||
addMenuItem(parentMenu, "Select All", "selectAll:", "a", FALSE);
|
||||
@@ -511,21 +508,20 @@ unsigned long parseModifiers(const char **modifiers) {
|
||||
const char *thisModifier = modifiers[0];
|
||||
int count = 0;
|
||||
while( thisModifier != NULL ) {
|
||||
|
||||
// Determine flags
|
||||
if( STREQ(thisModifier, "cmdorctrl") ) {
|
||||
if( STREQ(thisModifier, "CmdOrCtrl") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "optionoralt") ) {
|
||||
if( STREQ(thisModifier, "OptionOrAlt") ) {
|
||||
result |= NSEventModifierFlagOption;
|
||||
}
|
||||
if( STREQ(thisModifier, "shift") ) {
|
||||
if( STREQ(thisModifier, "Shift") ) {
|
||||
result |= NSEventModifierFlagShift;
|
||||
}
|
||||
if( STREQ(thisModifier, "super") ) {
|
||||
if( STREQ(thisModifier, "Super") ) {
|
||||
result |= NSEventModifierFlagCommand;
|
||||
}
|
||||
if( STREQ(thisModifier, "ctrl") ) {
|
||||
if( STREQ(thisModifier, "Control") ) {
|
||||
result |= NSEventModifierFlagControl;
|
||||
}
|
||||
count++;
|
||||
@@ -543,18 +539,18 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
||||
// Create a MenuItemCallbackData
|
||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Radio);
|
||||
|
||||
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
|
||||
id key = processAcceleratorKey(acceleratorkey);
|
||||
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), key);
|
||||
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
|
||||
msg_id(parentmenu, s("addItem:"), item);
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
|
||||
}
|
||||
@@ -569,136 +565,73 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
||||
// Create a MenuItemCallbackData
|
||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Checkbox);
|
||||
|
||||
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg_int(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg_id(parentmenu, s("addItem:"), item);
|
||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(key));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
msg(item, s("setState:"), (checked ? NSControlStateValueOn : NSControlStateValueOff));
|
||||
msg(parentmenu, s("addItem:"), item);
|
||||
return item;
|
||||
}
|
||||
|
||||
// getColour returns the colour from a styledLabel based on the key
|
||||
const char* getColour(JsonNode *styledLabelEntry, const char* key) {
|
||||
JsonNode* colEntry = getJSONObject(styledLabelEntry, key);
|
||||
if( colEntry == NULL ) {
|
||||
return NULL;
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
|
||||
// Create a MenuItemCallbackData
|
||||
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));
|
||||
}
|
||||
return getJSONString(colEntry, "hex");
|
||||
}
|
||||
|
||||
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Create result
|
||||
id attributedString = ALLOC_INIT("NSMutableAttributedString");
|
||||
msg_reg(attributedString, s("autorelease"));
|
||||
|
||||
// Create new Dictionary
|
||||
// Process Menu Item attributes
|
||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||
msg_reg(dictionary, s("autorelease"));
|
||||
|
||||
// Use default font
|
||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||
|
||||
// Check user supplied font
|
||||
if( STR_HAS_CHARS(fontName) ) {
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
if( userFont != NULL ) {
|
||||
font = userFont;
|
||||
}
|
||||
}
|
||||
|
||||
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||
id NSBackgroundColorAttributeName = lookupStringConstant(str("NSBackgroundColorAttributeName"));
|
||||
|
||||
// Loop over styled text creating NSAttributedText and appending to result
|
||||
JsonNode *styledLabelEntry;
|
||||
json_foreach(styledLabelEntry, styledLabel) {
|
||||
|
||||
// Clear dictionary
|
||||
msg_reg(dictionary, s("removeAllObjects"));
|
||||
|
||||
// Add font to dictionary
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||
|
||||
// Get Text
|
||||
const char* thisLabel = mustJSONString(styledLabelEntry, "Label");
|
||||
|
||||
// Get foreground colour
|
||||
const char *hexColour = getColour(styledLabelEntry, "FgCol");
|
||||
if( hexColour != NULL) {
|
||||
unsigned short r, g, b, a;
|
||||
|
||||
// white by default
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
// Get background colour
|
||||
hexColour = getColour(styledLabelEntry, "BgCol");
|
||||
if( hexColour != NULL) {
|
||||
unsigned short r, g, b, a;
|
||||
|
||||
// white by default
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(hexColour, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
// Create AttributedText
|
||||
id thisString = ALLOC("NSMutableAttributedString");
|
||||
msg_reg(thisString, s("autorelease"));
|
||||
msg_id_id(thisString, s("initWithString:attributes:"), str(thisLabel), dictionary);
|
||||
|
||||
// Append text to result
|
||||
msg_id(attributedString, s("appendAttributedString:"), thisString);
|
||||
}
|
||||
|
||||
return attributedString;
|
||||
|
||||
}
|
||||
|
||||
|
||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||
|
||||
// Create new Dictionary
|
||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||
// Process font
|
||||
id font;
|
||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||
|
||||
// Use default font
|
||||
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||
|
||||
// Check user supplied font
|
||||
if( STR_HAS_CHARS(fontName) ) {
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
if( userFont != NULL ) {
|
||||
font = userFont;
|
||||
// Check if valid
|
||||
id fontNameAsNSString = str(fontName);
|
||||
id fontsOnSystem = msg(msg(c("NSFontManager"), s("sharedFontManager")), s("availableFonts"));
|
||||
bool valid = msg(fontsOnSystem, s("containsObject:"), fontNameAsNSString);
|
||||
if( valid ) {
|
||||
font = msg(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||
} else {
|
||||
bool supportsMonospacedDigitSystemFont = (bool) msg(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
||||
if( supportsMonospacedDigitSystemFont ) {
|
||||
font = msg(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, NSFontWeightRegular);
|
||||
} else {
|
||||
font = msg(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
||||
}
|
||||
}
|
||||
|
||||
// Add font to dictionary
|
||||
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||
msg(dictionary, s("setObject:forKey:"), font, lookupStringConstant(str("NSFontAttributeName")));
|
||||
|
||||
// Add offset to dictionary
|
||||
id offset = msg(c("NSNumber"), s("numberWithFloat:"), 0.0);
|
||||
msg(dictionary, s("setObject:forKey:"), offset, lookupStringConstant(str("NSBaselineOffsetAttributeName")));
|
||||
|
||||
// RGBA
|
||||
if( RGBA != NULL && strlen(RGBA) > 0) {
|
||||
@@ -708,75 +641,32 @@ id createAttributedString(const char* title, const char* fontName, int fontSize,
|
||||
r = g = b = a = 255;
|
||||
int count = sscanf(RGBA, "#%02hx%02hx%02hx%02hx", &r, &g, &b, &a);
|
||||
if (count > 0) {
|
||||
id colour = ((id(*)(id, SEL, CGFloat, CGFloat, CGFloat, CGFloat))objc_msgSend)(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(CGFloat)r / (CGFloat)255.0,
|
||||
(CGFloat)g / (CGFloat)255.0,
|
||||
(CGFloat)b / (CGFloat)255.0,
|
||||
(CGFloat)a / (CGFloat)255.0);
|
||||
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||
id colour = msg(c("NSColor"), s("colorWithCalibratedRed:green:blue:alpha:"),
|
||||
(float)r / 255.0,
|
||||
(float)g / 255.0,
|
||||
(float)b / 255.0,
|
||||
(float)a / 255.0);
|
||||
msg(dictionary, s("setObject:forKey:"), colour, lookupStringConstant(str("NSForegroundColorAttributeName")));
|
||||
msg(colour, s("release"));
|
||||
}
|
||||
}
|
||||
|
||||
id attributedString = ALLOC("NSMutableAttributedString");
|
||||
msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||
msg_reg(attributedString, s("autorelease"));
|
||||
msg_reg(dictionary, s("autorelease"));
|
||||
return attributedString;
|
||||
}
|
||||
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||
msg(dictionary, s("release"));
|
||||
|
||||
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, JsonNode* styledLabel) {
|
||||
id item = ALLOC("NSMenuItem");
|
||||
msg(item, s("setAttributedTitle:"), attributedString);
|
||||
msg(attributedString, s("autorelease"));
|
||||
|
||||
// Create a MenuItemCallbackData
|
||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
||||
|
||||
id wrappedId = msg_id(c("NSValue"), s("valueWithPointer:"), (id)callback);
|
||||
msg_id(item, s("setRepresentedObject:"), wrappedId);
|
||||
|
||||
if( !alternate ) {
|
||||
id key = processAcceleratorKey(acceleratorkey);
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||
s("menuItemCallback:"), key);
|
||||
} else {
|
||||
((id(*)(id, SEL, id, SEL, id))objc_msgSend)(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
||||
}
|
||||
|
||||
if( tooltip != NULL ) {
|
||||
msg_id(item, s("setToolTip:"), str(tooltip));
|
||||
}
|
||||
|
||||
// Process image
|
||||
if( image != NULL && strlen(image) > 0) {
|
||||
id nsimage = createImageFromBase64Data(image, templateImage);
|
||||
msg_id(item, s("setImage:"), nsimage);
|
||||
}
|
||||
|
||||
id attributedString = NULL;
|
||||
if( styledLabel != NULL) {
|
||||
attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize);
|
||||
} else {
|
||||
attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
||||
}
|
||||
msg_id(item, s("setAttributedTitle:"), attributedString);
|
||||
|
||||
//msg_id(item, s("setTitle:"), str(title));
|
||||
|
||||
msg_bool(item, s("setEnabled:"), !disabled);
|
||||
msg_reg(item, s("autorelease"));
|
||||
msg(item, s("setEnabled:"), !disabled);
|
||||
msg(item, s("autorelease"));
|
||||
|
||||
// Process modifiers
|
||||
if( modifiers != NULL && !alternate) {
|
||||
if( modifiers != NULL ) {
|
||||
unsigned long modifierFlags = parseModifiers(modifiers);
|
||||
((id(*)(id, SEL, unsigned long))objc_msgSend)(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||
msg(item, s("setKeyEquivalentModifierMask:"), modifierFlags);
|
||||
}
|
||||
|
||||
// alternate
|
||||
if( alternate ) {
|
||||
msg_bool(item, s("setAlternate:"), true);
|
||||
msg_int(item, s("setKeyEquivalentModifierMask:"), NSEventModifierFlagOption);
|
||||
}
|
||||
msg_id(parentMenu, s("addItem:"), item);
|
||||
msg(parentMenu, s("addItem:"), item);
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -797,6 +687,38 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a submenu
|
||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||
if( submenu != NULL ) {
|
||||
// Get the label
|
||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||
const char *name = "";
|
||||
if ( menuNameNode != NULL) {
|
||||
name = menuNameNode->string_;
|
||||
}
|
||||
|
||||
id thisMenuItem = createMenuItemNoAutorelease(str(name), NULL, "");
|
||||
id thisMenu = createMenu(str(name));
|
||||
|
||||
msg(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||
msg(parentMenu, s("addItem:"), thisMenuItem);
|
||||
|
||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||
// If we have no items, just return
|
||||
if ( submenuItems == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over submenu items
|
||||
JsonNode *item;
|
||||
json_foreach(item, submenuItems) {
|
||||
// Get item label
|
||||
processMenuItem(menu, thisMenu, item);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a user menu. Get the common data
|
||||
// Get the label
|
||||
const char *label = getJSONString(item, "Label");
|
||||
@@ -804,13 +726,6 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
label = "(empty)";
|
||||
}
|
||||
|
||||
// Check for a styled label
|
||||
JsonNode *styledLabel = getJSONObject(item, "StyledLabel");
|
||||
|
||||
// Is this an alternate menu item?
|
||||
bool alternate = false;
|
||||
getJSONBool(item, "MacAlternate", &alternate);
|
||||
|
||||
const char *menuid = getJSONString(item, "ID");
|
||||
if ( menuid == NULL) {
|
||||
menuid = "";
|
||||
@@ -831,7 +746,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
bool templateImage = false;
|
||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||
|
||||
int fontSize = 0;
|
||||
int fontSize = 12;
|
||||
getJSONInt(item, "FontSize", &fontSize);
|
||||
|
||||
// If we have an accelerator
|
||||
@@ -864,36 +779,9 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
// Get the Type
|
||||
JsonNode *type = json_find_member(item, "Type");
|
||||
if( type != NULL ) {
|
||||
if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) {
|
||||
id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate, styledLabel);
|
||||
|
||||
// Check if this node has a submenu
|
||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||
if( submenu != NULL ) {
|
||||
// Get the label
|
||||
JsonNode *menuNameNode = json_find_member(item, "Label");
|
||||
const char *name = "";
|
||||
if ( menuNameNode != NULL) {
|
||||
name = menuNameNode->string_;
|
||||
}
|
||||
|
||||
id thisMenu = createMenu(str(name));
|
||||
|
||||
msg_id(thisMenuItem, s("setSubmenu:"), thisMenu);
|
||||
|
||||
JsonNode *submenuItems = json_find_member(submenu, "Items");
|
||||
// If we have no items, just return
|
||||
if ( submenuItems == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over submenu items
|
||||
JsonNode *item;
|
||||
json_foreach(item, submenuItems) {
|
||||
// Get item label
|
||||
processMenuItem(menu, thisMenu, item);
|
||||
}
|
||||
}
|
||||
if( STREQ(type->string_, "Text")) {
|
||||
processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage);
|
||||
}
|
||||
else if ( STREQ(type->string_, "Separator")) {
|
||||
addSeparator(parentMenu);
|
||||
@@ -912,6 +800,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
||||
|
||||
processRadioMenuItem(menu, parentMenu, label, menuid, disabled, checked, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( modifiers != NULL ) {
|
||||
|
||||
@@ -105,13 +105,10 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
||||
|
||||
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
||||
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate, JsonNode* styledLabel);
|
||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage);
|
||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||
|
||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) ;
|
||||
id GetMenu(Menu *menu);
|
||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
||||
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize);
|
||||
|
||||
#endif //ASSETS_C_MENU_DARWIN_H
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "traymenu_darwin.h"
|
||||
#include "trayicons.h"
|
||||
|
||||
extern Class trayMenuDelegateClass;
|
||||
|
||||
// A cache for all our tray menu icons
|
||||
// Global because it's a singleton
|
||||
struct hashmap_s trayIconCache;
|
||||
@@ -31,25 +29,12 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
||||
|
||||
result->ID = mustJSONString(processedJSON, "ID");
|
||||
result->label = mustJSONString(processedJSON, "Label");
|
||||
result->icon = mustJSONString(processedJSON, "Image");
|
||||
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);
|
||||
|
||||
result->styledLabel = getJSONObject(processedJSON, "StyledLabel");
|
||||
result->icon = mustJSONString(processedJSON, "Icon");
|
||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||
|
||||
// Create the menu
|
||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||
result->menu = NewMenu(processedMenu);
|
||||
|
||||
result->delegate = NULL;
|
||||
|
||||
// Init tray status bar item
|
||||
result->statusbaritem = NULL;
|
||||
|
||||
@@ -65,28 +50,15 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
||||
}
|
||||
|
||||
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel) {
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
||||
|
||||
// Exit early if NULL
|
||||
if( trayMenu->label == NULL ) {
|
||||
return;
|
||||
}
|
||||
// Update button label
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
id attributedString = NULL;
|
||||
if( styledLabel != NULL) {
|
||||
attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize);
|
||||
} else {
|
||||
attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||
}
|
||||
|
||||
if( tooltip != NULL ) {
|
||||
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
|
||||
}
|
||||
|
||||
msg_bool(statusBarButton, s("setEnabled:"), !disabled);
|
||||
|
||||
msg_id(statusBarButton, s("setAttributedTitle:"), attributedString);
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setTitle:"), str(label));
|
||||
}
|
||||
|
||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||
@@ -96,57 +68,44 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
|
||||
// Empty icon means remove it
|
||||
if( STREMPTY(trayMenu->icon) ) {
|
||||
// Remove image
|
||||
msg_id(statusBarButton, s("setImage:"), NULL);
|
||||
msg(statusBarButton, s("setImage:"), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
trayImage = createImageFromBase64Data(trayMenu->icon, trayMenu->templateImage);
|
||||
}
|
||||
|
||||
msg_int(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
msg_id(statusBarButton, s("setImage:"), trayImage);
|
||||
|
||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
msg(statusBarButton, s("setImage:"), trayImage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ShowTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// Create a status bar item if we don't have one
|
||||
if( trayMenu->statusbaritem == NULL ) {
|
||||
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
trayMenu->statusbaritem = ((id(*)(id, SEL, CGFloat))objc_msgSend)(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||
msg_reg(trayMenu->statusbaritem, s("retain"));
|
||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||
msg(trayMenu->statusbaritem, s("retain"));
|
||||
|
||||
}
|
||||
|
||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||
msg_uint(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||
|
||||
// Update the icon if needed
|
||||
UpdateTrayIcon(trayMenu);
|
||||
|
||||
// Update the label if needed
|
||||
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled, trayMenu->styledLabel);
|
||||
UpdateTrayLabel(trayMenu, trayMenu->label);
|
||||
|
||||
// Update the menu
|
||||
id menu = GetMenu(trayMenu->menu);
|
||||
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
||||
|
||||
// Create delegate
|
||||
id trayMenuDelegate = msg_reg((id)trayMenuDelegateClass, s("new"));
|
||||
msg_id(menu, s("setDelegate:"), trayMenuDelegate);
|
||||
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
||||
|
||||
// Create menu delegate
|
||||
trayMenu->delegate = trayMenuDelegate;
|
||||
|
||||
msg_id(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||
}
|
||||
|
||||
// UpdateTrayMenuInPlace receives 2 menus. The current menu gets
|
||||
@@ -168,7 +127,6 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
|
||||
// Copy the other data
|
||||
currentMenu->ID = newMenu->ID;
|
||||
currentMenu->label = newMenu->label;
|
||||
currentMenu->styledLabel = newMenu->styledLabel;
|
||||
currentMenu->trayIconPosition = newMenu->trayIconPosition;
|
||||
currentMenu->icon = newMenu->icon;
|
||||
|
||||
@@ -189,16 +147,12 @@ void DeleteTrayMenu(TrayMenu* trayMenu) {
|
||||
|
||||
// Free the status item
|
||||
if ( trayMenu->statusbaritem != NULL ) {
|
||||
id statusBar = msg_reg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
msg_id(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
||||
msg_reg(trayMenu->statusbaritem, s("release"));
|
||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||
msg(statusBar, s("removeStatusItem:"), trayMenu->statusbaritem);
|
||||
msg(trayMenu->statusbaritem, s("release"));
|
||||
trayMenu->statusbaritem = NULL;
|
||||
}
|
||||
|
||||
if ( trayMenu->delegate != NULL ) {
|
||||
msg_reg(trayMenu->delegate, s("release"));
|
||||
}
|
||||
|
||||
// Free the tray menu memory
|
||||
MEMFREE(trayMenu);
|
||||
}
|
||||
@@ -228,9 +182,9 @@ void LoadTrayIcons() {
|
||||
int length = atoi((const char *)lengthAsString);
|
||||
|
||||
// Create the icon and add to the hashmap
|
||||
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), (id)data, length);
|
||||
id imageData = msg(c("NSData"), s("dataWithBytes:length:"), data, length);
|
||||
id trayImage = ALLOC("NSImage");
|
||||
msg_id(trayImage, s("initWithData:"), imageData);
|
||||
msg(trayImage, s("initWithData:"), imageData);
|
||||
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,14 @@ typedef struct {
|
||||
const char *label;
|
||||
const char *icon;
|
||||
const char *ID;
|
||||
const char *tooltip;
|
||||
|
||||
bool templateImage;
|
||||
const char *fontName;
|
||||
int fontSize;
|
||||
const char *RGBA;
|
||||
|
||||
bool disabled;
|
||||
|
||||
Menu* menu;
|
||||
|
||||
id statusbaritem;
|
||||
unsigned int trayIconPosition;
|
||||
int trayIconPosition;
|
||||
|
||||
JsonNode* processedJSON;
|
||||
|
||||
JsonNode* styledLabel;
|
||||
|
||||
id delegate;
|
||||
|
||||
} TrayMenu;
|
||||
|
||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||
@@ -40,7 +28,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel);
|
||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char*);
|
||||
|
||||
void LoadTrayIcons();
|
||||
void UnloadTrayIcons();
|
||||
|
||||
@@ -16,11 +16,6 @@ TrayMenuStore* NewTrayMenuStore() {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -30,19 +25,15 @@ int dumpTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
}
|
||||
|
||||
void DumpTrayMenuStore(TrayMenuStore* store) {
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_iterate_pairs(&store->trayMenuMap, dumpTrayMenu, NULL);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
}
|
||||
|
||||
void AddTrayMenuToStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
|
||||
TrayMenu* newMenu = NewTrayMenu(menuJSON);
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
//TODO: check if there is already an entry for this menu
|
||||
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) {
|
||||
@@ -52,13 +43,12 @@ int showTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
}
|
||||
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store) {
|
||||
pthread_mutex_lock(&store->lock);
|
||||
if( hashmap_num_entries(&store->trayMenuMap) > 0 ) {
|
||||
hashmap_iterate_pairs(&store->trayMenuMap, showTrayMenu, NULL);
|
||||
}
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
}
|
||||
|
||||
|
||||
int freeTrayMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
DeleteTrayMenu(e->data);
|
||||
return -1;
|
||||
@@ -75,39 +65,22 @@ void DeleteTrayMenuStore(TrayMenuStore *store) {
|
||||
|
||||
// Destroy tray menu map
|
||||
hashmap_destroy(&store->trayMenuMap);
|
||||
|
||||
pthread_mutex_destroy(&store->lock);
|
||||
}
|
||||
|
||||
TrayMenu* GetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||
// Get the current menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
return result;
|
||||
return hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
}
|
||||
|
||||
TrayMenu* MustGetTrayMenuFromStore(TrayMenuStore* store, const char* menuID) {
|
||||
// Get the current menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
TrayMenu* result = hashmap_get(&store->trayMenuMap, menuID, strlen(menuID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
if (result == NULL ) {
|
||||
ABORT("Unable to find TrayMenu with ID '%s' in the TrayMenuStore!", menuID);
|
||||
}
|
||||
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) {
|
||||
// Parse the JSON
|
||||
JsonNode *parsedUpdate = mustParseJSON(JSON);
|
||||
@@ -118,19 +91,7 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
||||
|
||||
// Check we have this menu
|
||||
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||
|
||||
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);
|
||||
|
||||
JsonNode *styledLabel = getJSONObject(parsedUpdate, "StyledLabel");
|
||||
|
||||
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled, styledLabel);
|
||||
|
||||
UpdateTrayLabel(menu, Label);
|
||||
|
||||
}
|
||||
|
||||
@@ -144,9 +105,7 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
// If we don't have a menu, we create one
|
||||
if ( currentMenu == NULL ) {
|
||||
// Store the new menu
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Show it
|
||||
ShowTrayMenu(newMenu);
|
||||
@@ -157,9 +116,7 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
// Save the status bar reference
|
||||
newMenu->statusbaritem = currentMenu->statusbaritem;
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_remove(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID));
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Delete the current menu
|
||||
DeleteMenu(currentMenu->menu);
|
||||
@@ -168,10 +125,9 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON) {
|
||||
// Free the tray menu memory
|
||||
MEMFREE(currentMenu);
|
||||
|
||||
pthread_mutex_lock(&store->lock);
|
||||
hashmap_put(&store->trayMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
||||
pthread_mutex_unlock(&store->lock);
|
||||
|
||||
// Show the updated menu
|
||||
ShowTrayMenu(newMenu);
|
||||
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#ifndef TRAYMENUSTORE_DARWIN_H
|
||||
#define TRAYMENUSTORE_DARWIN_H
|
||||
|
||||
#include "traymenu_darwin.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
typedef struct {
|
||||
|
||||
int dummy;
|
||||
@@ -17,8 +13,6 @@ typedef struct {
|
||||
// It maps tray IDs to TrayMenu*
|
||||
struct hashmap_s trayMenuMap;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
|
||||
} TrayMenuStore;
|
||||
|
||||
TrayMenuStore* NewTrayMenuStore();
|
||||
@@ -28,9 +22,6 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON);
|
||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||
|
||||
TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID);
|
||||
|
||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
||||
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
||||
|
||||
#endif //TRAYMENUSTORE_DARWIN_H
|
||||
|
||||
@@ -40,7 +40,7 @@ func Mkdir(dirname string) error {
|
||||
// Returns error on failure
|
||||
func MkDirs(fullPath string, mode ...os.FileMode) error {
|
||||
var perms os.FileMode
|
||||
perms = 0755
|
||||
perms = 0700
|
||||
if len(mode) == 1 {
|
||||
perms = mode[0]
|
||||
}
|
||||
@@ -201,6 +201,10 @@ func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
||||
|
||||
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 {
|
||||
@@ -239,7 +243,7 @@ func CopyDir(src string, dst string) (err error) {
|
||||
return fmt.Errorf("destination already exists")
|
||||
}
|
||||
|
||||
err = MkDirs(dst)
|
||||
err = os.MkdirAll(dst, si.Mode())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -112,9 +112,6 @@ func (a *AssetBundle) processHTML(htmldata string) error {
|
||||
if attr.Key == "as" && attr.Val == "script" {
|
||||
asset.Type = AssetTypes.JS
|
||||
}
|
||||
if attr.Key == "rel" && attr.Val == "modulepreload" {
|
||||
asset.Type = AssetTypes.JS
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure we don't include duplicates
|
||||
|
||||
@@ -2,9 +2,6 @@ package menumanager
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/go-ansi-parser"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||
@@ -40,29 +37,14 @@ type ProcessedMenuItem struct {
|
||||
// Image - base64 image data
|
||||
Image string `json:",omitempty"`
|
||||
MacTemplateImage bool `json:", omitempty"`
|
||||
MacAlternate bool `json:", omitempty"`
|
||||
|
||||
// Tooltip
|
||||
Tooltip string `json:",omitempty"`
|
||||
|
||||
// Styled label
|
||||
StyledLabel []*ansi.StyledText `json:",omitempty"`
|
||||
}
|
||||
|
||||
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
|
||||
|
||||
ID := menuItemMap.menuItemToIDMap[menuItem]
|
||||
|
||||
// Parse ANSI text
|
||||
var styledLabel []*ansi.StyledText
|
||||
tempLabel := menuItem.Label
|
||||
if strings.Contains(tempLabel, "\033[") {
|
||||
parsedLabel, err := ansi.Parse(menuItem.Label)
|
||||
if err == nil {
|
||||
styledLabel = parsedLabel
|
||||
}
|
||||
}
|
||||
|
||||
result := &ProcessedMenuItem{
|
||||
ID: ID,
|
||||
Label: menuItem.Label,
|
||||
@@ -78,9 +60,7 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
|
||||
FontName: menuItem.FontName,
|
||||
Image: menuItem.Image,
|
||||
MacTemplateImage: menuItem.MacTemplateImage,
|
||||
MacAlternate: menuItem.MacAlternate,
|
||||
Tooltip: menuItem.Tooltip,
|
||||
StyledLabel: styledLabel,
|
||||
}
|
||||
|
||||
if menuItem.SubMenu != nil {
|
||||
|
||||
@@ -3,43 +3,29 @@ package menumanager
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/leaanthony/go-ansi-parser"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var trayMenuID int
|
||||
var trayMenuIDMutex sync.Mutex
|
||||
|
||||
func generateTrayID() string {
|
||||
var idStr string
|
||||
trayMenuIDMutex.Lock()
|
||||
idStr = strconv.Itoa(trayMenuID)
|
||||
result := fmt.Sprintf("%d", trayMenuID)
|
||||
trayMenuID++
|
||||
trayMenuIDMutex.Unlock()
|
||||
return idStr
|
||||
return result
|
||||
}
|
||||
|
||||
type TrayMenu struct {
|
||||
ID string
|
||||
Label string
|
||||
FontSize int
|
||||
FontName string
|
||||
Disabled bool
|
||||
Tooltip string `json:",omitempty"`
|
||||
Image string
|
||||
MacTemplateImage bool
|
||||
RGBA string
|
||||
menuItemMap *MenuItemMap
|
||||
menu *menu.Menu
|
||||
ProcessedMenu *WailsMenu
|
||||
trayMenu *menu.TrayMenu
|
||||
StyledLabel []*ansi.StyledText `json:",omitempty"`
|
||||
ID string
|
||||
Label string
|
||||
Icon string
|
||||
menuItemMap *MenuItemMap
|
||||
menu *menu.Menu
|
||||
ProcessedMenu *WailsMenu
|
||||
}
|
||||
|
||||
func (t *TrayMenu) AsJSON() (string, error) {
|
||||
@@ -52,29 +38,11 @@ func (t *TrayMenu) AsJSON() (string, error) {
|
||||
|
||||
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
||||
|
||||
// Parse ANSI text
|
||||
var styledLabel []*ansi.StyledText
|
||||
tempLabel := trayMenu.Label
|
||||
if strings.Contains(tempLabel, "\033[") {
|
||||
parsedLabel, err := ansi.Parse(tempLabel)
|
||||
if err == nil {
|
||||
styledLabel = parsedLabel
|
||||
}
|
||||
}
|
||||
|
||||
result := &TrayMenu{
|
||||
Label: trayMenu.Label,
|
||||
FontName: trayMenu.FontName,
|
||||
FontSize: trayMenu.FontSize,
|
||||
Disabled: trayMenu.Disabled,
|
||||
Tooltip: trayMenu.Tooltip,
|
||||
Image: trayMenu.Image,
|
||||
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||
menu: trayMenu.Menu,
|
||||
RGBA: trayMenu.RGBA,
|
||||
menuItemMap: NewMenuItemMap(),
|
||||
trayMenu: trayMenu,
|
||||
StyledLabel: styledLabel,
|
||||
Label: trayMenu.Label,
|
||||
Icon: trayMenu.Icon,
|
||||
menu: trayMenu.Menu,
|
||||
menuItemMap: NewMenuItemMap(),
|
||||
}
|
||||
|
||||
result.menuItemMap.AddMenu(trayMenu.Menu)
|
||||
@@ -83,28 +51,6 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
||||
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) {
|
||||
newTrayMenu := NewTrayMenu(trayMenu)
|
||||
|
||||
@@ -119,14 +65,6 @@ func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||
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
|
||||
func (m *Manager) SetTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||
trayID, trayMenuKnown := m.trayMenuPointers[trayMenu]
|
||||
@@ -164,39 +102,13 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
|
||||
}
|
||||
|
||||
type LabelUpdate struct {
|
||||
ID string
|
||||
Label string `json:",omitempty"`
|
||||
FontName string `json:",omitempty"`
|
||||
FontSize int
|
||||
RGBA string `json:",omitempty"`
|
||||
Disabled bool
|
||||
Tooltip string `json:",omitempty"`
|
||||
Image string `json:",omitempty"`
|
||||
MacTemplateImage bool
|
||||
StyledLabel []*ansi.StyledText `json:",omitempty"`
|
||||
}
|
||||
|
||||
// Parse ANSI text
|
||||
var styledLabel []*ansi.StyledText
|
||||
tempLabel := trayMenu.Label
|
||||
if strings.Contains(tempLabel, "\033[") {
|
||||
parsedLabel, err := ansi.Parse(tempLabel)
|
||||
if err == nil {
|
||||
styledLabel = parsedLabel
|
||||
}
|
||||
ID string
|
||||
Label string
|
||||
}
|
||||
|
||||
update := &LabelUpdate{
|
||||
ID: trayID,
|
||||
Label: trayMenu.Label,
|
||||
FontName: trayMenu.FontName,
|
||||
FontSize: trayMenu.FontSize,
|
||||
Disabled: trayMenu.Disabled,
|
||||
Tooltip: trayMenu.Tooltip,
|
||||
Image: trayMenu.Image,
|
||||
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||
RGBA: trayMenu.RGBA,
|
||||
StyledLabel: styledLabel,
|
||||
ID: trayID,
|
||||
Label: trayMenu.Label,
|
||||
}
|
||||
|
||||
data, err := json.Marshal(update)
|
||||
|
||||
@@ -37,7 +37,6 @@ type Client interface {
|
||||
SetTrayMenu(trayMenuJSON string)
|
||||
UpdateTrayMenuLabel(JSON string)
|
||||
UpdateContextMenu(contextMenuJSON string)
|
||||
DeleteTrayMenuByID(id string)
|
||||
}
|
||||
|
||||
// DispatchClient is what the frontends use to interface with the
|
||||
|
||||
@@ -32,14 +32,6 @@ func menuMessageParser(message string) (*parsedMessage, error) {
|
||||
callbackid := message[2:]
|
||||
topic = "menu:clicked"
|
||||
data = callbackid
|
||||
case 'o':
|
||||
callbackid := message[2:]
|
||||
topic = "menu:ontrayopen"
|
||||
data = callbackid
|
||||
case 'c':
|
||||
callbackid := message[2:]
|
||||
topic = "menu:ontrayclose"
|
||||
data = callbackid
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid menu message: %s", message)
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
||||
'M': menuMessageParser,
|
||||
'T': trayMessageParser,
|
||||
'X': contextMenusMessageParser,
|
||||
'U': urlMessageParser,
|
||||
}
|
||||
|
||||
// Parse will attempt to parse the given message
|
||||
func Parse(message string) (*parsedMessage, error) {
|
||||
|
||||
if len(message) == 0 {
|
||||
return nil, fmt.Errorf("MessageParser received blank message")
|
||||
return nil, fmt.Errorf("MessageParser received blank message");
|
||||
}
|
||||
|
||||
parseMethod := messageParsers[message[0]]
|
||||
|
||||
@@ -40,8 +40,7 @@ func systemMessageParser(message string) (*parsedMessage, error) {
|
||||
// This is our startup hook - the frontend is now ready
|
||||
case 'S':
|
||||
topic := "hooks:startup"
|
||||
startupURL := message[1:]
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: startupURL}
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: nil}
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
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])
|
||||
}
|
||||
}
|
||||
@@ -527,17 +527,6 @@ func (d *Dispatcher) processMenuMessage(result *servicebus.Message) {
|
||||
for _, client := range d.clients {
|
||||
client.frontend.UpdateTrayMenuLabel(updatedTrayMenuLabel)
|
||||
}
|
||||
case "deletetraymenu":
|
||||
traymenuid, ok := result.Data().(string)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid data for 'menufrontend:updatetraymenulabel' : %#v",
|
||||
result.Data())
|
||||
return
|
||||
}
|
||||
|
||||
for _, client := range d.clients {
|
||||
client.frontend.DeleteTrayMenuByID(traymenuid)
|
||||
}
|
||||
|
||||
default:
|
||||
d.logger.Error("Unknown menufrontend command: %s", command)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
@@ -9,18 +10,16 @@ import (
|
||||
|
||||
// Process defines a process that can be executed
|
||||
type Process struct {
|
||||
logger *clilogger.CLILogger
|
||||
cmd *exec.Cmd
|
||||
exitChannel chan bool
|
||||
Running bool
|
||||
logger *clilogger.CLILogger
|
||||
cmd *exec.Cmd
|
||||
running bool
|
||||
}
|
||||
|
||||
// NewProcess creates a new process struct
|
||||
func NewProcess(logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
func NewProcess(ctx context.Context, logger *clilogger.CLILogger, cmd string, args ...string) *Process {
|
||||
result := &Process{
|
||||
logger: logger,
|
||||
cmd: exec.Command(cmd, args...),
|
||||
exitChannel: make(chan bool, 1),
|
||||
logger: logger,
|
||||
cmd: exec.CommandContext(ctx, cmd, args...),
|
||||
}
|
||||
result.cmd.Stdout = os.Stdout
|
||||
result.cmd.Stderr = os.Stderr
|
||||
@@ -35,35 +34,30 @@ func (p *Process) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Running = true
|
||||
p.running = true
|
||||
|
||||
go func(cmd *exec.Cmd, running *bool, logger *clilogger.CLILogger, exitChannel chan bool) {
|
||||
logger.Println("Starting process (PID: %d)", cmd.Process.Pid)
|
||||
err := cmd.Wait()
|
||||
go func(p *Process) {
|
||||
p.logger.Println("Starting process (PID: %d)", p.cmd.Process.Pid)
|
||||
err := p.cmd.Wait()
|
||||
if err != nil {
|
||||
if err.Error() != "signal: killed" {
|
||||
logger.Fatal("Fatal error from app: " + err.Error())
|
||||
}
|
||||
p.running = false
|
||||
p.logger.Println("Process failed to run: %s", err.Error())
|
||||
return
|
||||
}
|
||||
logger.Println("Exiting process (PID: %d)", cmd.Process.Pid)
|
||||
*running = false
|
||||
exitChannel <- true
|
||||
}(p.cmd, &p.Running, p.logger, p.exitChannel)
|
||||
p.logger.Println("Exiting process (PID: %d)", p.cmd.Process.Pid)
|
||||
}(p)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Kill the process
|
||||
func (p *Process) Kill() error {
|
||||
if !p.Running {
|
||||
return nil
|
||||
if p.running {
|
||||
println("Calling kill")
|
||||
p.running = false
|
||||
return p.cmd.Process.Kill()
|
||||
}
|
||||
err := p.cmd.Process.Kill()
|
||||
|
||||
// Wait for command to exit properly
|
||||
<-p.exitChannel
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// PID returns the process PID
|
||||
|
||||
@@ -620,7 +620,7 @@
|
||||
}
|
||||
|
||||
/** Menubar **/
|
||||
const menuVisible = writable(false);
|
||||
const menuVisible = writable(true);
|
||||
|
||||
/** Trays **/
|
||||
|
||||
@@ -649,18 +649,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
function fade(node, { delay = 0, duration = 400, easing = identity } = {}) {
|
||||
@@ -791,18 +779,18 @@
|
||||
|
||||
function add_css$1() {
|
||||
var style = element("style");
|
||||
style.id = "svelte-1oysp7o-style";
|
||||
style.textContent = ".menu.svelte-1oysp7o.svelte-1oysp7o{padding:3px;background-color:#0008;color:#EEF;border-radius:5px;margin-top:5px;position:absolute;backdrop-filter:blur(3px) saturate(160%) contrast(45%) brightness(140%);border:1px solid rgb(88,88,88);box-shadow:0 0 1px rgb(146,146,148) inset}.menuitem.svelte-1oysp7o.svelte-1oysp7o{display:flex;align-items:center;padding:1px 5px}.menuitem.svelte-1oysp7o.svelte-1oysp7o:hover{display:flex;align-items:center;background-color:rgb(57,131,223);padding:1px 5px;border-radius:5px}.menuitem.svelte-1oysp7o img.svelte-1oysp7o{padding-right:5px}";
|
||||
style.id = "svelte-1ucacnf-style";
|
||||
style.textContent = ".menu.svelte-1ucacnf.svelte-1ucacnf{padding:5px;background-color:#0008;color:#EEF;border-radius:5px;margin-top:5px;position:absolute;backdrop-filter:blur(3px) saturate(160%) contrast(45%) brightness(140%);border:1px solid rgb(88,88,88);box-shadow:0 0 1px rgb(146,146,148) inset}.menuitem.svelte-1ucacnf.svelte-1ucacnf{display:flex;align-items:center;padding:1px 5px}.menuitem.svelte-1ucacnf.svelte-1ucacnf:hover{display:flex;align-items:center;background-color:rgb(57,131,223);padding:1px 5px;border-radius:5px}.menuitem.svelte-1ucacnf img.svelte-1ucacnf{padding-right:5px}.separator.svelte-1ucacnf.svelte-1ucacnf{padding-top:5px;width:100%;padding-bottom:5px}.separator.svelte-1ucacnf.svelte-1ucacnf:hover{background-color:#0000}";
|
||||
append(document.head, style);
|
||||
}
|
||||
|
||||
function get_each_context(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[2] = list[i];
|
||||
child_ctx[3] = list[i];
|
||||
return child_ctx;
|
||||
}
|
||||
|
||||
// (8:0) {#if !hidden}
|
||||
// (14:0) {#if visible}
|
||||
function create_if_block$1(ctx) {
|
||||
let div;
|
||||
let if_block = /*menu*/ ctx[0].Menu && create_if_block_1(ctx);
|
||||
@@ -811,7 +799,7 @@
|
||||
c() {
|
||||
div = element("div");
|
||||
if (if_block) if_block.c();
|
||||
attr(div, "class", "menu svelte-1oysp7o");
|
||||
attr(div, "class", "menu svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
@@ -838,7 +826,7 @@
|
||||
};
|
||||
}
|
||||
|
||||
// (10:4) {#if menu.Menu }
|
||||
// (16:4) {#if menu.Menu }
|
||||
function create_if_block_1(ctx) {
|
||||
let each_1_anchor;
|
||||
let each_value = /*menu*/ ctx[0].Menu.Items;
|
||||
@@ -864,7 +852,7 @@
|
||||
insert(target, each_1_anchor, anchor);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1) {
|
||||
if (dirty & /*click, menu*/ 1) {
|
||||
each_value = /*menu*/ ctx[0].Menu.Items;
|
||||
let i;
|
||||
|
||||
@@ -894,44 +882,41 @@
|
||||
};
|
||||
}
|
||||
|
||||
// (13:12) {#if menuItem.Image }
|
||||
function create_if_block_2(ctx) {
|
||||
// (25:52)
|
||||
function create_if_block_4(ctx) {
|
||||
let div;
|
||||
let img;
|
||||
let img_src_value;
|
||||
|
||||
return {
|
||||
c() {
|
||||
div = element("div");
|
||||
img = element("img");
|
||||
attr(img, "alt", "");
|
||||
if (img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) attr(img, "src", img_src_value);
|
||||
attr(img, "class", "svelte-1oysp7o");
|
||||
div.innerHTML = `<hr/>`;
|
||||
attr(div, "class", "separator svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
append(div, img);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1 && img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[2].Image)) {
|
||||
attr(img, "src", img_src_value);
|
||||
}
|
||||
},
|
||||
p: noop,
|
||||
d(detaching) {
|
||||
if (detaching) detach(div);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (11:8) {#each menu.Menu.Items as menuItem}
|
||||
function create_each_block(ctx) {
|
||||
// (18:12) {#if menuItem.Type === "Text" }
|
||||
function create_if_block_2(ctx) {
|
||||
let div1;
|
||||
let t0;
|
||||
let div0;
|
||||
let t1_value = /*menuItem*/ ctx[2].Label + "";
|
||||
let t1_value = /*menuItem*/ ctx[3].Label + "";
|
||||
let t1;
|
||||
let t2;
|
||||
let if_block = /*menuItem*/ ctx[2].Image && create_if_block_2(ctx);
|
||||
let mounted;
|
||||
let dispose;
|
||||
let if_block = /*menuItem*/ ctx[3].Image && create_if_block_3(ctx);
|
||||
|
||||
function click_handler() {
|
||||
return /*click_handler*/ ctx[2](/*menuItem*/ ctx[3]);
|
||||
}
|
||||
|
||||
return {
|
||||
c() {
|
||||
@@ -942,7 +927,7 @@
|
||||
t1 = text(t1_value);
|
||||
t2 = space();
|
||||
attr(div0, "class", "menulabel");
|
||||
attr(div1, "class", "menuitem svelte-1oysp7o");
|
||||
attr(div1, "class", "menuitem svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div1, anchor);
|
||||
@@ -951,13 +936,20 @@
|
||||
append(div1, div0);
|
||||
append(div0, t1);
|
||||
append(div1, t2);
|
||||
|
||||
if (!mounted) {
|
||||
dispose = listen(div1, "click", click_handler);
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (/*menuItem*/ ctx[2].Image) {
|
||||
p(new_ctx, dirty) {
|
||||
ctx = new_ctx;
|
||||
|
||||
if (/*menuItem*/ ctx[3].Image) {
|
||||
if (if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
if_block = create_if_block_2(ctx);
|
||||
if_block = create_if_block_3(ctx);
|
||||
if_block.c();
|
||||
if_block.m(div1, t0);
|
||||
}
|
||||
@@ -966,18 +958,93 @@
|
||||
if_block = null;
|
||||
}
|
||||
|
||||
if (dirty & /*menu*/ 1 && t1_value !== (t1_value = /*menuItem*/ ctx[2].Label + "")) set_data(t1, t1_value);
|
||||
if (dirty & /*menu*/ 1 && t1_value !== (t1_value = /*menuItem*/ ctx[3].Label + "")) set_data(t1, t1_value);
|
||||
},
|
||||
d(detaching) {
|
||||
if (detaching) detach(div1);
|
||||
if (if_block) if_block.d();
|
||||
mounted = false;
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (20:12) {#if menuItem.Image }
|
||||
function create_if_block_3(ctx) {
|
||||
let div;
|
||||
let img;
|
||||
let img_src_value;
|
||||
|
||||
return {
|
||||
c() {
|
||||
div = element("div");
|
||||
img = element("img");
|
||||
attr(img, "alt", "");
|
||||
if (img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[3].Image)) attr(img, "src", img_src_value);
|
||||
attr(img, "class", "svelte-1ucacnf");
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
append(div, img);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (dirty & /*menu*/ 1 && img.src !== (img_src_value = "data:image/png;base64," + /*menuItem*/ ctx[3].Image)) {
|
||||
attr(img, "src", img_src_value);
|
||||
}
|
||||
},
|
||||
d(detaching) {
|
||||
if (detaching) detach(div);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// (17:8) {#each menu.Menu.Items as menuItem}
|
||||
function create_each_block(ctx) {
|
||||
let if_block_anchor;
|
||||
|
||||
function select_block_type(ctx, dirty) {
|
||||
if (/*menuItem*/ ctx[3].Type === "Text") return create_if_block_2;
|
||||
if (/*menuItem*/ ctx[3].Type === "Separator") return create_if_block_4;
|
||||
}
|
||||
|
||||
let current_block_type = select_block_type(ctx);
|
||||
let if_block = current_block_type && current_block_type(ctx);
|
||||
|
||||
return {
|
||||
c() {
|
||||
if (if_block) if_block.c();
|
||||
if_block_anchor = empty();
|
||||
},
|
||||
m(target, anchor) {
|
||||
if (if_block) if_block.m(target, anchor);
|
||||
insert(target, if_block_anchor, anchor);
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
if (current_block_type === (current_block_type = select_block_type(ctx)) && if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
if (if_block) if_block.d(1);
|
||||
if_block = current_block_type && current_block_type(ctx);
|
||||
|
||||
if (if_block) {
|
||||
if_block.c();
|
||||
if_block.m(if_block_anchor.parentNode, if_block_anchor);
|
||||
}
|
||||
}
|
||||
},
|
||||
d(detaching) {
|
||||
if (if_block) {
|
||||
if_block.d(detaching);
|
||||
}
|
||||
|
||||
if (detaching) detach(if_block_anchor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function create_fragment$1(ctx) {
|
||||
let if_block_anchor;
|
||||
let if_block = !/*hidden*/ ctx[1] && create_if_block$1(ctx);
|
||||
let if_block = /*visible*/ ctx[1] && create_if_block$1(ctx);
|
||||
|
||||
return {
|
||||
c() {
|
||||
@@ -989,7 +1056,7 @@
|
||||
insert(target, if_block_anchor, anchor);
|
||||
},
|
||||
p(ctx, [dirty]) {
|
||||
if (!/*hidden*/ ctx[1]) {
|
||||
if (/*visible*/ ctx[1]) {
|
||||
if (if_block) {
|
||||
if_block.p(ctx, dirty);
|
||||
} else {
|
||||
@@ -1011,23 +1078,29 @@
|
||||
};
|
||||
}
|
||||
|
||||
function click(id) {
|
||||
console.log("MenuItem", id, "pressed");
|
||||
}
|
||||
|
||||
function instance$1($$self, $$props, $$invalidate) {
|
||||
let { menu } = $$props;
|
||||
let { hidden = true } = $$props;
|
||||
console.log({ menu });
|
||||
let { visible = false } = $$props;
|
||||
const click_handler = menuItem => click(menuItem.ID);
|
||||
|
||||
$$self.$$set = $$props => {
|
||||
if ("menu" in $$props) $$invalidate(0, menu = $$props.menu);
|
||||
if ("hidden" in $$props) $$invalidate(1, hidden = $$props.hidden);
|
||||
if ("visible" in $$props) $$invalidate(1, visible = $$props.visible);
|
||||
};
|
||||
|
||||
return [menu, hidden];
|
||||
return [menu, visible, click_handler];
|
||||
}
|
||||
|
||||
class Menu extends SvelteComponent {
|
||||
constructor(options) {
|
||||
super();
|
||||
if (!document.getElementById("svelte-1oysp7o-style")) add_css$1();
|
||||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { menu: 0, hidden: 1 });
|
||||
if (!document.getElementById("svelte-1ucacnf-style")) add_css$1();
|
||||
init(this, options, instance$1, create_fragment$1, safe_not_equal, { menu: 0, visible: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,7 +1115,7 @@
|
||||
append(document_1.head, style);
|
||||
}
|
||||
|
||||
// (47:4) {#if tray.ProcessedMenu }
|
||||
// (48:4) {#if tray.ProcessedMenu }
|
||||
function create_if_block$2(ctx) {
|
||||
let menu;
|
||||
let current;
|
||||
@@ -1050,7 +1123,7 @@
|
||||
menu = new Menu({
|
||||
props: {
|
||||
menu: /*tray*/ ctx[0].ProcessedMenu,
|
||||
hidden: /*hidden*/ ctx[1]
|
||||
visible: /*visible*/ ctx[1]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1065,7 +1138,7 @@
|
||||
p(ctx, dirty) {
|
||||
const menu_changes = {};
|
||||
if (dirty & /*tray*/ 1) menu_changes.menu = /*tray*/ ctx[0].ProcessedMenu;
|
||||
if (dirty & /*hidden*/ 2) menu_changes.hidden = /*hidden*/ ctx[1];
|
||||
if (dirty & /*visible*/ 2) menu_changes.visible = /*visible*/ ctx[1];
|
||||
menu.$set(menu_changes);
|
||||
},
|
||||
i(local) {
|
||||
@@ -1169,6 +1242,7 @@
|
||||
function clickOutside(node) {
|
||||
const handleClick = event => {
|
||||
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
||||
console.log("click outside of node");
|
||||
node.dispatchEvent(new CustomEvent("click_outside", node));
|
||||
}
|
||||
};
|
||||
@@ -1183,7 +1257,7 @@
|
||||
}
|
||||
|
||||
function instance$2($$self, $$props, $$invalidate) {
|
||||
let hidden;
|
||||
let visible;
|
||||
let $selectedMenu;
|
||||
component_subscribe($$self, selectedMenu, $$value => $$invalidate(4, $selectedMenu = $$value));
|
||||
let { tray = null } = $$props;
|
||||
@@ -1206,11 +1280,11 @@
|
||||
|
||||
$$self.$$.update = () => {
|
||||
if ($$self.$$.dirty & /*$selectedMenu, tray*/ 17) {
|
||||
$$invalidate(1, hidden = $selectedMenu !== tray);
|
||||
$$invalidate(1, visible = $selectedMenu === tray);
|
||||
}
|
||||
};
|
||||
|
||||
return [tray, hidden, closeMenu, trayClicked, $selectedMenu];
|
||||
return [tray, visible, closeMenu, trayClicked, $selectedMenu];
|
||||
}
|
||||
|
||||
class TrayMenu extends SvelteComponent {
|
||||
@@ -1232,11 +1306,11 @@
|
||||
|
||||
function get_each_context$1(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[9] = list[i];
|
||||
child_ctx[8] = list[i];
|
||||
return child_ctx;
|
||||
}
|
||||
|
||||
// (38:0) {#if $menuVisible }
|
||||
// (29:0) {#if $menuVisible }
|
||||
function create_if_block$3(ctx) {
|
||||
let div;
|
||||
let span1;
|
||||
@@ -1348,11 +1422,11 @@
|
||||
};
|
||||
}
|
||||
|
||||
// (41:4) {#each $trays as tray}
|
||||
// (32:4) {#each $trays as tray}
|
||||
function create_each_block$1(ctx) {
|
||||
let traymenu;
|
||||
let current;
|
||||
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[9] } });
|
||||
traymenu = new TrayMenu({ props: { tray: /*tray*/ ctx[8] } });
|
||||
|
||||
return {
|
||||
c() {
|
||||
@@ -1364,7 +1438,7 @@
|
||||
},
|
||||
p(ctx, dirty) {
|
||||
const traymenu_changes = {};
|
||||
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[9];
|
||||
if (dirty & /*$trays*/ 4) traymenu_changes.tray = /*tray*/ ctx[8];
|
||||
traymenu.$set(traymenu_changes);
|
||||
},
|
||||
i(local) {
|
||||
@@ -1385,8 +1459,6 @@
|
||||
function create_fragment$3(ctx) {
|
||||
let if_block_anchor;
|
||||
let current;
|
||||
let mounted;
|
||||
let dispose;
|
||||
let if_block = /*$menuVisible*/ ctx[1] && create_if_block$3(ctx);
|
||||
|
||||
return {
|
||||
@@ -1398,11 +1470,6 @@
|
||||
if (if_block) if_block.m(target, anchor);
|
||||
insert(target, if_block_anchor, anchor);
|
||||
current = true;
|
||||
|
||||
if (!mounted) {
|
||||
dispose = listen(window, "keydown", /*handleKeydown*/ ctx[3]);
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
p(ctx, [dirty]) {
|
||||
if (/*$menuVisible*/ ctx[1]) {
|
||||
@@ -1440,8 +1507,6 @@
|
||||
d(detaching) {
|
||||
if (if_block) if_block.d(detaching);
|
||||
if (detaching) detach(if_block_anchor);
|
||||
mounted = false;
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1461,7 +1526,7 @@
|
||||
onMount(() => {
|
||||
const interval = setInterval(
|
||||
() => {
|
||||
$$invalidate(4, time = new Date());
|
||||
$$invalidate(3, time = new Date());
|
||||
},
|
||||
1000
|
||||
);
|
||||
@@ -1471,52 +1536,33 @@
|
||||
};
|
||||
});
|
||||
|
||||
function handleKeydown(e) {
|
||||
// Backtick toggle
|
||||
if (e.keyCode == 192) {
|
||||
menuVisible.update(current => {
|
||||
return !current;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$$self.$$.update = () => {
|
||||
if ($$self.$$.dirty & /*time*/ 16) {
|
||||
$$invalidate(5, day = time.toLocaleString("default", { weekday: "short" }));
|
||||
if ($$self.$$.dirty & /*time*/ 8) {
|
||||
$$invalidate(4, day = time.toLocaleString("default", { weekday: "short" }));
|
||||
}
|
||||
|
||||
if ($$self.$$.dirty & /*time*/ 16) {
|
||||
$$invalidate(6, dom = time.getDate());
|
||||
if ($$self.$$.dirty & /*time*/ 8) {
|
||||
$$invalidate(5, dom = time.getDate());
|
||||
}
|
||||
|
||||
if ($$self.$$.dirty & /*time*/ 16) {
|
||||
$$invalidate(7, mon = time.toLocaleString("default", { month: "short" }));
|
||||
if ($$self.$$.dirty & /*time*/ 8) {
|
||||
$$invalidate(6, mon = time.toLocaleString("default", { month: "short" }));
|
||||
}
|
||||
|
||||
if ($$self.$$.dirty & /*time*/ 16) {
|
||||
$$invalidate(8, currentTime = time.toLocaleString("en-US", {
|
||||
if ($$self.$$.dirty & /*time*/ 8) {
|
||||
$$invalidate(7, currentTime = time.toLocaleString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
hour12: true
|
||||
}).toLowerCase());
|
||||
}
|
||||
|
||||
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 480) {
|
||||
if ($$self.$$.dirty & /*day, dom, mon, currentTime*/ 240) {
|
||||
$$invalidate(0, dateTimeString = `${day} ${dom} ${mon} ${currentTime}`);
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
dateTimeString,
|
||||
$menuVisible,
|
||||
$trays,
|
||||
handleKeydown,
|
||||
time,
|
||||
day,
|
||||
dom,
|
||||
mon,
|
||||
currentTime
|
||||
];
|
||||
return [dateTimeString, $menuVisible, $trays, time, day, dom, mon, currentTime];
|
||||
}
|
||||
|
||||
class Menubar extends SvelteComponent {
|
||||
@@ -1678,11 +1724,6 @@
|
||||
let trayLabelData = JSON.parse(updateTrayLabelJSON);
|
||||
updateTrayLabel(trayLabelData);
|
||||
break
|
||||
case 'D':
|
||||
// Delete Tray Menu
|
||||
const id = trayMessage.slice(1);
|
||||
deleteTrayMenu(id);
|
||||
break
|
||||
default:
|
||||
log('Unknown tray message: ' + message.data);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wails/runtime",
|
||||
"version": "1.3.12",
|
||||
"version": "1.3.9",
|
||||
"description": "Wails V2 Javascript runtime library",
|
||||
"main": "main.js",
|
||||
"types": "runtime.d.ts",
|
||||
|
||||
@@ -2,19 +2,29 @@
|
||||
|
||||
export let menu;
|
||||
|
||||
export let hidden = true;
|
||||
console.log({menu})
|
||||
|
||||
export let visible = false;
|
||||
|
||||
function click(id) {
|
||||
console.log("MenuItem", id, "pressed")
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !hidden}
|
||||
{#if visible}
|
||||
<div class="menu">
|
||||
{#if menu.Menu }
|
||||
{#each menu.Menu.Items as menuItem}
|
||||
<div class="menuitem">
|
||||
{#if menuItem.Type === "Text" }
|
||||
<div class="menuitem" on:click={() => click(menuItem.ID)}>
|
||||
{#if menuItem.Image }
|
||||
<div><img alt="" src="data:image/png;base64,{menuItem.Image}"/></div>
|
||||
{/if}
|
||||
<div class="menulabel">{menuItem.Label}</div>
|
||||
<div class="menulabel">{menuItem.Label}</div>
|
||||
</div>
|
||||
{:else if menuItem.Type === "Separator"}
|
||||
<div class="separator"><hr/></div>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
@@ -23,7 +33,7 @@
|
||||
<style>
|
||||
|
||||
.menu {
|
||||
padding: 3px;
|
||||
padding: 5px;
|
||||
background-color: #0008;
|
||||
color: #EEF;
|
||||
border-radius: 5px;
|
||||
@@ -32,7 +42,6 @@
|
||||
backdrop-filter: blur(3px) saturate(160%) contrast(45%) brightness(140%);
|
||||
border: 1px solid rgb(88,88,88);
|
||||
box-shadow: 0 0 1px rgb(146,146,148) inset;
|
||||
|
||||
}
|
||||
|
||||
.menuitem {
|
||||
@@ -53,4 +62,13 @@
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.separator {
|
||||
padding-top: 5px;
|
||||
width: 100%;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.separator:hover {
|
||||
background-color: #0000;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -24,15 +24,6 @@
|
||||
};
|
||||
});
|
||||
|
||||
function handleKeydown(e) {
|
||||
// Backtick toggle
|
||||
if( e.keyCode == 192 ) {
|
||||
menuVisible.update( (current) => {
|
||||
return !current;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if $menuVisible }
|
||||
@@ -46,8 +37,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<svelte:window on:keydown={handleKeydown}/>
|
||||
|
||||
<style>
|
||||
|
||||
.tray-menus {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
export let tray = null;
|
||||
|
||||
$: hidden = $selectedMenu !== tray;
|
||||
$: visible = $selectedMenu === tray;
|
||||
|
||||
function closeMenu() {
|
||||
selectedMenu.set(null);
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
const handleClick = event => {
|
||||
if (node && !node.contains(event.target) && !event.defaultPrevented) {
|
||||
console.log("click outside of node")
|
||||
node.dispatchEvent(
|
||||
new CustomEvent('click_outside', node)
|
||||
)
|
||||
@@ -45,7 +46,7 @@
|
||||
<!--{/if}-->
|
||||
<span class="label" on:click={trayClicked}>{tray.Label}</span>
|
||||
{#if tray.ProcessedMenu }
|
||||
<Menu menu="{tray.ProcessedMenu}" {hidden}/>
|
||||
<Menu menu="{tray.ProcessedMenu}" visible="{visible}"/>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export function hideOverlay() {
|
||||
}
|
||||
|
||||
/** Menubar **/
|
||||
export const menuVisible = writable(false);
|
||||
export const menuVisible = writable(true);
|
||||
|
||||
export function showMenuBar() {
|
||||
menuVisible.set(true);
|
||||
@@ -49,16 +49,4 @@ 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);
|
||||
@@ -10,7 +10,7 @@ The lightweight framework for web-like apps
|
||||
/* jshint esversion: 6 */
|
||||
|
||||
|
||||
import {setTray, hideOverlay, showOverlay, updateTrayLabel, deleteTrayMenu} from "./store";
|
||||
import {setTray, hideOverlay, showOverlay, updateTrayLabel} from "./store";
|
||||
import {log} from "./log";
|
||||
|
||||
let websocket = null;
|
||||
@@ -154,11 +154,6 @@ function handleMessage(message) {
|
||||
let trayLabelData = JSON.parse(updateTrayLabelJSON)
|
||||
updateTrayLabel(trayLabelData)
|
||||
break
|
||||
case 'D':
|
||||
// Delete Tray Menu
|
||||
const id = trayMessage.slice(1);
|
||||
deleteTrayMenu(id)
|
||||
break
|
||||
default:
|
||||
log('Unknown tray message: ' + message.data);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ type Menu interface {
|
||||
UpdateApplicationMenu()
|
||||
UpdateContextMenu(contextMenu *menu.ContextMenu)
|
||||
SetTrayMenu(trayMenu *menu.TrayMenu)
|
||||
DeleteTrayMenu(trayMenu *menu.TrayMenu)
|
||||
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
|
||||
}
|
||||
|
||||
@@ -40,7 +39,3 @@ func (m *menuRuntime) SetTrayMenu(trayMenu *menu.TrayMenu) {
|
||||
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
|
||||
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
|
||||
}
|
||||
|
||||
func (m *menuRuntime) DeleteTrayMenu(trayMenu *menu.TrayMenu) {
|
||||
m.bus.Publish("menu:deletetraymenu", trayMenu)
|
||||
}
|
||||
|
||||
@@ -6,19 +6,20 @@ import (
|
||||
|
||||
// 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
|
||||
System System
|
||||
Menu Menu
|
||||
Store *StoreProvider
|
||||
Log Log
|
||||
bus *servicebus.ServiceBus
|
||||
Browser Browser
|
||||
Events Events
|
||||
Window Window
|
||||
Dialog Dialog
|
||||
System System
|
||||
Menu Menu
|
||||
Store *StoreProvider
|
||||
Log Log
|
||||
bus *servicebus.ServiceBus
|
||||
shutdownCallback func()
|
||||
}
|
||||
|
||||
// New creates a new runtime
|
||||
func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||
func New(serviceBus *servicebus.ServiceBus, shutdownCallback func()) *Runtime {
|
||||
result := &Runtime{
|
||||
Browser: newBrowser(),
|
||||
Events: newEvents(serviceBus),
|
||||
@@ -35,6 +36,11 @@ func New(serviceBus *servicebus.ServiceBus) *Runtime {
|
||||
|
||||
// Quit the application
|
||||
func (r *Runtime) Quit() {
|
||||
// Call back to user's shutdown method if defined
|
||||
if r.shutdownCallback != nil {
|
||||
r.shutdownCallback()
|
||||
}
|
||||
|
||||
// Start shutdown of Wails
|
||||
r.bus.Publish("quit", "runtime.Quit()")
|
||||
}
|
||||
|
||||
@@ -26,20 +26,25 @@ type Manager struct {
|
||||
ctx context.Context
|
||||
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
|
||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger) (*Manager, error) {
|
||||
func NewManager(ctx context.Context, cancel context.CancelFunc, bus *servicebus.ServiceBus, logger *logger.Logger, shutdownCallback func()) (*Manager, error) {
|
||||
|
||||
result := &Manager{
|
||||
bus: bus,
|
||||
logger: logger.CustomLogger("Event Manager"),
|
||||
signalchannel: make(chan os.Signal, 2),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
bus: bus,
|
||||
logger: logger.CustomLogger("Event Manager"),
|
||||
signalchannel: make(chan os.Signal, 2),
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
shutdownCallback: shutdownCallback,
|
||||
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -62,6 +67,11 @@ func (m *Manager) Start() {
|
||||
m.logger.Trace("Ctrl+C detected. Shutting down...")
|
||||
m.bus.Publish("quit", "ctrl-c pressed")
|
||||
|
||||
// Shutdown app first
|
||||
if m.shutdownCallback != nil {
|
||||
m.shutdownCallback()
|
||||
}
|
||||
|
||||
// Start shutdown of Wails
|
||||
m.cancel()
|
||||
|
||||
@@ -70,4 +80,5 @@ func (m *Manager) Start() {
|
||||
m.logger.Trace("Shutdown")
|
||||
m.wg.Done()
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
@@ -77,12 +77,6 @@ func (m *Menu) Start() error {
|
||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||
menuMessageType := splitTopic[1]
|
||||
switch menuMessageType {
|
||||
case "ontrayopen":
|
||||
trayID := menuMessage.Data().(string)
|
||||
m.menuManager.OnTrayMenuOpen(trayID)
|
||||
case "ontrayclose":
|
||||
trayID := menuMessage.Data().(string)
|
||||
m.menuManager.OnTrayMenuClose(trayID)
|
||||
case "clicked":
|
||||
if len(splitTopic) != 2 {
|
||||
m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
|
||||
@@ -143,17 +137,6 @@ func (m *Menu) Start() error {
|
||||
// Notify frontend of menu change
|
||||
m.bus.Publish("menufrontend:settraymenu", updatedMenu)
|
||||
|
||||
case "deletetraymenu":
|
||||
trayMenu := menuMessage.Data().(*menu.TrayMenu)
|
||||
trayID, err := m.menuManager.GetTrayID(trayMenu)
|
||||
if err != nil {
|
||||
m.logger.Trace("%s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Notify frontend of menu change
|
||||
m.bus.Publish("menufrontend:deletetraymenu", trayID)
|
||||
|
||||
case "updatetraymenulabel":
|
||||
trayMenu := menuMessage.Data().(*menu.TrayMenu)
|
||||
updatedLabel, err := m.menuManager.UpdateTrayMenuLabel(trayMenu)
|
||||
|
||||
@@ -34,13 +34,10 @@ type Runtime struct {
|
||||
|
||||
// Startup Hook
|
||||
startupOnce sync.Once
|
||||
|
||||
// Service bus
|
||||
bus *servicebus.ServiceBus
|
||||
}
|
||||
|
||||
// NewRuntime creates a new runtime subsystem
|
||||
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime)) (*Runtime, error) {
|
||||
func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, startupCallback func(*runtime.Runtime), shutdownCallback func()) (*Runtime, error) {
|
||||
|
||||
// Subscribe to log messages
|
||||
runtimeChannel, err := bus.Subscribe("runtime:")
|
||||
@@ -55,13 +52,13 @@ func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.
|
||||
}
|
||||
|
||||
result := &Runtime{
|
||||
runtimeChannel: runtimeChannel,
|
||||
hooksChannel: hooksChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: runtime.New(bus),
|
||||
startupCallback: startupCallback,
|
||||
ctx: ctx,
|
||||
bus: bus,
|
||||
runtimeChannel: runtimeChannel,
|
||||
hooksChannel: hooksChannel,
|
||||
logger: logger.CustomLogger("Runtime Subsystem"),
|
||||
runtime: runtime.New(bus, shutdownCallback),
|
||||
startupCallback: startupCallback,
|
||||
shutdownCallback: shutdownCallback,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
@@ -83,15 +80,7 @@ func (r *Runtime) Start() error {
|
||||
case "startup":
|
||||
if r.startupCallback != nil {
|
||||
r.startupOnce.Do(func() {
|
||||
go func() {
|
||||
r.startupCallback(r.runtime)
|
||||
|
||||
// If we got a url, publish it now startup completed
|
||||
url, ok := hooksMessage.Data().(string)
|
||||
if ok && len(url) > 0 {
|
||||
r.bus.Publish("url:handler", url)
|
||||
}
|
||||
}()
|
||||
go r.startupCallback(r.runtime)
|
||||
})
|
||||
} else {
|
||||
r.logger.Warning("no startup callback registered!")
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package subsystem
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
)
|
||||
|
||||
// URL is the URL Handler subsystem. It handles messages with topics starting
|
||||
// with "url:"
|
||||
type URL struct {
|
||||
urlChannel <-chan *servicebus.Message
|
||||
|
||||
// quit flag
|
||||
shouldQuit bool
|
||||
|
||||
// Logger!
|
||||
logger *logger.Logger
|
||||
|
||||
// Context for shutdown
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// internal waitgroup
|
||||
wg sync.WaitGroup
|
||||
|
||||
// Handlers
|
||||
handlers map[string]func(string)
|
||||
}
|
||||
|
||||
// NewURL creates a new log subsystem
|
||||
func NewURL(bus *servicebus.ServiceBus, logger *logger.Logger, handlers map[string]func(string)) (*URL, error) {
|
||||
|
||||
// Subscribe to log messages
|
||||
urlChannel, err := bus.Subscribe("url")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
result := &URL{
|
||||
urlChannel: urlChannel,
|
||||
logger: logger,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
handlers: handlers,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Start the subsystem
|
||||
func (u *URL) Start() error {
|
||||
|
||||
u.wg.Add(1)
|
||||
|
||||
// Spin off a go routine
|
||||
go func() {
|
||||
defer u.logger.Trace("URL Shutdown")
|
||||
|
||||
for u.shouldQuit == false {
|
||||
select {
|
||||
case <-u.ctx.Done():
|
||||
u.wg.Done()
|
||||
return
|
||||
case urlMessage := <-u.urlChannel:
|
||||
// Guard against nil messages
|
||||
if urlMessage == nil {
|
||||
continue
|
||||
}
|
||||
messageType := strings.TrimPrefix(urlMessage.Topic(), "url:")
|
||||
switch messageType {
|
||||
case "handler":
|
||||
url := urlMessage.Data().(string)
|
||||
splitURL := strings.Split(url, ":")
|
||||
protocol := splitURL[0]
|
||||
callback, ok := u.handlers[protocol]
|
||||
if ok {
|
||||
go callback(url)
|
||||
}
|
||||
default:
|
||||
u.logger.Error("unknown url message: %+v", urlMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *URL) Close() {
|
||||
u.cancel()
|
||||
u.wg.Wait()
|
||||
}
|
||||
@@ -17,14 +17,13 @@ func platformInfo() (*OS, error) {
|
||||
// Ignore errors as it isn't a showstopper
|
||||
key, _ := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
|
||||
defer key.Close()
|
||||
|
||||
fmt.Printf("%+v\n", key)
|
||||
|
||||
// Ignore errors as it isn't a showstopper
|
||||
productName, _, _ := key.GetStringValue("ProductName")
|
||||
currentBuild, _, _ := key.GetStringValue("CurrentBuildNumber")
|
||||
displayVersion, _, _ := key.GetStringValue("DisplayVersion")
|
||||
releaseId, _, _ := key.GetStringValue("ReleaseId")
|
||||
fmt.Println(productName)
|
||||
|
||||
result.Name = productName
|
||||
result.Version = fmt.Sprintf("%s (Build: %s)", releaseId, currentBuild)
|
||||
result.ID = displayVersion
|
||||
|
||||
return &result, key.Close()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -12,5 +12,10 @@ func (i *Info) discover() error {
|
||||
return err
|
||||
}
|
||||
i.OS = osinfo
|
||||
|
||||
// dll := syscall.MustLoadDLL("kernel32.dll")
|
||||
// p := dll.MustFindProc("GetVersion")
|
||||
// v, _, _ := p.Call()
|
||||
// fmt.Printf("Windows version %d.%d (Build %d)\n", byte(v), uint8(v>>8), uint16(v>>16))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
gofs "io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/debme"
|
||||
"github.com/leaanthony/gosod"
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
@@ -18,12 +16,6 @@ import (
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
//go:embed templates
|
||||
var templates embed.FS
|
||||
|
||||
//go:embed ides/*
|
||||
var ides embed.FS
|
||||
|
||||
// Cahce for the templates
|
||||
// We use this because we need different views of the same data
|
||||
var templateCache []Template = nil
|
||||
@@ -67,21 +59,20 @@ type Template struct {
|
||||
HelpURL string `json:"helpurl"`
|
||||
|
||||
// Other data
|
||||
FS gofs.FS `json:"-"`
|
||||
Directory string `json:"-"`
|
||||
}
|
||||
|
||||
func parseTemplate(template gofs.FS) (Template, error) {
|
||||
func parseTemplate(directory string) (Template, error) {
|
||||
templateJSON := filepath.Join(directory, "template.json")
|
||||
var result Template
|
||||
data, err := gofs.ReadFile(template, "template.json")
|
||||
data, err := ioutil.ReadFile(templateJSON)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
result.Directory = directory
|
||||
err = json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result.FS = template
|
||||
return result, nil
|
||||
return result, err
|
||||
}
|
||||
|
||||
// TemplateShortNames returns a slicer of short template names
|
||||
@@ -143,13 +134,11 @@ func getTemplateByShortname(shortname string) (Template, error) {
|
||||
// Loads the template cache
|
||||
func loadTemplateCache() error {
|
||||
|
||||
templatesFS, err := debme.FS(templates, "templates")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Get local template directory
|
||||
templateDir := fs.RelativePath("templates")
|
||||
|
||||
// Get directories
|
||||
files, err := templatesFS.ReadDir(".")
|
||||
files, err := ioutil.ReadDir(templateDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -159,11 +148,8 @@ func loadTemplateCache() error {
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
templateFS, err := templatesFS.FS(file.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template, err := parseTemplate(templateFS)
|
||||
templateDir := filepath.Join(templateDir, file.Name())
|
||||
template, err := parseTemplate(templateDir)
|
||||
if err != nil {
|
||||
// Cannot parse this template, continue
|
||||
continue
|
||||
@@ -177,6 +163,7 @@ func loadTemplateCache() error {
|
||||
|
||||
// Install the given template
|
||||
func Install(options *Options) error {
|
||||
|
||||
// Get cwd
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -224,16 +211,19 @@ func Install(options *Options) error {
|
||||
}
|
||||
|
||||
// Use Gosod to install the template
|
||||
installer := gosod.New(template.FS)
|
||||
installer, err := gosod.TemplateDir(template.Directory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ignore template.json files
|
||||
installer.IgnoreFile("template.json")
|
||||
installer.IgnoreFilename("template.json")
|
||||
|
||||
// Setup the data.
|
||||
// We use the directory name for the binary name, like Go
|
||||
BinaryName := filepath.Base(options.TargetDir)
|
||||
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
|
||||
localWailsDirectory := fs.RelativePath("../../../../../..")
|
||||
localWailsDirectory := fs.RelativePath("../..")
|
||||
templateData := &Data{
|
||||
ProjectName: options.ProjectName,
|
||||
BinaryName: filepath.Base(options.TargetDir),
|
||||
@@ -305,14 +295,14 @@ func generateIDEFiles(options *Options) error {
|
||||
func generateVSCodeFiles(options *Options) error {
|
||||
|
||||
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
||||
source, err := debme.FS(ides, "ides/vscode")
|
||||
sourceDir := fs.RelativePath(filepath.Join("./ides/vscode"))
|
||||
|
||||
// Use Gosod to install the template
|
||||
installer, err := gosod.TemplateDir(sourceDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use gosod to install the template
|
||||
installer := gosod.New(source)
|
||||
|
||||
binaryName := filepath.Base(options.TargetDir)
|
||||
if runtime.GOOS == "windows" {
|
||||
// yay windows
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Angular",
|
||||
"shortname": "angular",
|
||||
"author": "bh90210 <ktc@pm.me>",
|
||||
"description": "Angular projects w/ @angular/cli - Note: in order to reach the cli use npx like this: npx ng"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "React JS",
|
||||
"shortname": "react",
|
||||
"author": "bh90210 <ktc@pm.me>",
|
||||
"description": "Create React App v3 standar tooling"
|
||||
}
|
||||
@@ -11,20 +11,21 @@ type Basic struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// NewBasic creates a new Basic application struct
|
||||
func NewBasic() *Basic {
|
||||
// newBasic creates a new Basic application struct
|
||||
func newBasic() *Basic {
|
||||
return &Basic{}
|
||||
}
|
||||
|
||||
// startup is called at application startup
|
||||
func (b *Basic) startup(runtime *wails.Runtime) {
|
||||
// WailsInit is called at application startup
|
||||
func (b *Basic) WailsInit(runtime *wails.Runtime) error {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
return nil
|
||||
}
|
||||
|
||||
// shutdown is called at application termination
|
||||
func (b *Basic) shutdown() {
|
||||
// WailsShutdown is called at application termination
|
||||
func (b *Basic) WailsShutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
4
v2/internal/templates/templates/svelte-mui/frontend/.gitignore
vendored
Normal file
4
v2/internal/templates/templates/svelte-mui/frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules/
|
||||
/public/build/
|
||||
|
||||
.DS_Store
|
||||
@@ -0,0 +1,93 @@
|
||||
*Looking for a shareable component template? Go here --> [sveltejs/component-template](https://github.com/sveltejs/component-template)*
|
||||
|
||||
---
|
||||
|
||||
# svelte app
|
||||
|
||||
This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template.
|
||||
|
||||
To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit):
|
||||
|
||||
```bash
|
||||
npx degit sveltejs/template svelte-app
|
||||
cd svelte-app
|
||||
```
|
||||
|
||||
*Note that you will need to have [Node.js](https://nodejs.org) installed.*
|
||||
|
||||
|
||||
## Get started
|
||||
|
||||
Install the dependencies...
|
||||
|
||||
```bash
|
||||
cd svelte-app
|
||||
npm install
|
||||
```
|
||||
|
||||
...then start [Rollup](https://rollupjs.org):
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Navigate to [localhost:5000](http://localhost:5000). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes.
|
||||
|
||||
By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`.
|
||||
|
||||
|
||||
## Building and running in production mode
|
||||
|
||||
To create an optimised version of the app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com).
|
||||
|
||||
|
||||
## Single-page app mode
|
||||
|
||||
By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere.
|
||||
|
||||
If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json:
|
||||
|
||||
```js
|
||||
"start": "sirv public --single"
|
||||
```
|
||||
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [now](https://zeit.co/now)
|
||||
|
||||
Install `now` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
cd public
|
||||
now deploy --name my-project
|
||||
```
|
||||
|
||||
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g surge
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
surge public my-project.surge.sh
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "npx rollup -c",
|
||||
"dev": "npx rollup -c -w",
|
||||
"start": "npx sirv public",
|
||||
"start:dev": "npx sirv public --single --host 0.0.0.0 --dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||
"focus-visible": "^5.2.0",
|
||||
"rollup": "^2.35.1",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"svelte": "^3.31.1",
|
||||
"svelte-mui": "^0.3.3-5"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^0.4.4"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<meta name="apple-mobile-web-app-title" content="svelte-mui" />
|
||||
<meta name="application-name" content="svelte-mui" />
|
||||
<meta name="theme-color" content="#212121" />
|
||||
|
||||
<title>Svelte app</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='stylesheet' href='/bundle.css'>
|
||||
|
||||
<script defer src='/bundle.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>Please enable JavaScript.</noscript>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,71 @@
|
||||
import svelte from 'rollup-plugin-svelte';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css: css => {
|
||||
css.write('public/bundle.css');
|
||||
}
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
// some cases you'll need additional configuration -
|
||||
// consult the documentation for details:
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte']
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
|
||||
// Watch the `public` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
|
||||
function serve() {
|
||||
let started = false;
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
|
||||
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<h1>Hello {name}!</h1>
|
||||
|
||||
<Snackbar bind:visible bg="#f44336">
|
||||
{response}
|
||||
<span slot="action">
|
||||
<Button color="#ff0" on:click={() => (visible = false)}>Close</Button>
|
||||
</span>
|
||||
</Snackbar>
|
||||
|
||||
<Textfield
|
||||
autocomplete="off"
|
||||
label="Message"
|
||||
required
|
||||
bind:value
|
||||
message=""
|
||||
/>
|
||||
|
||||
<Button
|
||||
outlined
|
||||
shaped
|
||||
color="Red"
|
||||
on:click={() => {
|
||||
window.backend.main.Basic.Greet(value).then((result) => {
|
||||
response = result;
|
||||
visible = true;
|
||||
});
|
||||
}}
|
||||
>
|
||||
Hello
|
||||
</Button>
|
||||
|
||||
|
||||
<script>
|
||||
export let name;
|
||||
let value = '';
|
||||
let response = '';
|
||||
|
||||
// optional import focus-visible polyfill only once
|
||||
import 'focus-visible';
|
||||
// import any components
|
||||
import { Button, Checkbox, Snackbar, Textfield } from 'svelte-mui';
|
||||
|
||||
let checked = true;
|
||||
let visible = false;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
h1 {
|
||||
color: purple;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,10 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.body,
|
||||
props: {
|
||||
name: 'Wails User'
|
||||
}
|
||||
});
|
||||
|
||||
export default app;
|
||||
@@ -1,6 +1,6 @@
|
||||
module test
|
||||
|
||||
go 1.16
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
||||
22
v2/internal/templates/templates/svelte-mui/main.tmpl.go
Normal file
22
v2/internal/templates/templates/svelte-mui/main.tmpl.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create application with options
|
||||
app, err := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.Bind(newBasic())
|
||||
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
7
v2/internal/templates/templates/svelte-mui/template.json
Normal file
7
v2/internal/templates/templates/svelte-mui/template.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Svelte + MUI3",
|
||||
"shortname": "svelte-mui",
|
||||
"author": "Travis McLane <travis.mclane@gmail.com>",
|
||||
"description": "A simple template using Svelte + Mui3",
|
||||
"helpurl": "https://wails.app/help/templates/svelte-mui3"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "{{.ProjectName}}",
|
||||
"outputfilename": "{{.BinaryName}}",
|
||||
"html": "frontend/public/index.html",
|
||||
"js": "frontend/public/bundle.js",
|
||||
"css": "frontend/public/bundle.css",
|
||||
"frontend:build": "npm run build",
|
||||
"frontend:install": "npm install"
|
||||
}
|
||||
35
v2/internal/templates/templates/vanilla/basic.tmpl.go
Normal file
35
v2/internal/templates/templates/vanilla/basic.tmpl.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// Basic application struct
|
||||
type Basic struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// newBasic creates a new Basic application struct
|
||||
func newBasic() *Basic {
|
||||
return &Basic{}
|
||||
}
|
||||
|
||||
// WailsInit is called at application startup
|
||||
func (b *Basic) WailsInit(runtime *wails.Runtime) error {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
return nil
|
||||
}
|
||||
|
||||
// WailsShutdown is called at application termination
|
||||
func (b *Basic) WailsShutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *Basic) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<link rel="stylesheet" href="/main.css">
|
||||
</head>
|
||||
|
||||
<body data-wails-drag>
|
||||
<body>
|
||||
<div id="logo"></div>
|
||||
<div id="input">
|
||||
<input id="name" type="text"></input>
|
||||
18
v2/internal/templates/templates/vanilla/frontend/main.css
Normal file
18
v2/internal/templates/templates/vanilla/frontend/main.css
Normal file
File diff suppressed because one or more lines are too long
@@ -1,12 +1,12 @@
|
||||
// Get input + focus
|
||||
let nameElement = document.getElementById("name");
|
||||
var nameElement = document.getElementById("name");
|
||||
nameElement.focus();
|
||||
|
||||
// Setup the greet function
|
||||
// Stup the greet function
|
||||
window.greet = function () {
|
||||
|
||||
// Get name
|
||||
let name = nameElement.value;
|
||||
var name = nameElement.value;
|
||||
|
||||
// Call Basic.Greet(name)
|
||||
window.backend.main.Basic.Greet(name).then((result) => {
|
||||
9
v2/internal/templates/templates/vanilla/go.mod.tmpl
Normal file
9
v2/internal/templates/templates/vanilla/go.mod.tmpl
Normal file
@@ -0,0 +1,9 @@
|
||||
module test
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
||||
)
|
||||
|
||||
replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
|
||||
22
v2/internal/templates/templates/vanilla/main.tmpl.go
Normal file
22
v2/internal/templates/templates/vanilla/main.tmpl.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/v2"
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Create application with options
|
||||
app, err := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
app.Bind(newBasic())
|
||||
|
||||
err = app.Run()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "{{.ProjectName}}",
|
||||
"outputfilename": "{{.BinaryName}}",
|
||||
"html": "frontend/src/index.html",
|
||||
"html": "frontend/index.html",
|
||||
"author": {
|
||||
"name": "{{.AuthorName}}",
|
||||
"email": "{{.AuthorEmail}}"
|
||||
6
v2/internal/templates/templates/vuebasic/template.json
Normal file
6
v2/internal/templates/templates/vuebasic/template.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Vue2/Webpack Basic",
|
||||
"shortname": "vue",
|
||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
||||
"description": "A basic template using Vue 2 and bundled using Webpack 4"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Vuetify1.5/Webpack Basic",
|
||||
"shortname": "vuetify1",
|
||||
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||
"description": "Basic template using Vuetify v1.5 and bundled using Webpack"
|
||||
}
|
||||
35
v2/internal/templates/templates/vuetify2-basic/basic.tmpl.go
Normal file
35
v2/internal/templates/templates/vuetify2-basic/basic.tmpl.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2"
|
||||
)
|
||||
|
||||
// Basic application struct
|
||||
type Basic struct {
|
||||
runtime *wails.Runtime
|
||||
}
|
||||
|
||||
// newBasic creates a new Basic application struct
|
||||
func newBasic() *Basic {
|
||||
return &Basic{}
|
||||
}
|
||||
|
||||
// WailsInit is called at application startup
|
||||
func (b *Basic) WailsInit(runtime *wails.Runtime) error {
|
||||
// Perform your setup here
|
||||
b.runtime = runtime
|
||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
||||
return nil
|
||||
}
|
||||
|
||||
// WailsShutdown is called at application termination
|
||||
func (b *Basic) WailsShutdown() {
|
||||
// Perform your teardown here
|
||||
}
|
||||
|
||||
// Greet returns a greeting for the given name
|
||||
func (b *Basic) Greet(name string) string {
|
||||
return fmt.Sprintf("Hello %s!", name)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
[ '@vue/app', { useBuiltIns: 'entry' } ]
|
||||
]
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "vuetify2",
|
||||
"author": "{{.AuthorNameAndEmail}}",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.1",
|
||||
"regenerator-runtime": "^0.13.3",
|
||||
"vue": "^2.5.22",
|
||||
"vuetify": "^2.2.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdi/font": "^4.3.95",
|
||||
"@vue/cli-plugin-babel": "^3.4.0",
|
||||
"@vue/cli-plugin-eslint": "^3.4.0",
|
||||
"@vue/cli-service": "^3.4.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eslint": "^5.8.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"eventsource-polyfill": "^0.9.6",
|
||||
"vue-template-compiler": "^2.5.21",
|
||||
"webpack-hot-middleware": "^2.24.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
"autoprefixer": {}
|
||||
}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><title>Wails</title></head><body><div id=app></div></body></html>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<v-app id="inspire">
|
||||
<v-navigation-drawer v-model="drawer" clipped fixed app>
|
||||
<v-list dense>
|
||||
<v-list-item>
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-view-dashboard</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Dashboard</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item-icon>
|
||||
<v-icon>mdi-settings</v-icon>
|
||||
</v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>Settings</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-navigation-drawer>
|
||||
<v-app-bar app fixed clipped-left>
|
||||
<v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
|
||||
<v-toolbar-title>Application</v-toolbar-title>
|
||||
</v-app-bar>
|
||||
<v-content>
|
||||
<v-container fluid class="px-0">
|
||||
<v-layout justify-center align-center class="px-0">
|
||||
<hello-world></hello-world>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-content>
|
||||
<v-footer app fixed>
|
||||
<span style="margin-left:1em">© You</span>
|
||||
</v-footer>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HelloWorld from "./components/HelloWorld.vue"
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
drawer: false
|
||||
}),
|
||||
components: {
|
||||
HelloWorld
|
||||
},
|
||||
props: {
|
||||
source: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.logo {
|
||||
width: 16em;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<v-container fluid class="px-0">
|
||||
<v-layout>
|
||||
<v-flex xs12 sm6 offset-sm3>
|
||||
<v-card raised="raised" class="pa-4 ma-4">
|
||||
<v-layout justify-center align-center class="pa-4 ma-4">
|
||||
<v-img :src="require('../assets/images/logo.png')"></v-img>
|
||||
</v-layout>
|
||||
<v-card-actions>
|
||||
<v-layout justify-center align-center class="px-0">
|
||||
<v-btn color="blue" @click="getMessage">Press Me</v-btn>
|
||||
</v-layout>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<div class="text-xs-center">
|
||||
<v-dialog v-model="dialog" width="500">
|
||||
<v-card>
|
||||
<v-card-title class="headline" primary-title>Message from Go</v-card-title>
|
||||
<v-card-text>{{message}}</v-card-text>
|
||||
<v-divider></v-divider>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn color="primary" text @click="dialog = false">Awesome</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
message: " ",
|
||||
raised: true,
|
||||
dialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMessage: function () {
|
||||
var self = this
|
||||
window.backend.main.Basic.Greet("Hello from Go!").then(result => {
|
||||
self.message = result
|
||||
self.dialog = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h1 {
|
||||
margin-top: 2em;
|
||||
position: relative;
|
||||
min-height: 5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
font-size: 1.7em;
|
||||
border-color: blue;
|
||||
background-color: blue;
|
||||
color: white;
|
||||
border: 3px solid white;
|
||||
border-radius: 10px;
|
||||
padding: 9px;
|
||||
cursor: pointer;
|
||||
transition: 500ms;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 1.7em;
|
||||
border-color: white;
|
||||
background-color: #121212;
|
||||
color: white;
|
||||
border: 3px solid white;
|
||||
border-radius: 10px;
|
||||
padding: 9px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
import '@mdi/font/css/materialdesignicons.css';
|
||||
import Vue from 'vue';
|
||||
import Vuetify from 'vuetify';
|
||||
import 'vuetify/dist/vuetify.min.css';
|
||||
|
||||
Vue.use(Vuetify);
|
||||
|
||||
import App from './App.vue';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.config.devtools = true;
|
||||
|
||||
Wails.Init(() => {
|
||||
new Vue({
|
||||
vuetify: new Vuetify({
|
||||
icons: {
|
||||
iconfont: 'mdi'
|
||||
},
|
||||
theme: {
|
||||
dark: true
|
||||
}
|
||||
}),
|
||||
render: h => h(App)
|
||||
}).$mount('#app');
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user