Compare commits

...

16 Commits

Author SHA1 Message Date
Lea Anthony
8caf277bf1 v2.0.0-alpha.65 2021-04-20 19:44:27 +10:00
Lea Anthony
4b480bb085 Move templates to CLI dir. Use go:embed 2021-04-18 17:10:12 +10:00
Lea Anthony
c1d63aff34 Refactor vanilla template to work with wails dev out of the box 2021-04-18 07:25:02 +10:00
Lea Anthony
5fc89c4cad Fix Promise<void> return type. Tidy up. 2021-04-18 07:24:29 +10:00
Lea Anthony
5e96bb5a32 [windows] Improve wails doctor 2021-04-17 13:40:27 +10:00
Lea Anthony
c5e76c50b0 v2.0.0-alpha.64 2021-04-13 21:07:24 +10:00
Lea Anthony
e40226ff7a ANSI support for tray labels and submenus 2021-04-13 21:05:56 +10:00
Lea Anthony
53a3638fa8 ANSI support for tray labels 2021-04-13 05:43:37 +10:00
Lea Anthony
1344638c52 Add OS detection 2021-04-12 06:25:15 +10:00
Lea Anthony
6fdc87454a Initial support for desktop notifications 2021-04-07 06:32:21 +10:00
Lea Anthony
c36f9501a9 Dialog warning on JS Error. Remove shutdown hack. Optional dialog callback. 2021-04-06 20:18:29 +10:00
Lea Anthony
c23b43c352 Improve vanilla app JS 2021-04-04 15:47:03 +10:00
Lea Anthony
a76851463b Font smoothing for vanilla app 2021-04-04 15:45:24 +10:00
Lea Anthony
e17b432c8f Tidy up templates 2021-04-04 15:36:55 +10:00
Lea Anthony
5d444cd6dd Support user tags 2021-04-04 13:42:48 +10:00
Lea Anthony
be43049fc6 Remove debug code 2021-04-04 05:25:21 +10:00
69 changed files with 582 additions and 896 deletions

View File

@@ -24,10 +24,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
command := app.NewSubCommand("build", "Builds the application")
// Setup target type flag
//description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
//command.StringFlag("t", description, &outputType)
// Setup production flag
production := false
command.BoolFlag("production", "Build in production mode", &production)
@@ -54,9 +50,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
ldflags := ""
command.StringFlag("ldflags", "optional ldflags", &ldflags)
// Log to file
//logFile := ""
//command.StringFlag("l", "Log to file", &logFile)
// tags to pass to `go`
tags := ""
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
// Retain assets
keepAssets := false
@@ -70,11 +66,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
cleanBuildDirectory := false
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 {
quiet := verbosity == 0
@@ -92,11 +83,6 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
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
mode := build.Debug
if production {
@@ -121,6 +107,16 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
println("Warning: compress flag unsupported for universal binaries. Ignoring.")
compress = false
}
// Tags
userTags := []string{}
for _, tag := range strings.Split(tags, " ") {
thisTag := strings.TrimSpace(tag)
if thisTag != "" {
userTags = append(userTags, thisTag)
}
}
// Create BuildOptions
buildOptions := &build.Options{
Logger: logger,
@@ -132,9 +128,9 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
LDFlags: ldflags,
Compiler: compilerCommand,
KeepAssets: keepAssets,
AppleIdentity: appleIdentity,
Verbosity: verbosity,
Compress: compress,
UserTags: userTags,
}
// Calculate platform and arch
@@ -166,6 +162,7 @@ func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
if len(buildOptions.OutputFile) > 0 {
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
}

View File

@@ -48,9 +48,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
// Exit early if PM not found
if info.PM == nil {
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
w.Flush()
println()
return nil
}
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())

View File

