mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 04:05:12 -07:00
Compare commits
25 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8caf277bf1 | ||
|
|
4b480bb085 | ||
|
|
c1d63aff34 | ||
|
|
5fc89c4cad | ||
|
|
5e96bb5a32 | ||
|
|
c5e76c50b0 | ||
|
|
e40226ff7a | ||
|
|
53a3638fa8 | ||
|
|
1344638c52 | ||
|
|
6fdc87454a | ||
|
|
c36f9501a9 | ||
|
|
c23b43c352 | ||
|
|
a76851463b | ||
|
|
e17b432c8f | ||
|
|
5d444cd6dd | ||
|
|
be43049fc6 | ||
|
|
2e01710412 | ||
|
|
1b0193161c | ||
|
|
1b377fb575 | ||
|
|
1203ae64b8 | ||
|
|
26ed8002b9 | ||
|
|
cf23bffc67 | ||
|
|
d70f6fffe7 | ||
|
|
54c99fc386 | ||
|
|
86c1ea5e6a |
@@ -24,10 +24,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
|
|
||||||
command := app.NewSubCommand("build", "Builds the application")
|
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
|
// Setup production flag
|
||||||
production := false
|
production := false
|
||||||
command.BoolFlag("production", "Build in production mode", &production)
|
command.BoolFlag("production", "Build in production mode", &production)
|
||||||
@@ -39,6 +35,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
compilerCommand := "go"
|
compilerCommand := "go"
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||||
|
|
||||||
|
compress := false
|
||||||
|
command.BoolFlag("compress", "Compress final binary", &compress)
|
||||||
|
|
||||||
// Setup Platform flag
|
// Setup Platform flag
|
||||||
platform := runtime.GOOS
|
platform := runtime.GOOS
|
||||||
command.StringFlag("platform", "Platform to target", &platform)
|
command.StringFlag("platform", "Platform to target", &platform)
|
||||||
@@ -51,9 +50,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
ldflags := ""
|
ldflags := ""
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
||||||
|
|
||||||
// Log to file
|
// tags to pass to `go`
|
||||||
//logFile := ""
|
tags := ""
|
||||||
//command.StringFlag("l", "Log to file", &logFile)
|
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
|
||||||
|
|
||||||
// Retain assets
|
// Retain assets
|
||||||
keepAssets := false
|
keepAssets := false
|
||||||
@@ -67,11 +66,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
cleanBuildDirectory := false
|
cleanBuildDirectory := false
|
||||||
command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory)
|
command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory)
|
||||||
|
|
||||||
appleIdentity := ""
|
|
||||||
if runtime.GOOS == "darwin" {
|
|
||||||
command.StringFlag("sign", "Signs your app with the given identity.", &appleIdentity)
|
|
||||||
}
|
|
||||||
|
|
||||||
command.Action(func() error {
|
command.Action(func() error {
|
||||||
|
|
||||||
quiet := verbosity == 0
|
quiet := verbosity == 0
|
||||||
@@ -89,11 +83,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
app.PrintBanner()
|
app.PrintBanner()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure package is used with apple identity
|
|
||||||
if appleIdentity != "" && pack == false {
|
|
||||||
return fmt.Errorf("must use `-package` flag when using `-sign`")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup mode
|
// Setup mode
|
||||||
mode := build.Debug
|
mode := build.Debug
|
||||||
if production {
|
if production {
|
||||||
@@ -114,6 +103,20 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
return fmt.Errorf("platform %s is not supported", 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
|
// Create BuildOptions
|
||||||
buildOptions := &build.Options{
|
buildOptions := &build.Options{
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
@@ -125,8 +128,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
LDFlags: ldflags,
|
LDFlags: ldflags,
|
||||||
Compiler: compilerCommand,
|
Compiler: compilerCommand,
|
||||||
KeepAssets: keepAssets,
|
KeepAssets: keepAssets,
|
||||||
AppleIdentity: appleIdentity,
|
|
||||||
Verbosity: verbosity,
|
Verbosity: verbosity,
|
||||||
|
Compress: compress,
|
||||||
|
UserTags: userTags,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate platform and arch
|
// Calculate platform and arch
|
||||||
@@ -147,15 +151,18 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write out the system information
|
// Write out the system information
|
||||||
|
fmt.Fprintf(w, "\n")
|
||||||
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
||||||
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
||||||
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
||||||
fmt.Fprintf(w, "Compiler: \t%s\n", buildOptions.Compiler)
|
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, "Build Mode: \t%s\n", buildModeText)
|
||||||
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
||||||
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
||||||
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
||||||
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
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 {
|
if len(buildOptions.OutputFile) > 0 {
|
||||||
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Exit early if PM not found
|
// Exit early if PM not found
|
||||||
if info.PM == nil {
|
if info.PM == nil {
|
||||||
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
|
|
||||||
w.Flush()
|
w.Flush()
|
||||||
println()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
"github.com/wailsapp/wails/v2/pkg/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
// AddSubcommand adds the `generate` command for the Wails application
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||||
|
|
||||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
command := app.NewSubCommand("generate", "Code Generation Tools")
|
||||||
@@ -19,7 +19,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|||||||
|
|
||||||
// Quiet Init
|
// Quiet Init
|
||||||
quiet := false
|
quiet := false
|
||||||
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
|
backendAPI.BoolFlag("q", "Suppress output to console", &quiet)
|
||||||
|
|
||||||
backendAPI.Action(func() error {
|
backendAPI.Action(func() error {
|
||||||
|
|
||||||
@@ -85,7 +85,4 @@ func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.Println("")
|
logger.Println("")
|
||||||
|
|
||||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
|
||||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/wailsapp/wails/v2/internal/templates"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/git"
|
"github.com/wailsapp/wails/v2/pkg/git"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
gofs "io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/debme"
|
||||||
"github.com/leaanthony/gosod"
|
"github.com/leaanthony/gosod"
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
@@ -16,6 +18,12 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
"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
|
// Cahce for the templates
|
||||||
// We use this because we need different views of the same data
|
// We use this because we need different views of the same data
|
||||||
var templateCache []Template = nil
|
var templateCache []Template = nil
|
||||||
@@ -59,20 +67,21 @@ type Template struct {
|
|||||||
HelpURL string `json:"helpurl"`
|
HelpURL string `json:"helpurl"`
|
||||||
|
|
||||||
// Other data
|
// Other data
|
||||||
Directory string `json:"-"`
|
FS gofs.FS `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseTemplate(directory string) (Template, error) {
|
func parseTemplate(template gofs.FS) (Template, error) {
|
||||||
templateJSON := filepath.Join(directory, "template.json")
|
|
||||||
var result Template
|
var result Template
|
||||||
data, err := ioutil.ReadFile(templateJSON)
|
data, err := gofs.ReadFile(template, "template.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Directory = directory
|
|
||||||
err = json.Unmarshal(data, &result)
|
err = json.Unmarshal(data, &result)
|
||||||
return result, err
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result.FS = template
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TemplateShortNames returns a slicer of short template names
|
// TemplateShortNames returns a slicer of short template names
|
||||||
@@ -134,11 +143,13 @@ func getTemplateByShortname(shortname string) (Template, error) {
|
|||||||
// Loads the template cache
|
// Loads the template cache
|
||||||
func loadTemplateCache() error {
|
func loadTemplateCache() error {
|
||||||
|
|
||||||
// Get local template directory
|
templatesFS, err := debme.FS(templates, "templates")
|
||||||
templateDir := fs.RelativePath("templates")
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Get directories
|
// Get directories
|
||||||
files, err := ioutil.ReadDir(templateDir)
|
files, err := templatesFS.ReadDir(".")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -148,8 +159,11 @@ func loadTemplateCache() error {
|
|||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
if file.IsDir() {
|
if file.IsDir() {
|
||||||
templateDir := filepath.Join(templateDir, file.Name())
|
templateFS, err := templatesFS.FS(file.Name())
|
||||||
template, err := parseTemplate(templateDir)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
template, err := parseTemplate(templateFS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Cannot parse this template, continue
|
// Cannot parse this template, continue
|
||||||
continue
|
continue
|
||||||
@@ -163,7 +177,6 @@ func loadTemplateCache() error {
|
|||||||
|
|
||||||
// Install the given template
|
// Install the given template
|
||||||
func Install(options *Options) error {
|
func Install(options *Options) error {
|
||||||
|
|
||||||
// Get cwd
|
// Get cwd
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -211,19 +224,16 @@ func Install(options *Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use Gosod to install the template
|
// Use Gosod to install the template
|
||||||
installer, err := gosod.TemplateDir(template.Directory)
|
installer := gosod.New(template.FS)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore template.json files
|
// Ignore template.json files
|
||||||
installer.IgnoreFilename("template.json")
|
installer.IgnoreFile("template.json")
|
||||||
|
|
||||||
// Setup the data.
|
// Setup the data.
|
||||||
// We use the directory name for the binary name, like Go
|
// We use the directory name for the binary name, like Go
|
||||||
BinaryName := filepath.Base(options.TargetDir)
|
BinaryName := filepath.Base(options.TargetDir)
|
||||||
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
|
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
|
||||||
localWailsDirectory := fs.RelativePath("../..")
|
localWailsDirectory := fs.RelativePath("../../../../../..")
|
||||||
templateData := &Data{
|
templateData := &Data{
|
||||||
ProjectName: options.ProjectName,
|
ProjectName: options.ProjectName,
|
||||||
BinaryName: filepath.Base(options.TargetDir),
|
BinaryName: filepath.Base(options.TargetDir),
|
||||||
@@ -295,14 +305,14 @@ func generateIDEFiles(options *Options) error {
|
|||||||
func generateVSCodeFiles(options *Options) error {
|
func generateVSCodeFiles(options *Options) error {
|
||||||
|
|
||||||
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
||||||
sourceDir := fs.RelativePath(filepath.Join("./ides/vscode"))
|
source, err := debme.FS(ides, "ides/vscode")
|
||||||
|
|
||||||
// Use Gosod to install the template
|
|
||||||
installer, err := gosod.TemplateDir(sourceDir)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use gosod to install the template
|
||||||
|
installer := gosod.New(source)
|
||||||
|
|
||||||
binaryName := filepath.Base(options.TargetDir)
|
binaryName := filepath.Base(options.TargetDir)
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// yay windows
|
// yay windows
|
||||||
@@ -11,7 +11,7 @@ type Basic struct {
|
|||||||
runtime *wails.Runtime
|
runtime *wails.Runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBasic creates a new Basic application struct
|
// NewBasic creates a new Basic application struct
|
||||||
func NewBasic() *Basic {
|
func NewBasic() *Basic {
|
||||||
return &Basic{}
|
return &Basic{}
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,7 @@ button {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
outline: none;
|
outline: none;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
// Get input + focus
|
// Get input + focus
|
||||||
var nameElement = document.getElementById("name");
|
let nameElement = document.getElementById("name");
|
||||||
nameElement.focus();
|
nameElement.focus();
|
||||||
|
|
||||||
// Stup the greet function
|
// Setup the greet function
|
||||||
window.greet = function () {
|
window.greet = function () {
|
||||||
|
|
||||||
// Get name
|
// Get name
|
||||||
var name = nameElement.value;
|
let name = nameElement.value;
|
||||||
|
|
||||||
// Call Basic.Greet(name)
|
// Call Basic.Greet(name)
|
||||||
window.backend.main.Basic.Greet(name).then((result) => {
|
window.backend.main.Basic.Greet(name).then((result) => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module test
|
module test
|
||||||
|
|
||||||
go 1.13
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "{{.ProjectName}}",
|
"name": "{{.ProjectName}}",
|
||||||
"outputfilename": "{{.BinaryName}}",
|
"outputfilename": "{{.BinaryName}}",
|
||||||
"html": "frontend/index.html",
|
"html": "frontend/src/index.html",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "{{.AuthorName}}",
|
"name": "{{.AuthorName}}",
|
||||||
"email": "{{.AuthorEmail}}"
|
"email": "{{.AuthorEmail}}"
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -67,5 +67,6 @@ func main() {
|
|||||||
err = app.Run()
|
err = app.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println("\n\nERROR: " + err.Error())
|
println("\n\nERROR: " + err.Error())
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.60"
|
var version = "v2.0.0-alpha.65"
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/Masterminds/semver v1.5.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/gorilla/websocket v1.4.1
|
github.com/gorilla/websocket v1.4.1
|
||||||
github.com/imdario/mergo v0.3.11
|
github.com/imdario/mergo v0.3.11
|
||||||
github.com/jackmordaunt/icns v1.0.0
|
github.com/jackmordaunt/icns v1.0.0
|
||||||
github.com/leaanthony/clir v1.0.4
|
github.com/leaanthony/clir v1.0.4
|
||||||
github.com/leaanthony/gosod v0.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/slicer v1.5.0
|
github.com/leaanthony/slicer v1.5.0
|
||||||
github.com/matryer/is v1.4.0
|
github.com/matryer/is v1.4.0
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||||
@@ -42,8 +41,12 @@ github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eT
|
|||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
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 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
github.com/leaanthony/debme v1.1.1 h1:2CQjJkfrjr4b2VgpDmkq2aghem5R2bNbg1Yg5cKQGBQ=
|
||||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
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/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
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/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
|||||||
@@ -14,30 +14,6 @@ import (
|
|||||||
"github.com/leaanthony/slicer"
|
"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
|
//go:embed assets/package.json
|
||||||
var packageJSON []byte
|
var packageJSON []byte
|
||||||
|
|
||||||
@@ -100,6 +76,8 @@ const backend = {`)
|
|||||||
}
|
}
|
||||||
returnType += ">"
|
returnType += ">"
|
||||||
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
||||||
|
} else {
|
||||||
|
returnType = "Promise<void>"
|
||||||
}
|
}
|
||||||
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
||||||
output.WriteString(" */\n")
|
output.WriteString(" */\n")
|
||||||
@@ -125,13 +103,14 @@ const backend = {`)
|
|||||||
export default backend;`)
|
export default backend;`)
|
||||||
output.WriteString("\n")
|
output.WriteString("\n")
|
||||||
|
|
||||||
|
// TODO: Make this configurable in wails.json
|
||||||
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !fs.DirExists(dirname) {
|
if !fs.DirExists(dirname) {
|
||||||
err := fs.Mkdir(dirname)
|
err := fs.MkDirs(dirname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ struct hashmap_s dialogIconCache;
|
|||||||
// Dispatch Method
|
// Dispatch Method
|
||||||
typedef void (^dispatchMethod)(void);
|
typedef void (^dispatchMethod)(void);
|
||||||
|
|
||||||
|
// Message Dialog
|
||||||
|
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);
|
||||||
|
|
||||||
TrayMenuStore *TrayMenuStoreSingleton;
|
TrayMenuStore *TrayMenuStoreSingleton;
|
||||||
|
|
||||||
// dispatch will execute the given `func` pointer
|
// dispatch will execute the given `func` pointer
|
||||||
@@ -80,6 +83,11 @@ void ShowMouse() {
|
|||||||
msg_reg(c("NSCursor"), s("unhide"));
|
msg_reg(c("NSCursor"), s("unhide"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OSVersion getOSVersion() {
|
||||||
|
id processInfo = msg_reg(c("NSProcessInfo"), s("processInfo"));
|
||||||
|
return GET_OSVERSION(processInfo);
|
||||||
|
}
|
||||||
|
|
||||||
struct Application {
|
struct Application {
|
||||||
|
|
||||||
// Cocoa data
|
// Cocoa data
|
||||||
@@ -295,7 +303,11 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
|||||||
const char *name = (const char *)msg_reg(msg_reg(message, s("name")), s("UTF8String"));
|
const char *name = (const char *)msg_reg(msg_reg(message, s("name")), s("UTF8String"));
|
||||||
if( strcmp(name, "error") == 0 ) {
|
if( strcmp(name, "error") == 0 ) {
|
||||||
printf("There was a Javascript error. Please open the devtools for more information.\n");
|
printf("There was a Javascript error. Please open the devtools for more information.\n");
|
||||||
Show(app);
|
// Show app if we are in debug mode
|
||||||
|
if( debug ) {
|
||||||
|
Show(app);
|
||||||
|
MessageDialog(app, "", "error", "Javascript Error", "There was a Javascript error. Please open the devtools for more information.", "", "", "", "","","","");
|
||||||
|
}
|
||||||
} else if( strcmp(name, "completed") == 0) {
|
} else if( strcmp(name, "completed") == 0) {
|
||||||
// Delete handler
|
// Delete handler
|
||||||
msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
msg_id(app->manager, s("removeScriptMessageHandlerForName:"), str("completed"));
|
||||||
@@ -691,7 +703,7 @@ void processDialogButton(id alert, char *buttonTitle, char *cancelButton, char *
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
@@ -791,18 +803,20 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
|
|||||||
buttonPressed = button4;
|
buttonPressed = button4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct callback message. Format "DM<callbackID>|<selected button index>"
|
if ( STR_HAS_CHARS(callbackID) ) {
|
||||||
const char *callback = concat("DM", callbackID);
|
// Construct callback message. Format "DM<callbackID>|<selected button index>"
|
||||||
const char *header = concat(callback, "|");
|
const char *callback = concat("DM", callbackID);
|
||||||
const char *responseMessage = concat(header, buttonPressed);
|
const char *header = concat(callback, "|");
|
||||||
|
const char *responseMessage = concat(header, buttonPressed);
|
||||||
|
|
||||||
// Send message to backend
|
// Send message to backend
|
||||||
app->sendMessageToBackend(responseMessage);
|
app->sendMessageToBackend(responseMessage);
|
||||||
|
|
||||||
// Free memory
|
// Free memory
|
||||||
MEMFREE(header);
|
MEMFREE(header);
|
||||||
MEMFREE(callback);
|
MEMFREE(callback);
|
||||||
MEMFREE(responseMessage);
|
MEMFREE(responseMessage);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1049,22 +1063,26 @@ void SetDebug(void *applicationPointer, int flag) {
|
|||||||
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
void AddContextMenu(struct Application *app, const char *contextMenuJSON) {
|
||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
ON_MAIN_THREAD (
|
||||||
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
AddContextMenuToStore(app->contextMenuStore, contextMenuJSON);
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
void UpdateContextMenu(struct Application *app, const char* contextMenuJSON) {
|
||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
ON_MAIN_THREAD(
|
||||||
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
UpdateContextMenuInStore(app->contextMenuStore, contextMenuJSON);
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
AddTrayMenuToStore(TrayMenuStoreSingleton, trayMenuJSON);
|
ON_MAIN_THREAD(
|
||||||
|
AddTrayMenuToStore(TrayMenuStoreSingleton, trayMenuJSON);
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||||
@@ -1407,7 +1425,9 @@ void SetApplicationMenu(struct Application *app, const char *menuAsJSON) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update menu
|
// Update menu
|
||||||
updateMenu(app, menuAsJSON);
|
ON_MAIN_THREAD (
|
||||||
|
updateMenu(app, menuAsJSON);
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) {
|
void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) {
|
||||||
@@ -1692,9 +1712,6 @@ void HasURLHandlers(struct Application* app) {
|
|||||||
void Quit(struct Application *app) {
|
void Quit(struct Application *app) {
|
||||||
Debug(app, "Quit Called");
|
Debug(app, "Quit Called");
|
||||||
msg_id(app->application, s("stop:"), NULL);
|
msg_id(app->application, s("stop:"), NULL);
|
||||||
SetSize(app, 0, 0);
|
|
||||||
Show(app);
|
|
||||||
Hide(app);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id createImageFromBase64Data(const char *data, bool isTemplateImage) {
|
id createImageFromBase64Data(const char *data, bool isTemplateImage) {
|
||||||
|
|||||||
@@ -11,6 +11,12 @@
|
|||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "stdlib.h"
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long maj;
|
||||||
|
long min;
|
||||||
|
long patch;
|
||||||
|
} OSVersion;
|
||||||
|
|
||||||
// Macros to make it slightly more sane
|
// Macros to make it slightly more sane
|
||||||
#define msg objc_msgSend
|
#define msg objc_msgSend
|
||||||
#define msg_reg ((id(*)(id, SEL))objc_msgSend)
|
#define msg_reg ((id(*)(id, SEL))objc_msgSend)
|
||||||
@@ -37,11 +43,13 @@
|
|||||||
#if defined (__aarch64__)
|
#if defined (__aarch64__)
|
||||||
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("frame"))
|
#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_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("bounds"))
|
||||||
|
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend)(processInfo, s("operatingSystemVersion"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined (__x86_64__)
|
#if defined (__x86_64__)
|
||||||
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
|
#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_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
|
#endif
|
||||||
|
|
||||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))
|
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))
|
||||||
|
|||||||
@@ -189,6 +189,9 @@ id processAcceleratorKey(const char *key) {
|
|||||||
if( STREQ(key, "return") ) {
|
if( STREQ(key, "return") ) {
|
||||||
return strunicode(0x000d);
|
return strunicode(0x000d);
|
||||||
}
|
}
|
||||||
|
if( STREQ(key, "enter") ) {
|
||||||
|
return strunicode(0x000d);
|
||||||
|
}
|
||||||
if( STREQ(key, "escape") ) {
|
if( STREQ(key, "escape") ) {
|
||||||
return strunicode(0x001b);
|
return strunicode(0x001b);
|
||||||
}
|
}
|
||||||
@@ -576,32 +579,127 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
|||||||
return 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;
|
||||||
|
}
|
||||||
|
return getJSONString(colEntry, "hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize) {
|
||||||
|
|
||||||
|
// Create result
|
||||||
|
id attributedString = ALLOC_INIT("NSMutableAttributedString");
|
||||||
|
msg_reg(attributedString, s("autorelease"));
|
||||||
|
|
||||||
|
// Create new Dictionary
|
||||||
|
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) {
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||||
|
|
||||||
// Process Menu Item attributes
|
// Create new Dictionary
|
||||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
|
|
||||||
// Process font
|
|
||||||
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
CGFloat fontSizeFloat = (CGFloat)fontSize;
|
||||||
|
|
||||||
// Check if valid
|
// Use default font
|
||||||
id fontNameAsNSString = str(fontName);
|
id font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuBarFontOfSize:"), fontSizeFloat);
|
||||||
id font = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
|
||||||
if( font == NULL ) {
|
// Check user supplied font
|
||||||
bool supportsMonospacedDigitSystemFont = (bool) ((id(*)(id, SEL, SEL))objc_msgSend)(c("NSFont"), s("respondsToSelector:"), s("monospacedDigitSystemFontOfSize:weight:"));
|
if( STR_HAS_CHARS(fontName) ) {
|
||||||
if( supportsMonospacedDigitSystemFont ) {
|
id fontNameAsNSString = str(fontName);
|
||||||
font = ((id(*)(id, SEL, CGFloat, CGFloat))objc_msgSend)(c("NSFont"), s("monospacedDigitSystemFontOfSize:weight:"), fontSizeFloat, (CGFloat)NSFontWeightRegular);
|
id userFont = ((id(*)(id, SEL, id, CGFloat))objc_msgSend)(c("NSFont"), s("fontWithName:size:"), fontNameAsNSString, fontSizeFloat);
|
||||||
} else {
|
if( userFont != NULL ) {
|
||||||
font = ((id(*)(id, SEL, CGFloat))objc_msgSend)(c("NSFont"), s("menuFontOfSize:"), fontSizeFloat);
|
font = userFont;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add font to dictionary
|
// Add font to dictionary
|
||||||
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
id fan = lookupStringConstant(str("NSFontAttributeName"));
|
||||||
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
msg_id_id(dictionary, s("setObject:forKey:"), font, fan);
|
||||||
id offset = msg_float(c("NSNumber"), s("numberWithFloat:"), (float)0.0);
|
|
||||||
id offsetAttrName = lookupStringConstant(str("NSBaselineOffsetAttributeName"));
|
|
||||||
msg_id_id(dictionary, s("setObject:forKey:"), offset, offsetAttrName);
|
|
||||||
// RGBA
|
// RGBA
|
||||||
if( RGBA != NULL && strlen(RGBA) > 0) {
|
if( RGBA != NULL && strlen(RGBA) > 0) {
|
||||||
unsigned short r, g, b, a;
|
unsigned short r, g, b, a;
|
||||||
@@ -617,18 +715,17 @@ id createAttributedString(const char* title, const char* fontName, int fontSize,
|
|||||||
(CGFloat)a / (CGFloat)255.0);
|
(CGFloat)a / (CGFloat)255.0);
|
||||||
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
id NSForegroundColorAttributeName = lookupStringConstant(str("NSForegroundColorAttributeName"));
|
||||||
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
msg_id_id(dictionary, s("setObject:forKey:"), colour, NSForegroundColorAttributeName);
|
||||||
msg_reg(colour, s("autorelease"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id attributedString = ALLOC("NSMutableAttributedString");
|
id attributedString = ALLOC("NSMutableAttributedString");
|
||||||
msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
msg_id_id(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||||
msg_reg(attributedString, s("autorelease"));
|
msg_reg(attributedString, s("autorelease"));
|
||||||
msg_reg(dictionary, s("release"));
|
msg_reg(dictionary, s("autorelease"));
|
||||||
return attributedString;
|
return attributedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate) {
|
id 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");
|
id item = ALLOC("NSMenuItem");
|
||||||
|
|
||||||
// Create a MenuItemCallbackData
|
// Create a MenuItemCallbackData
|
||||||
@@ -655,7 +752,12 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
msg_id(item, s("setImage:"), nsimage);
|
msg_id(item, s("setImage:"), nsimage);
|
||||||
}
|
}
|
||||||
|
|
||||||
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
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("setAttributedTitle:"), attributedString);
|
||||||
|
|
||||||
//msg_id(item, s("setTitle:"), str(title));
|
//msg_id(item, s("setTitle:"), str(title));
|
||||||
@@ -702,6 +804,8 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
label = "(empty)";
|
label = "(empty)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for a styled label
|
||||||
|
JsonNode *styledLabel = getJSONObject(item, "StyledLabel");
|
||||||
|
|
||||||
// Is this an alternate menu item?
|
// Is this an alternate menu item?
|
||||||
bool alternate = false;
|
bool alternate = false;
|
||||||
@@ -761,7 +865,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
JsonNode *type = json_find_member(item, "Type");
|
JsonNode *type = json_find_member(item, "Type");
|
||||||
if( type != NULL ) {
|
if( type != NULL ) {
|
||||||
if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) {
|
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);
|
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
|
// Check if this node has a submenu
|
||||||
JsonNode *submenu = json_find_member(item, "SubMenu");
|
JsonNode *submenu = json_find_member(item, "SubMenu");
|
||||||
|
|||||||
@@ -105,12 +105,13 @@ id processRadioMenuItem(Menu *menu, id parentmenu, const char *title, const char
|
|||||||
|
|
||||||
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const char *menuid, bool disabled, bool checked, const char *key);
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate);
|
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);
|
||||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||||
|
|
||||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||||
id GetMenu(Menu *menu);
|
id GetMenu(Menu *menu);
|
||||||
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
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
|
#endif //ASSETS_C_MENU_DARWIN_H
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
|||||||
result->disabled = false;
|
result->disabled = false;
|
||||||
getJSONBool(processedJSON, "Disabled", &result->disabled);
|
getJSONBool(processedJSON, "Disabled", &result->disabled);
|
||||||
|
|
||||||
|
result->styledLabel = getJSONObject(processedJSON, "StyledLabel");
|
||||||
|
|
||||||
// Create the menu
|
// Create the menu
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||||
result->menu = NewMenu(processedMenu);
|
result->menu = NewMenu(processedMenu);
|
||||||
@@ -63,7 +65,7 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled) {
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel) {
|
||||||
|
|
||||||
// Exit early if NULL
|
// Exit early if NULL
|
||||||
if( trayMenu->label == NULL ) {
|
if( trayMenu->label == NULL ) {
|
||||||
@@ -71,7 +73,12 @@ void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName
|
|||||||
}
|
}
|
||||||
// Update button label
|
// Update button label
|
||||||
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg_reg(trayMenu->statusbaritem, s("button"));
|
||||||
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
id attributedString = NULL;
|
||||||
|
if( styledLabel != NULL) {
|
||||||
|
attributedString = createAttributedStringFromStyledLabel(styledLabel, fontName, fontSize);
|
||||||
|
} else {
|
||||||
|
attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||||
|
}
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
if( tooltip != NULL ) {
|
||||||
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
|
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
|
||||||
@@ -125,7 +132,7 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
UpdateTrayIcon(trayMenu);
|
UpdateTrayIcon(trayMenu);
|
||||||
|
|
||||||
// Update the label if needed
|
// Update the label if needed
|
||||||
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled);
|
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA, trayMenu->tooltip, trayMenu->disabled, trayMenu->styledLabel);
|
||||||
|
|
||||||
// Update the menu
|
// Update the menu
|
||||||
id menu = GetMenu(trayMenu->menu);
|
id menu = GetMenu(trayMenu->menu);
|
||||||
@@ -161,6 +168,7 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
|
|||||||
// Copy the other data
|
// Copy the other data
|
||||||
currentMenu->ID = newMenu->ID;
|
currentMenu->ID = newMenu->ID;
|
||||||
currentMenu->label = newMenu->label;
|
currentMenu->label = newMenu->label;
|
||||||
|
currentMenu->styledLabel = newMenu->styledLabel;
|
||||||
currentMenu->trayIconPosition = newMenu->trayIconPosition;
|
currentMenu->trayIconPosition = newMenu->trayIconPosition;
|
||||||
currentMenu->icon = newMenu->icon;
|
currentMenu->icon = newMenu->icon;
|
||||||
|
|
||||||
@@ -220,7 +228,7 @@ void LoadTrayIcons() {
|
|||||||
int length = atoi((const char *)lengthAsString);
|
int length = atoi((const char *)lengthAsString);
|
||||||
|
|
||||||
// Create the icon and add to the hashmap
|
// Create the icon and add to the hashmap
|
||||||
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), data, length);
|
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), (id)data, length);
|
||||||
id trayImage = ALLOC("NSImage");
|
id trayImage = ALLOC("NSImage");
|
||||||
msg_id(trayImage, s("initWithData:"), imageData);
|
msg_id(trayImage, s("initWithData:"), imageData);
|
||||||
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ typedef struct {
|
|||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
|
JsonNode* styledLabel;
|
||||||
|
|
||||||
id delegate;
|
id delegate;
|
||||||
|
|
||||||
} TrayMenu;
|
} TrayMenu;
|
||||||
@@ -38,7 +40,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
|||||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled);
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled, JsonNode *styledLabel);
|
||||||
|
|
||||||
void LoadTrayIcons();
|
void LoadTrayIcons();
|
||||||
void UnloadTrayIcons();
|
void UnloadTrayIcons();
|
||||||
|
|||||||
@@ -127,7 +127,9 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
bool disabled = false;
|
bool disabled = false;
|
||||||
getJSONBool(parsedUpdate, "Disabled", &disabled);
|
getJSONBool(parsedUpdate, "Disabled", &disabled);
|
||||||
|
|
||||||
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled);
|
JsonNode *styledLabel = getJSONObject(parsedUpdate, "StyledLabel");
|
||||||
|
|
||||||
|
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA, tooltip, disabled, styledLabel);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,10 +201,6 @@ func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
|||||||
|
|
||||||
func DirIsEmpty(dir string) (bool, 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
|
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
||||||
f, err := os.Open(dir)
|
f, err := os.Open(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package menumanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leaanthony/go-ansi-parser"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
"github.com/wailsapp/wails/v2/pkg/menu/keys"
|
||||||
@@ -41,11 +44,25 @@ type ProcessedMenuItem struct {
|
|||||||
|
|
||||||
// Tooltip
|
// Tooltip
|
||||||
Tooltip string `json:",omitempty"`
|
Tooltip string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Styled label
|
||||||
|
StyledLabel []*ansi.StyledText `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
|
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
|
||||||
|
|
||||||
ID := menuItemMap.menuItemToIDMap[menuItem]
|
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{
|
result := &ProcessedMenuItem{
|
||||||
ID: ID,
|
ID: ID,
|
||||||
Label: menuItem.Label,
|
Label: menuItem.Label,
|
||||||
@@ -63,6 +80,7 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
|
|||||||
MacTemplateImage: menuItem.MacTemplateImage,
|
MacTemplateImage: menuItem.MacTemplateImage,
|
||||||
MacAlternate: menuItem.MacAlternate,
|
MacAlternate: menuItem.MacAlternate,
|
||||||
Tooltip: menuItem.Tooltip,
|
Tooltip: menuItem.Tooltip,
|
||||||
|
StyledLabel: styledLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if menuItem.SubMenu != nil {
|
if menuItem.SubMenu != nil {
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/leaanthony/go-ansi-parser"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
)
|
)
|
||||||
@@ -36,6 +39,7 @@ type TrayMenu struct {
|
|||||||
menu *menu.Menu
|
menu *menu.Menu
|
||||||
ProcessedMenu *WailsMenu
|
ProcessedMenu *WailsMenu
|
||||||
trayMenu *menu.TrayMenu
|
trayMenu *menu.TrayMenu
|
||||||
|
StyledLabel []*ansi.StyledText `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrayMenu) AsJSON() (string, error) {
|
func (t *TrayMenu) AsJSON() (string, error) {
|
||||||
@@ -48,6 +52,16 @@ func (t *TrayMenu) AsJSON() (string, error) {
|
|||||||
|
|
||||||
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
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{
|
result := &TrayMenu{
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
FontName: trayMenu.FontName,
|
FontName: trayMenu.FontName,
|
||||||
@@ -60,6 +74,7 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
|||||||
RGBA: trayMenu.RGBA,
|
RGBA: trayMenu.RGBA,
|
||||||
menuItemMap: NewMenuItemMap(),
|
menuItemMap: NewMenuItemMap(),
|
||||||
trayMenu: trayMenu,
|
trayMenu: trayMenu,
|
||||||
|
StyledLabel: styledLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.menuItemMap.AddMenu(trayMenu.Menu)
|
result.menuItemMap.AddMenu(trayMenu.Menu)
|
||||||
@@ -150,14 +165,25 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
|
|
||||||
type LabelUpdate struct {
|
type LabelUpdate struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string `json:",omitempty"`
|
||||||
FontName string
|
FontName string `json:",omitempty"`
|
||||||
FontSize int
|
FontSize int
|
||||||
RGBA string
|
RGBA string `json:",omitempty"`
|
||||||
Disabled bool
|
Disabled bool
|
||||||
Tooltip string
|
Tooltip string `json:",omitempty"`
|
||||||
Image string
|
Image string `json:",omitempty"`
|
||||||
MacTemplateImage bool
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update := &LabelUpdate{
|
update := &LabelUpdate{
|
||||||
@@ -170,6 +196,7 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
|
|||||||
Image: trayMenu.Image,
|
Image: trayMenu.Image,
|
||||||
MacTemplateImage: trayMenu.MacTemplateImage,
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
RGBA: trayMenu.RGBA,
|
RGBA: trayMenu.RGBA,
|
||||||
|
StyledLabel: styledLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := json.Marshal(update)
|
data, err := json.Marshal(update)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "React JS",
|
|
||||||
"shortname": "react",
|
|
||||||
"author": "bh90210 <ktc@pm.me>",
|
|
||||||
"description": "Create React App v3 standar tooling"
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
/node_modules/
|
|
||||||
/public/build/
|
|
||||||
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
*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
|
|
||||||
```
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"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.
|
Before Width: | Height: | Size: 7.9 KiB |
@@ -1,24 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import App from './App.svelte';
|
|
||||||
|
|
||||||
const app = new App({
|
|
||||||
target: document.body,
|
|
||||||
props: {
|
|
||||||
name: 'Wails User'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default app;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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}}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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}}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[ '@vue/app', { useBuiltIns: 'entry' } ]
|
|
||||||
]
|
|
||||||
};
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<!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>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<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.
|
Before Width: | Height: | Size: 301 KiB |
@@ -1,85 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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');
|
|
||||||
});
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
let cssConfig = {};
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV == 'production') {
|
|
||||||
cssConfig = {
|
|
||||||
extract: {
|
|
||||||
filename: '[name].css',
|
|
||||||
chunkFilename: '[name].css'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
indexPath: 'index.html',
|
|
||||||
publicPath: 'public',
|
|
||||||
// disable hashes in filenames
|
|
||||||
filenameHashing: false,
|
|
||||||
// delete HTML related webpack plugins
|
|
||||||
chainWebpack: config => {
|
|
||||||
config.plugins.delete('preload');
|
|
||||||
config.plugins.delete('prefetch');
|
|
||||||
config
|
|
||||||
.plugin('html')
|
|
||||||
.tap(args => {
|
|
||||||
args[0].template = 'public/index.html';
|
|
||||||
args[0].inject = false;
|
|
||||||
args[0].cache = false;
|
|
||||||
args[0].minify = false;
|
|
||||||
args[0].filename = 'index.html';
|
|
||||||
return args;
|
|
||||||
});
|
|
||||||
|
|
||||||
let limit = 9999999999999999;
|
|
||||||
config.module
|
|
||||||
.rule('images')
|
|
||||||
.test(/\.(png|gif|jpg)(\?.*)?$/i)
|
|
||||||
.use('url-loader')
|
|
||||||
.loader('url-loader')
|
|
||||||
.tap(options => Object.assign(options, { limit: limit }));
|
|
||||||
config.module
|
|
||||||
.rule('fonts')
|
|
||||||
.test(/\.(woff2?|eot|ttf|otf|svg)(\?.*)?$/i)
|
|
||||||
.use('url-loader')
|
|
||||||
.loader('url-loader')
|
|
||||||
.options({
|
|
||||||
limit: limit
|
|
||||||
});
|
|
||||||
},
|
|
||||||
css: cssConfig,
|
|
||||||
configureWebpack: {
|
|
||||||
output: {
|
|
||||||
filename: '[name].js'
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
splitChunks: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
devServer: {
|
|
||||||
disableHostCheck: true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/wailsapp/wails/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Create application with options
|
|
||||||
app := wails.CreateApp("{{.ProjectName}}", 1024, 768)
|
|
||||||
|
|
||||||
app.Bind(newBasic())
|
|
||||||
|
|
||||||
app.Run()
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Vuetify 2 ",
|
|
||||||
"shortname": "vuetify2",
|
|
||||||
"author": "Michael Hipp <michael@redmule.com>",
|
|
||||||
"description": "A simple template using only HTML/CSS/JS",
|
|
||||||
"helpurl": "https://wails.app/help/templates/vuetify2"
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "{{.ProjectName}}",
|
|
||||||
"outputfilename": "{{.BinaryName}}",
|
|
||||||
"html": "frontend/dist/index.html",
|
|
||||||
"js": "frontend/dist/app.js",
|
|
||||||
"css": "frontend/dist/app.css",
|
|
||||||
"frontend:build": "npm run build",
|
|
||||||
"frontend:install": "npm install",
|
|
||||||
"author": {
|
|
||||||
"name": "{{.AuthorName}}",
|
|
||||||
"email": "{{.AuthorEmail}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/matryer/is"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
|
||||||
|
|
||||||
is := is.New(t)
|
|
||||||
templates, err := List()
|
|
||||||
is.Equal(err, nil)
|
|
||||||
|
|
||||||
println("Found these templates:")
|
|
||||||
for _, template := range templates {
|
|
||||||
fmt.Printf("%+v\n", template)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
"github.com/wailsapp/wails/v2/internal/fs"
|
||||||
@@ -188,10 +190,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
|
|
||||||
var tags slicer.StringSlicer
|
var tags slicer.StringSlicer
|
||||||
tags.Add(options.OutputType)
|
tags.Add(options.OutputType)
|
||||||
|
tags.AddSlice(options.UserTags)
|
||||||
if options.Mode == Debug {
|
if options.Mode == Debug {
|
||||||
tags.Add("debug")
|
tags.Add("debug")
|
||||||
}
|
}
|
||||||
|
tags.Deduplicate()
|
||||||
|
|
||||||
// Add the output type build tag
|
// Add the output type build tag
|
||||||
commands.Add("-tags")
|
commands.Add("-tags")
|
||||||
@@ -291,6 +294,27 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
return fmt.Errorf("%s\n%s", err, string(stde.Bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !options.Compress {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have upx installed?
|
||||||
|
if !shell.CommandExists("upx") {
|
||||||
|
println("Warning: Cannot compress binary: upx not found")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
println(" Compressing with:", "upx", "--best", "--no-color", "--no-progress", options.CompiledBinary)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := exec.Command(options.BuildDirectory, "upx", "--best", "--no-color", "--no-progress", options.CompiledBinary).Output()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Error during compression:")
|
||||||
|
}
|
||||||
|
if verbose {
|
||||||
|
println(output)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ var modeMap = []string{"Debug", "Production"}
|
|||||||
// Options contains all the build options as well as the project data
|
// Options contains all the build options as well as the project data
|
||||||
type Options struct {
|
type Options struct {
|
||||||
LDFlags string // Optional flags to pass to linker
|
LDFlags string // Optional flags to pass to linker
|
||||||
|
UserTags []string // Tags to pass to the Go compiler
|
||||||
Logger *clilogger.CLILogger // All output to the logger
|
Logger *clilogger.CLILogger // All output to the logger
|
||||||
OutputType string // EG: desktop, server....
|
OutputType string // EG: desktop, server....
|
||||||
Mode Mode // release or debug
|
Mode Mode // release or debug
|
||||||
@@ -42,8 +43,9 @@ type Options struct {
|
|||||||
BuildDirectory string // Directory to use for building the application
|
BuildDirectory string // Directory to use for building the application
|
||||||
CleanBuildDirectory bool // Indicates if the build directory should be cleaned before building
|
CleanBuildDirectory bool // Indicates if the build directory should be cleaned before building
|
||||||
CompiledBinary string // Fully qualified path to the compiled binary
|
CompiledBinary string // Fully qualified path to the compiled binary
|
||||||
KeepAssets bool // /Keep the generated assets/files
|
KeepAssets bool // Keep the generated assets/files
|
||||||
Verbosity int // Verbosity level (0 - silent, 1 - default, 2 - verbose)
|
Verbosity int // Verbosity level (0 - silent, 1 - default, 2 - verbose)
|
||||||
|
Compress bool // Compress the final binary
|
||||||
AppleIdentity string
|
AppleIdentity string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
181
v2/pkg/commands/build/desktop_windows.go
Normal file
181
v2/pkg/commands/build/desktop_windows.go
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package build
|
||||||
|
|
||||||
|
// We will compile all tray icons found at <projectdir>/assets/trayicons/*.png into the application
|
||||||
|
func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error {
|
||||||
|
//
|
||||||
|
// var err error
|
||||||
|
//
|
||||||
|
// // Get all the tray icon filenames
|
||||||
|
// trayIconDirectory := filepath.Join(options.ProjectData.AssetsDir, "tray")
|
||||||
|
//
|
||||||
|
// // If the directory doesn't exist, create it
|
||||||
|
// if !fs.DirExists(trayIconDirectory) {
|
||||||
|
// err = fs.MkDirs(trayIconDirectory)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var trayIconFilenames []string
|
||||||
|
// trayIconFilenames, err = filepath.Glob(trayIconDirectory + "/*.png")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Setup target
|
||||||
|
// targetFilename := "trayicons"
|
||||||
|
// targetFile := filepath.Join(assetDir, targetFilename+".h")
|
||||||
|
// d.addFileToDelete(targetFile)
|
||||||
|
//
|
||||||
|
// var dataBytes []byte
|
||||||
|
//
|
||||||
|
// // Use a strings builder
|
||||||
|
// var cdata strings.Builder
|
||||||
|
//
|
||||||
|
// // Write header
|
||||||
|
// header := `// trayicons.h
|
||||||
|
//// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||||
|
//// This file was auto-generated. DO NOT MODIFY.
|
||||||
|
//
|
||||||
|
//`
|
||||||
|
// cdata.WriteString(header)
|
||||||
|
//
|
||||||
|
// var variableList slicer.StringSlicer
|
||||||
|
//
|
||||||
|
// // Loop over icons
|
||||||
|
// for count, filename := range trayIconFilenames {
|
||||||
|
//
|
||||||
|
// // Load the tray icon
|
||||||
|
// dataBytes, err = ioutil.ReadFile(filename)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// iconname := strings.TrimSuffix(filepath.Base(filename), ".png")
|
||||||
|
// trayIconName := fmt.Sprintf("trayIcon%dName", count)
|
||||||
|
// variableList.Add(trayIconName)
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconName, d.convertToHexLiteral([]byte(iconname))))
|
||||||
|
//
|
||||||
|
// trayIconLength := fmt.Sprintf("trayIcon%dLength", count)
|
||||||
|
// variableList.Add(trayIconLength)
|
||||||
|
// lengthAsString := strconv.Itoa(len(dataBytes))
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconLength, d.convertToHexLiteral([]byte(lengthAsString))))
|
||||||
|
//
|
||||||
|
// trayIconData := fmt.Sprintf("trayIcon%dData", count)
|
||||||
|
// variableList.Add(trayIconData)
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", trayIconData))
|
||||||
|
//
|
||||||
|
// // Convert each byte to hex
|
||||||
|
// for _, b := range dataBytes {
|
||||||
|
// cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// cdata.WriteString("0x00 };\n")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Write out main trayIcons data
|
||||||
|
// cdata.WriteString("const unsigned char *trayIcons[] = { ")
|
||||||
|
// cdata.WriteString(variableList.Join(", "))
|
||||||
|
// if len(trayIconFilenames) > 0 {
|
||||||
|
// cdata.WriteString(", ")
|
||||||
|
// }
|
||||||
|
// cdata.WriteString("0x00 };\n")
|
||||||
|
//
|
||||||
|
// err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We will compile all dialog icons found at <projectdir>/icons/dialog/*.png into the application
|
||||||
|
func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) error {
|
||||||
|
|
||||||
|
// var err error
|
||||||
|
//
|
||||||
|
// // Get all the dialog icon filenames
|
||||||
|
// dialogIconDirectory := filepath.Join(options.ProjectData.AssetsDir, "dialog")
|
||||||
|
// var dialogIconFilenames []string
|
||||||
|
//
|
||||||
|
// // If the directory does not exist, create it
|
||||||
|
// if !fs.DirExists(dialogIconDirectory) {
|
||||||
|
// err = fs.MkDirs(dialogIconDirectory)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// dialogIconFilenames, err = filepath.Glob(dialogIconDirectory + "/*.png")
|
||||||
|
// if err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Setup target
|
||||||
|
// targetFilename := "userdialogicons"
|
||||||
|
// targetFile := filepath.Join(assetDir, targetFilename+".h")
|
||||||
|
// d.addFileToDelete(targetFile)
|
||||||
|
//
|
||||||
|
// var dataBytes []byte
|
||||||
|
//
|
||||||
|
// // Use a strings builder
|
||||||
|
// var cdata strings.Builder
|
||||||
|
//
|
||||||
|
// // Write header
|
||||||
|
// header := `// userdialogicons.h
|
||||||
|
//// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||||
|
//// This file was auto-generated. DO NOT MODIFY.
|
||||||
|
//
|
||||||
|
//`
|
||||||
|
// cdata.WriteString(header)
|
||||||
|
//
|
||||||
|
// var variableList slicer.StringSlicer
|
||||||
|
//
|
||||||
|
// // Loop over icons
|
||||||
|
// for count, filename := range dialogIconFilenames {
|
||||||
|
//
|
||||||
|
// // Load the tray icon
|
||||||
|
// dataBytes, err = ioutil.ReadFile(filename)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// iconname := strings.TrimSuffix(filepath.Base(filename), ".png")
|
||||||
|
// dialogIconName := fmt.Sprintf("userDialogIcon%dName", count)
|
||||||
|
// variableList.Add(dialogIconName)
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, d.convertToHexLiteral([]byte(iconname))))
|
||||||
|
//
|
||||||
|
// dialogIconLength := fmt.Sprintf("userDialogIcon%dLength", count)
|
||||||
|
// variableList.Add(dialogIconLength)
|
||||||
|
// lengthAsString := strconv.Itoa(len(dataBytes))
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, d.convertToHexLiteral([]byte(lengthAsString))))
|
||||||
|
//
|
||||||
|
// dialogIconData := fmt.Sprintf("userDialogIcon%dData", count)
|
||||||
|
// variableList.Add(dialogIconData)
|
||||||
|
// cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData))
|
||||||
|
//
|
||||||
|
// // Convert each byte to hex
|
||||||
|
// for _, b := range dataBytes {
|
||||||
|
// cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// cdata.WriteString("0x00 };\n")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Write out main dialogIcons data
|
||||||
|
// cdata.WriteString("const unsigned char *userDialogIcons[] = { ")
|
||||||
|
// cdata.WriteString(variableList.Join(", "))
|
||||||
|
// if len(dialogIconFilenames) > 0 {
|
||||||
|
// cdata.WriteString(", ")
|
||||||
|
// }
|
||||||
|
// cdata.WriteString("0x00 };\n")
|
||||||
|
//
|
||||||
|
// err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -49,4 +49,4 @@ Example:
|
|||||||
|
|
||||||
## Mac
|
## Mac
|
||||||
|
|
||||||
The `mac` directory holds files specific to Mac builds, such as `info.plist`. These may be edited and used as part of the build.
|
The `mac` directory holds files specific to Mac builds, such as `Info.plist`. These may be edited and used as part of the build.
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func packageApplication(options *Options) error {
|
|||||||
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
return errors.Wrap(err, "Cannot move file: "+options.ProjectData.OutputFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate info.plist
|
// Generate Info.plist
|
||||||
err = processPList(options, contentsDirectory)
|
err = processPList(options, contentsDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -68,7 +68,7 @@ func packageApplication(options *Options) error {
|
|||||||
func processPList(options *Options, contentsDirectory string) error {
|
func processPList(options *Options, contentsDirectory string) error {
|
||||||
|
|
||||||
// Check if plist already exists in project dir
|
// Check if plist already exists in project dir
|
||||||
plistFile := filepath.Join(options.ProjectData.AssetsDir, "mac", "info.plist")
|
plistFile := filepath.Join(options.ProjectData.AssetsDir, "mac", "Info.plist")
|
||||||
|
|
||||||
// If the file doesn't exist, generate it
|
// If the file doesn't exist, generate it
|
||||||
if !fs.FileExists(plistFile) {
|
if !fs.FileExists(plistFile) {
|
||||||
@@ -79,7 +79,7 @@ func processPList(options *Options, contentsDirectory string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy it to the contents directory
|
// Copy it to the contents directory
|
||||||
targetFile := filepath.Join(contentsDirectory, "info.plist")
|
targetFile := filepath.Join(contentsDirectory, "Info.plist")
|
||||||
return fs.CopyFile(plistFile, targetFile)
|
return fs.CopyFile(plistFile, targetFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +88,11 @@ func generateDefaultPlist(options *Options, targetPlistFile string) error {
|
|||||||
exe := defaultString(options.OutputFile, name)
|
exe := defaultString(options.OutputFile, name)
|
||||||
version := "1.0.0"
|
version := "1.0.0"
|
||||||
author := defaultString(options.ProjectData.Author.Name, "Anonymous")
|
author := defaultString(options.ProjectData.Author.Name, "Anonymous")
|
||||||
packageID := strings.Join([]string{"wails", name, version}, ".")
|
packageID := strings.Join([]string{"wails", name}, ".")
|
||||||
plistData := newPlistData(name, exe, packageID, version, author)
|
plistData := newPlistData(name, exe, packageID, version, author)
|
||||||
|
|
||||||
tmpl := template.New("infoPlist")
|
tmpl := template.New("infoPlist")
|
||||||
plistTemplate := fs.RelativePath("./internal/packager/darwin/info.plist")
|
plistTemplate := fs.RelativePath("./internal/packager/darwin/Info.plist")
|
||||||
infoPlist, err := ioutil.ReadFile(plistTemplate)
|
infoPlist, err := ioutil.ReadFile(plistTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Cannot open plist template")
|
return errors.Wrap(err, "Cannot open plist template")
|
||||||
|
|||||||
30
v2/pkg/mac/notification_darwin.go
Normal file
30
v2/pkg/mac/notification_darwin.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Package mac provides MacOS related utility functions for Wails applications
|
||||||
|
package mac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/shell"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartAtLogin will either add or remove this application to/from the login
|
||||||
|
// items, depending on the given boolean flag. The limitation is that the
|
||||||
|
// currently running app must be in an app bundle.
|
||||||
|
func ShowNotification(title string, subtitle string, message string, sound string) error {
|
||||||
|
command := fmt.Sprintf("display notification \"%s\"", message)
|
||||||
|
if len(title) > 0 {
|
||||||
|
command += fmt.Sprintf(" with title \"%s\"", title)
|
||||||
|
}
|
||||||
|
if len(subtitle) > 0 {
|
||||||
|
command += fmt.Sprintf(" subtitle \"%s\"", subtitle)
|
||||||
|
}
|
||||||
|
if len(sound) > 0 {
|
||||||
|
command += fmt.Sprintf(" sound name \"%s\"", sound)
|
||||||
|
}
|
||||||
|
_, stde, err := shell.RunCommand("/tmp", "osascript", "-e", command)
|
||||||
|
if err != nil {
|
||||||
|
errors.Wrap(err, stde)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
34
v2/pkg/mac/notification_darwin_test.go
Normal file
34
v2/pkg/mac/notification_darwin_test.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package mac
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestShowNotification(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
title string
|
||||||
|
subtitle string
|
||||||
|
message string
|
||||||
|
sound string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
title string
|
||||||
|
subtitle string
|
||||||
|
message string
|
||||||
|
sound string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"No message", "", "", "", "", false},
|
||||||
|
{"Title only", "I am a Title", "", "", "", false},
|
||||||
|
{"SubTitle only", "", "I am a subtitle", "", "", false},
|
||||||
|
{"Message only", "", "", "I am a message!", "", false},
|
||||||
|
{"Sound only", "", "", "", "submarine.aiff", false},
|
||||||
|
{"Full", "Title", "Subtitle", "This is a long message to show that text gets wrapped in a notification", "submarine.aiff", false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := ShowNotification(tt.title, tt.subtitle, tt.message, tt.sound); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("ShowNotification() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
)
|
)
|
||||||
|
|
||||||
var namedKeys = slicer.String([]string{"backspace", "tab", "return", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"})
|
var namedKeys = slicer.String([]string{"backspace", "tab", "return", "enter", "escape", "left", "right", "up", "down", "space", "delete", "home", "end", "page up", "page down", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", "f32", "f33", "f34", "f35", "numlock"})
|
||||||
|
|
||||||
func parseKey(key string) (string, bool) {
|
func parseKey(key string) (string, bool) {
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/fatih/structtag"
|
"github.com/fatih/structtag"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -225,7 +224,6 @@ func (p *Parser) parseField(file *ast.File, field *ast.Field, pkg *Package) ([]*
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
spew.Dump(t)
|
|
||||||
return nil, fmt.Errorf("unsupported field found in struct: %+v", t)
|
return nil, fmt.Errorf("unsupported field found in struct: %+v", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
v2/test/kitchensink/.gitignore
vendored
2
v2/test/kitchensink/.gitignore
vendored
@@ -1 +1 @@
|
|||||||
info.plist
|
Info.plist
|
||||||
Reference in New Issue
Block a user