@@ -9,7 +9,7 @@ import (
"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 {
command := app.NewSubCommand("generate", "Code Generation Tools")
@@ -19,7 +19,7 @@ func AddSubcommand(app *clir.Cli, w io.Writer) error {
// Quiet Init
quiet := false
backendAPI.BoolFlag("q", "Supress output to console", &quiet)
backendAPI.BoolFlag("q", "Suppress output to console", &quiet)
backendAPI.Action(func() error {
@@ -85,7 +85,4 @@ func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
}
}
logger.Println("")
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
}

View File

@@ -6,9 +6,10 @@ import (
"strings"
"time"
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
"github.com/leaanthony/clir"
"github.com/pkg/errors"
"github.com/wailsapp/wails/v2/internal/templates"
"github.com/wailsapp/wails/v2/pkg/clilogger"
"github.com/wailsapp/wails/v2/pkg/git"
)

View File

@@ -1,14 +1,16 @@
package templates
import (
"embed"
"encoding/json"
"fmt"
"io/ioutil"
gofs "io/fs"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/debme"
"github.com/leaanthony/gosod"
"github.com/leaanthony/slicer"
"github.com/olekukonko/tablewriter"
@@ -16,6 +18,12 @@ import (
"github.com/wailsapp/wails/v2/pkg/clilogger"
)
//go:embed templates
var templates embed.FS
//go:embed ides/*
var ides embed.FS
// Cahce for the templates
// We use this because we need different views of the same data
var templateCache []Template = nil
@@ -59,20 +67,21 @@ type Template struct {
HelpURL string `json:"helpurl"`
// Other data
Directory string `json:"-"`
FS gofs.FS `json:"-"`
}
func parseTemplate(directory string) (Template, error) {
templateJSON := filepath.Join(directory, "template.json")
func parseTemplate(template gofs.FS) (Template, error) {
var result Template
data, err := ioutil.ReadFile(templateJSON)
data, err := gofs.ReadFile(template, "template.json")
if err != nil {
return result, err
}
result.Directory = directory
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
@@ -134,11 +143,13 @@ func getTemplateByShortname(shortname string) (Template, error) {
// Loads the template cache
func loadTemplateCache() error {
// Get local template directory
templateDir := fs.RelativePath("templates")
templatesFS, err := debme.FS(templates, "templates")
if err != nil {
return err
}
// Get directories
files, err := ioutil.ReadDir(templateDir)
files, err := templatesFS.ReadDir(".")
if err != nil {
return err
}
@@ -148,8 +159,11 @@ func loadTemplateCache() error {
for _, file := range files {
if file.IsDir() {
templateDir := filepath.Join(templateDir, file.Name())
template, err := parseTemplate(templateDir)
templateFS, err := templatesFS.FS(file.Name())
if err != nil {
return err
}
template, err := parseTemplate(templateFS)
if err != nil {
// Cannot parse this template, continue
continue
@@ -163,7 +177,6 @@ func loadTemplateCache() error {
// Install the given template
func Install(options *Options) error {
// Get cwd
cwd, err := os.Getwd()
if err != nil {
@@ -211,19 +224,16 @@ func Install(options *Options) error {
}
// Use Gosod to install the template
installer, err := gosod.TemplateDir(template.Directory)
if err != nil {
return err
}
installer := gosod.New(template.FS)
// Ignore template.json files
installer.IgnoreFilename("template.json")
installer.IgnoreFile("template.json")
// Setup the data.
// We use the directory name for the binary name, like Go
BinaryName := filepath.Base(options.TargetDir)
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
localWailsDirectory := fs.RelativePath("../..")
localWailsDirectory := fs.RelativePath("../../../../../..")
templateData := &Data{
ProjectName: options.ProjectName,
BinaryName: filepath.Base(options.TargetDir),
@@ -295,14 +305,14 @@ func generateIDEFiles(options *Options) error {
func generateVSCodeFiles(options *Options) error {
targetDir := filepath.Join(options.TargetDir, ".vscode")
sourceDir := fs.RelativePath(filepath.Join("./ides/vscode"))
// Use Gosod to install the template
installer, err := gosod.TemplateDir(sourceDir)
source, err := debme.FS(ides, "ides/vscode")
if err != nil {
return err
}
// Use gosod to install the template
installer := gosod.New(source)
binaryName := filepath.Base(options.TargetDir)
if runtime.GOOS == "windows" {
// yay windows

View File

@@ -11,7 +11,7 @@ type Basic struct {
runtime *wails.Runtime
}
// newBasic creates a new Basic application struct
// NewBasic creates a new Basic application struct
func NewBasic() *Basic {
return &Basic{}
}

View File

@@ -23,6 +23,7 @@ button {
border-radius: 3px;
outline: none;
height: 20px;
-webkit-font-smoothing: antialiased;
}
#logo {

View File

@@ -1,12 +1,12 @@
// Get input + focus
var nameElement = document.getElementById("name");
let nameElement = document.getElementById("name");
nameElement.focus();
// Stup the greet function
// Setup the greet function
window.greet = function () {
// Get name
var name = nameElement.value;
let name = nameElement.value;
// Call Basic.Greet(name)
window.backend.main.Basic.Greet(name).then((result) => {

View File

@@ -1,6 +1,6 @@
module test
go 1.13
go 1.16
require (
github.com/wailsapp/wails/v2 v2.0.0-alpha

View File

@@ -1,7 +1,7 @@
{
"name": "{{.ProjectName}}",
"outputfilename": "{{.BinaryName}}",
"html": "frontend/index.html",
"html": "frontend/src/index.html",
"author": {
"name": "{{.AuthorName}}",
"email": "{{.AuthorEmail}}"

View File

@@ -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)
}

View File

@@ -67,5 +67,6 @@ func main() {
err = app.Run()
if err != nil {
println("\n\nERROR: " + err.Error())
os.Exit(1)
}
}

View File

@@ -1,3 +1,3 @@
package main
var version = "v2.0.0-alpha.63"
var version = "v2.0.0-alpha.65"

View File

@@ -4,14 +4,15 @@ go 1.16
require (
github.com/Masterminds/semver v1.5.0
github.com/davecgh/go-spew v1.1.1
github.com/fatih/structtag v1.2.0
github.com/fsnotify/fsnotify v1.4.9
github.com/gorilla/websocket v1.4.1
github.com/imdario/mergo v0.3.11
github.com/jackmordaunt/icns v1.0.0
github.com/leaanthony/clir v1.0.4
github.com/leaanthony/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/matryer/is v1.4.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect

View File

@@ -1,7 +1,6 @@
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
@@ -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/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
github.com/leaanthony/debme v1.1.1 h1:2CQjJkfrjr4b2VgpDmkq2aghem5R2bNbg1Yg5cKQGBQ=
github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
github.com/leaanthony/gosod v1.0.1 h1:F+4c3DmEBfigi7oAswCV2RpQ+k4DcNbhuCZUGdBHacQ=
github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU=
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=

View File

@@ -14,30 +14,6 @@ import (
"github.com/leaanthony/slicer"
)
const _comment = `
const backend = {
main: {
"xbarApp": {
"GetCategories": () => {
window.backend.main.xbarApp.GetCategories.call(arguments);
},
/**
* @param {string} arg1
*/
"InstallPlugin": (arg1) => {
window.backend.main.xbarApp.InstallPlugin.call(arguments);
},
"GetPlugins": () => {
window.backend.main.xbarApp.GetPlugins.call(arguments);
}
}
}
}
export default backend;`
//go:embed assets/package.json
var packageJSON []byte
@@ -100,6 +76,8 @@ const backend = {`)
}
returnType += ">"
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
} else {
returnType = "Promise<void>"
}
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
output.WriteString(" */\n")
@@ -125,13 +103,14 @@ const backend = {`)
export default backend;`)
output.WriteString("\n")
// TODO: Make this configurable in wails.json
dirname, err := fs.RelativeToCwd("frontend/src/backend")
if err != nil {
log.Fatal(err)
}
if !fs.DirExists(dirname) {
err := fs.Mkdir(dirname)
err := fs.MkDirs(dirname)
if err != nil {
log.Fatal(err)
}

View File

@@ -24,6 +24,9 @@ struct hashmap_s dialogIconCache;
// Dispatch Method
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;
// dispatch will execute the given `func` pointer
@@ -80,6 +83,11 @@ void ShowMouse() {
msg_reg(c("NSCursor"), s("unhide"));
}
OSVersion getOSVersion() {
id processInfo = msg_reg(c("NSProcessInfo"), s("processInfo"));
return GET_OSVERSION(processInfo);
}
struct Application {
// 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"));
if( strcmp(name, "error") == 0 ) {
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) {
// Delete handler
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
if( app->shuttingDown ) return;
@@ -791,18 +803,20 @@ extern void MessageDialog(struct Application *app, char *callbackID, char *type,
buttonPressed = button4;
}
// Construct callback message. Format "DM<callbackID>|<selected button index>"
const char *callback = concat("DM", callbackID);
const char *header = concat(callback, "|");
const char *responseMessage = concat(header, buttonPressed);
if ( STR_HAS_CHARS(callbackID) ) {
// Construct callback message. Format "DM<callbackID>|<selected button index>"
const char *callback = concat("DM", callbackID);
const char *header = concat(callback, "|");
const char *responseMessage = concat(header, buttonPressed);
// Send message to backend
app->sendMessageToBackend(responseMessage);
// Send message to backend
app->sendMessageToBackend(responseMessage);
// Free memory
MEMFREE(header);
MEMFREE(callback);
MEMFREE(responseMessage);
// Free memory
MEMFREE(header);
MEMFREE(callback);
MEMFREE(responseMessage);
}
);
}
@@ -1698,9 +1712,6 @@ void HasURLHandlers(struct Application* app) {
void Quit(struct Application *app) {
Debug(app, "Quit Called");
msg_id(app->application, s("stop:"), NULL);
SetSize(app, 0, 0);
Show(app);
Hide(app);
}
id createImageFromBase64Data(const char *data, bool isTemplateImage) {

View File

@@ -11,6 +11,12 @@
#include "hashmap.h"
#include "stdlib.h"
typedef struct {
long maj;
long min;
long patch;
} OSVersion;
// Macros to make it slightly more sane
#define msg objc_msgSend
#define msg_reg ((id(*)(id, SEL))objc_msgSend)
@@ -37,11 +43,13 @@
#if defined (__aarch64__)
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("frame"))
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend)(receiver, s("bounds"))
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend)(processInfo, s("operatingSystemVersion"));
#endif
#if defined (__x86_64__)
#define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame"))
#define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds"))
#define GET_OSVERSION(receiver) ((OSVersion(*)(id, SEL))objc_msgSend_stret)(processInfo, s("operatingSystemVersion"));
#endif
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))objc_msgSend)(receiver, s("backingScaleFactor"))

View File

@@ -579,6 +579,105 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
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) {
// Create new Dictionary
@@ -626,7 +725,7 @@ id createAttributedString(const char* title, const char* fontName, int fontSize,
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");
// Create a MenuItemCallbackData
@@ -653,7 +752,12 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
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("setTitle:"), str(title));
@@ -700,6 +804,8 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
label = "(empty)";
}
// Check for a styled label
JsonNode *styledLabel = getJSONObject(item, "StyledLabel");
// Is this an alternate menu item?
bool alternate = false;
@@ -759,7 +865,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
JsonNode *type = json_find_member(item, "Type");
if( type != NULL ) {
if( STREQ(type->string_, "Text") || STREQ(type->string_, "Submenu")) {
id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate);
id thisMenuItem = processTextMenuItem(menu, parentMenu, label, menuid, disabled, acceleratorkey, modifiers, tooltip, image, fontName, fontSize, RGBA, templateImage, alternate, styledLabel);
// Check if this node has a submenu
JsonNode *submenu = json_find_member(item, "SubMenu");

View File

@@ -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 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 processMenuData(Menu *menu, JsonNode *menuData);
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
id GetMenu(Menu *menu);
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
id createAttributedStringFromStyledLabel(JsonNode *styledLabel, const char* fontName, int fontSize);
#endif //ASSETS_C_MENU_DARWIN_H

View File

@@ -42,6 +42,8 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
result->disabled = false;
getJSONBool(processedJSON, "Disabled", &result->disabled);
result->styledLabel = getJSONObject(processedJSON, "StyledLabel");
// Create the menu
JsonNode* processedMenu = mustJSONObject(processedJSON, "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
if( trayMenu->label == NULL ) {
@@ -71,7 +73,12 @@ void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName
}
// Update button label
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 ) {
msg_id(statusBarButton, s("setToolTip:"), str(tooltip));
@@ -125,7 +132,7 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
UpdateTrayIcon(trayMenu);
// 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
id menu = GetMenu(trayMenu->menu);
@@ -161,6 +168,7 @@ void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu) {
// Copy the other data
currentMenu->ID = newMenu->ID;
currentMenu->label = newMenu->label;
currentMenu->styledLabel = newMenu->styledLabel;
currentMenu->trayIconPosition = newMenu->trayIconPosition;
currentMenu->icon = newMenu->icon;
@@ -220,7 +228,7 @@ void LoadTrayIcons() {
int length = atoi((const char *)lengthAsString);
// Create the icon and add to the hashmap
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), data, length);
id imageData = ((id(*)(id, SEL, id, int))objc_msgSend)(c("NSData"), s("dataWithBytes:length:"), (id)data, length);
id trayImage = ALLOC("NSImage");
msg_id(trayImage, s("initWithData:"), imageData);
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);

View File

@@ -29,6 +29,8 @@ typedef struct {
JsonNode* processedJSON;
JsonNode* styledLabel;
id delegate;
} TrayMenu;
@@ -38,7 +40,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
void ShowTrayMenu(TrayMenu* trayMenu);
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
void UpdateTrayIcon(TrayMenu *trayMenu);
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA, const char *tooltip, bool disabled);
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 UnloadTrayIcons();

View File

@@ -127,7 +127,9 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
bool disabled = false;
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);
}

View File

@@ -201,10 +201,6 @@ func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
func DirIsEmpty(dir string) (bool, error) {
if !DirExists(dir) {
return false, fmt.Errorf("DirIsEmpty called with a non-existant directory: %s", dir)
}
// CREDIT: https://stackoverflow.com/a/30708914/8325411
f, err := os.Open(dir)
if err != nil {

View File

@@ -2,6 +2,9 @@ package menumanager
import (
"encoding/json"
"strings"
"github.com/leaanthony/go-ansi-parser"
"github.com/wailsapp/wails/v2/pkg/menu"
"github.com/wailsapp/wails/v2/pkg/menu/keys"
@@ -41,11 +44,25 @@ type ProcessedMenuItem struct {
// Tooltip
Tooltip string `json:",omitempty"`
// Styled label
StyledLabel []*ansi.StyledText `json:",omitempty"`
}
func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *ProcessedMenuItem {
ID := menuItemMap.menuItemToIDMap[menuItem]
// Parse ANSI text
var styledLabel []*ansi.StyledText
tempLabel := menuItem.Label
if strings.Contains(tempLabel, "\033[") {
parsedLabel, err := ansi.Parse(menuItem.Label)
if err == nil {
styledLabel = parsedLabel
}
}
result := &ProcessedMenuItem{
ID: ID,
Label: menuItem.Label,
@@ -63,6 +80,7 @@ func NewProcessedMenuItem(menuItemMap *MenuItemMap, menuItem *menu.MenuItem) *Pr
MacTemplateImage: menuItem.MacTemplateImage,
MacAlternate: menuItem.MacAlternate,
Tooltip: menuItem.Tooltip,
StyledLabel: styledLabel,
}
if menuItem.SubMenu != nil {

View File

@@ -4,8 +4,11 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"
"sync"
"github.com/leaanthony/go-ansi-parser"
"github.com/pkg/errors"
"github.com/wailsapp/wails/v2/pkg/menu"
)
@@ -36,6 +39,7 @@ type TrayMenu struct {
menu *menu.Menu
ProcessedMenu *WailsMenu
trayMenu *menu.TrayMenu
StyledLabel []*ansi.StyledText `json:",omitempty"`
}
func (t *TrayMenu) AsJSON() (string, error) {
@@ -48,6 +52,16 @@ func (t *TrayMenu) AsJSON() (string, error) {
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
// Parse ANSI text
var styledLabel []*ansi.StyledText
tempLabel := trayMenu.Label
if strings.Contains(tempLabel, "\033[") {
parsedLabel, err := ansi.Parse(tempLabel)
if err == nil {
styledLabel = parsedLabel
}
}
result := &TrayMenu{
Label: trayMenu.Label,
FontName: trayMenu.FontName,
@@ -60,6 +74,7 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
RGBA: trayMenu.RGBA,
menuItemMap: NewMenuItemMap(),
trayMenu: trayMenu,
StyledLabel: styledLabel,
}
result.menuItemMap.AddMenu(trayMenu.Menu)
@@ -150,14 +165,25 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
type LabelUpdate struct {
ID string
Label string
FontName string
Label string `json:",omitempty"`
FontName string `json:",omitempty"`
FontSize int
RGBA string
RGBA string `json:",omitempty"`
Disabled bool
Tooltip string
Image string
Tooltip string `json:",omitempty"`
Image string `json:",omitempty"`
MacTemplateImage bool
StyledLabel []*ansi.StyledText `json:",omitempty"`
}
// Parse ANSI text
var styledLabel []*ansi.StyledText
tempLabel := trayMenu.Label
if strings.Contains(tempLabel, "\033[") {
parsedLabel, err := ansi.Parse(tempLabel)
if err == nil {
styledLabel = parsedLabel
}
}
update := &LabelUpdate{
@@ -170,6 +196,7 @@ func (m *Manager) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) (string, error) {
Image: trayMenu.Image,
MacTemplateImage: trayMenu.MacTemplateImage,
RGBA: trayMenu.RGBA,
StyledLabel: styledLabel,
}
data, err := json.Marshal(update)

View File

@@ -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"
}

View File

@@ -1,6 +0,0 @@
{
"name": "React JS",
"shortname": "react",
"author": "bh90210 <ktc@pm.me>",
"description": "Create React App v3 standar tooling"
}

View File

@@ -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)
}

View File

@@ -1,4 +0,0 @@
/node_modules/
/public/build/
.DS_Store

View File

@@ -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
```

View File

@@ -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

View File

@@ -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>

View File

@@ -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
});
}
}
};
}

View File

@@ -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>

View File

@@ -1,10 +0,0 @@
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
name: 'Wails User'
}
});
export default app;

View File

@@ -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}}

View File

@@ -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)
}
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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}}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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)
}

View File

@@ -1,5 +0,0 @@
module.exports = {
presets: [
[ '@vue/app', { useBuiltIns: 'entry' } ]
]
};

View File

@@ -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"
]
}

View File

@@ -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>

View File

@@ -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">&copy; 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

View File

@@ -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>

View File

@@ -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');
});

View File

@@ -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
}
};

View File

@@ -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()
}

View File

@@ -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"
}

View File

@@ -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}}"
}
}

View File

@@ -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)
}
}

View File

@@ -190,10 +190,11 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
var tags slicer.StringSlicer
tags.Add(options.OutputType)
tags.AddSlice(options.UserTags)
if options.Mode == Debug {
tags.Add("debug")
}
tags.Deduplicate()
// Add the output type build tag
commands.Add("-tags")

View File

@@ -29,6 +29,7 @@ var modeMap = []string{"Debug", "Production"}
// Options contains all the build options as well as the project data
type Options struct {
LDFlags string // Optional flags to pass to linker
UserTags []string // Tags to pass to the Go compiler
Logger *clilogger.CLILogger // All output to the logger
OutputType string // EG: desktop, server....
Mode Mode // release or debug

View 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
}

View 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
}

View 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)
}
})
}
}

View File

@@ -5,7 +5,6 @@ import (
"go/ast"
"strings"
"github.com/davecgh/go-spew/spew"
"github.com/fatih/structtag"
)
@@ -225,7 +224,6 @@ func (p *Parser) parseField(file *ast.File, field *ast.Field, pkg *Package) ([]*
}
default:
spew.Dump(t)
return nil, fmt.Errorf("unsupported field found in struct: %+v", t)
}