Compare commits

...

61 Commits

Author SHA1 Message Date
Lea Anthony
24c7362163 Even more fixes 2019-07-13 22:18:42 +10:00
Lea Anthony
f6ff7d7b16 fix: linting for hound 2019-07-13 22:12:49 +10:00
Lea Anthony
b0a075cdf2 fix: more lint fixes 2019-07-13 22:10:43 +10:00
Lea Anthony
98d4d6b33c config: eslint 2019-07-13 15:42:57 +10:00
Lea Anthony
9ba3e0512b fix: linting 2019-07-13 15:35:50 +10:00
Lea Anthony
eff63175e5 fix: lint 2019-07-13 15:34:00 +10:00
Lea Anthony
75a0b632bc fix: capitalisation and binding fix 2019-07-13 15:30:52 +10:00
Lea Anthony
a2af626477 fix: config errors 2019-07-12 10:22:19 +10:00
Lea Anthony
8aa97f64ef feat: major refactor 2019-07-12 10:12:15 +10:00
Lea Anthony
caa1e04b5a Update README.md 2019-07-08 21:29:59 +10:00
Lea Anthony
cddf6a0204 chore: version bump 2019-07-07 10:28:58 +10:00
Lea Anthony
9fa1f42dc7 Merge pull request #155 from wailsapp/154-Support-Distribution-'Fedora'
feat: test support for Fedora
2019-07-07 10:28:19 +10:00
Lea Anthony
c7e709d487 feat: test support for Fedora 2019-07-07 10:25:59 +10:00
Lea Anthony
6801398f3d docs: update contributors 2019-07-07 10:17:44 +10:00
Lea Anthony
982d14c049 Merge pull request #152 from fallendusk/master
Add support for Gentoo
2019-07-07 10:16:21 +10:00
fallendusk
ddbaf55ae7 Merge branch 'develop' into master 2019-07-06 17:31:21 -04:00
Greg Helton
5552a8501b Add support for Gentoo 2019-07-06 01:43:02 -04:00
Lea Anthony
b997becb2f chore: bump version 2019-07-03 19:47:03 +10:00
Lea Anthony
753516bab7 Merge pull request #149 from wailsapp/148-Support-Distribution-'Zorin'
148 support distribution 'zorin'
2019-07-03 19:46:20 +10:00
Lea Anthony
89992d8636 feat: attempt to support Zorin 2019-07-03 19:42:32 +10:00
Lea Anthony
7dd42f964b Merge pull request #147 from wailsapp/develop
Release v0.17.0
2019-07-02 17:39:41 +10:00
Lea Anthony
078a7a5519 feat: release v0.17.0 2019-07-02 17:38:18 +10:00
Lea Anthony
d811f721ac docs: be explicit about node version 2019-07-01 21:50:16 +10:00
Lea Anthony
26950ba045 chore: version bump 2019-07-01 21:36:07 +10:00
Lea Anthony
80adb70e78 chore: version bump 2019-07-01 21:35:30 +10:00
Lea Anthony
0c042acd4a fix: template name order 2019-07-01 21:35:06 +10:00
Lea Anthony
0ad0c4151a feat: use slicer v1.3.2 2019-07-01 18:27:10 +10:00
Lea Anthony
65a8a1e1f7 feat: sort templates by name 2019-07-01 18:26:24 +10:00
Lea Anthony
a2ac8af882 chore: Bump to version v0.16.5-pre 2019-07-01 18:10:26 +10:00
Lea Anthony
52afbd3f15 fix: revert back to my-app 2019-07-01 09:01:07 +10:00
Lea Anthony
ce35ee5ca8 Merge branch 'bh90210-118-angular-support' into develop 2019-07-01 08:58:37 +10:00
Lea Anthony
74c64c6420 chore: misc updates for template 2019-07-01 08:57:42 +10:00
Lea Anthony
678328b7aa Merge branch '118-angular-support' of git://github.com/bh90210/wails into bh90210-118-angular-support 2019-06-30 18:11:35 +10:00
Lea Anthony
345c8bc094 fix: linting fixes 2019-06-30 17:16:38 +10:00
Lea Anthony
44386490c8 chore: add default case to bridge switch 2019-06-29 23:03:46 +10:00
bh90210
87158a342a feat: angular template 2019-06-29 15:45:48 +03:00
Lea Anthony
0b43fae32a docs: updated contributors 2019-06-29 21:43:19 +10:00
Lea Anthony
89f7a95167 Merge pull request #141 from wailsapp/138-Template-dependency-checker
feat: initial support for template dependencies
2019-06-27 19:56:57 +10:00
Lea Anthony
c5be3e5634 feat: initial support for template dependencies 2019-06-27 09:05:46 +10:00
Lea Anthony
7156740f6c fix: typo 2019-06-27 08:46:01 +10:00
Lea Anthony
2d29b626c7 chore: version bump 2019-06-26 19:50:22 +10:00
Lea Anthony
698145be1e Merge pull request #140 from wailsapp/139-go.mod-should-reflect-current-wails-version
feat: set wails version in go.mod
2019-06-26 19:48:35 +10:00
Lea Anthony
a9188cbfdd feat: set wails version in go.mod 2019-06-26 19:46:51 +10:00
Lea Anthony
d07cca0278 chore: version bump 2019-06-25 18:49:18 +10:00
Lea Anthony
60d1dc51ad Merge pull request #137 from wailsapp/135-Add-Debian-support
135 add debian support
2019-06-25 18:48:24 +10:00
Lea Anthony
105073e412 fix: add Debian support across tooling 2019-06-25 18:32:25 +10:00
Lea Anthony
9d1f1fff47 feat: debian support 2019-06-25 18:29:57 +10:00
Lea Anthony
08050ec35e Merge pull request #134 from wailsapp/133-Unsupported-Platform-Requests
133 unsupported platform requests
2019-06-25 08:18:06 +10:00
Lea Anthony
bd9751d888 feat: Support distribution support requests 2019-06-25 08:13:20 +10:00
Lea Anthony
7d171b0907 feat: initial support for platform requests 2019-06-24 09:11:06 +10:00
Lea Anthony
5b8f311465 Merge pull request #130 from wailsapp/123-Unify-Runtime-APIs
fix: linting
2019-06-22 08:47:25 +10:00
Lea Anthony
801465ac51 fix: linting 2019-06-22 08:45:46 +10:00
Lea Anthony
a84e2ae9b3 Merge pull request #129 from wailsapp/123-Unify-Runtime-APIs
feat: unify runtime API signatures
2019-06-22 08:43:23 +10:00
Lea Anthony
9496d1d47f Merge pull request #126 from bh90210/121-react-template-build-error
fix(react template): reverting bugfix + code clean-up
2019-06-22 08:39:16 +10:00
Lea Anthony
36e575e0a2 feat: unify runtime API signatures 2019-06-22 08:36:11 +10:00
ktc
70ccb8942b fix(react template): reverting bugfix + code clean-up 2019-06-19 20:09:23 +03:00
Lea Anthony
d3cd3d43bd chore: version bump 2019-06-19 21:04:27 +10:00
Lea Anthony
9116f0d06c Merge pull request #124 from wailsapp/116-Add-browser-methods-to-runtime
116 add browser methods to runtime
2019-06-19 21:01:53 +10:00
Lea Anthony
3e02e1676a Merge pull request #122 from bh90210/121-react-template-build-error
fix(react template): build bugfix
2019-06-19 08:31:57 +10:00
ktc
f2519e5af2 fix(react template): build bugfix 2019-06-18 13:57:45 +03:00
Lea Anthony
63f1767755 Merge pull request #120 from wailsapp/develop
Release v0.16.0
2019-06-18 08:25:24 +10:00
128 changed files with 10453 additions and 1157 deletions

View File

@@ -1,42 +0,0 @@
{{ if .Versions -}}
<a name="unreleased"></a>
## [Unreleased]
{{ if .Unreleased.CommitGroups -}}
{{ range .Unreleased.CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{ range .Versions }}
<a name="{{ .Tag.Name }}"></a>
## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }}
{{ range .CommitGroups -}}
### {{ .Title }}
{{ range .Commits -}}
- {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }}
{{ end }}
{{ end -}}
{{- if .NoteGroups -}}
{{ range .NoteGroups -}}
### {{ .Title }}
{{ range .Notes }}
{{ .Body }}
{{ end }}
{{ end -}}
{{ end -}}
{{ end -}}
{{- if .Versions }}
[Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD
{{ range .Versions -}}
{{ if .Tag.Previous -}}
[{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}
{{ end -}}
{{ end -}}
{{ end -}}

View File

@@ -1,27 +0,0 @@
style: github
template: CHANGELOG.tpl.md
info:
title: CHANGELOG
repository_url: https://github.com/wailsapp/wails
options:
commits:
# filters:
# Type:
# - feat
# - fix
# - perf
# - refactor
commit_groups:
# title_maps:
# feat: Features
# fix: Bug Fixes
# perf: Performance Improvements
# refactor: Code Refactoring
header:
pattern: "^(\\w*)\\:\\s(.*)$"
pattern_maps:
- Type
- Subject
notes:
keywords:
- BREAKING CHANGE

1
.eslintignore Normal file
View File

@@ -0,0 +1 @@
runtime/js/dist/wails.js

29
.eslintrc Normal file
View File

@@ -0,0 +1,29 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2016,
"sourceType": "module",
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

2
.gitignore vendored
View File

@@ -16,4 +16,4 @@ examples/**/example*
cmd/wails/wails cmd/wails/wails
.DS_Store .DS_Store
tmp tmp
dist node_modules

6
.hound.yml Normal file
View File

@@ -0,0 +1,6 @@
jshint:
config_file: .jshintrc
eslint:
enabled: true
config_file: .eslintrc
ignore_file: .eslintignore

View File

@@ -1,3 +1,4 @@
{ {
"go.formatTool": "goimports" "go.formatTool": "goimports",
"eslint.alwaysShowStatus": true
} }

View File

@@ -12,3 +12,5 @@ Wails is what it is because of the time and effort given by these great people.
* [intelwalk](https://github.com/intelwalk) * [intelwalk](https://github.com/intelwalk)
* [Mark Stenglein](https://github.com/ocelotsloth) * [Mark Stenglein](https://github.com/ocelotsloth)
* [admin_3.exe](https://github.com/bh90210) * [admin_3.exe](https://github.com/bh90210)
* [iceleo-com](https://github.com/iceleo-com)
* [fallendusk](https://github.com/fallendusk)

View File

@@ -46,7 +46,7 @@ Make sure you have the xcode command line tools installed. This can be done by r
### Linux ### Linux
#### Ubuntu 18.04 #### Ubuntu 18.04, Debian 9, Zorin 15
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev` `sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`

51
app.go
View File

@@ -2,6 +2,13 @@ package wails
import ( import (
"github.com/wailsapp/wails/cmd" "github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/runtime/go/runtime"
"github.com/wailsapp/wails/lib/renderer"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces"
) )
// -------------------------------- Compile time Flags ------------------------------ // -------------------------------- Compile time Flags ------------------------------
@@ -13,15 +20,15 @@ var BuildMode = cmd.BuildModeProd
// App defines the main application struct // App defines the main application struct
type App struct { type App struct {
config *AppConfig // The Application configuration object config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli cli *cmd.Cli // In debug mode, we have a cli
renderer Renderer // The renderer is what we will render the app to renderer interfaces.Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app logLevel string // The log level of the app
ipc *ipcManager // Handles the IPC calls ipc interfaces.IPCManager // Handles the IPC calls
log *CustomLogger // Logger log *logger.CustomLogger // Logger
bindingManager *bindingManager // Handles binding of Go code to renderer bindingManager interfaces.BindingManager // Handles binding of Go code to renderer
eventManager *eventManager // Handles all the events eventManager interfaces.EventManager // Handles all the events
runtime *Runtime // The runtime object for registered structs runtime interfaces.Runtime // The runtime object for registered structs
} }
// CreateApp creates the application window with the given configuration // CreateApp creates the application window with the given configuration
@@ -34,14 +41,14 @@ func CreateApp(optionalConfig ...*AppConfig) *App {
result := &App{ result := &App{
logLevel: "info", logLevel: "info",
renderer: &webViewRenderer{}, renderer: renderer.NewWebView(),
ipc: newIPCManager(), ipc: ipc.NewManager(),
bindingManager: newBindingManager(), bindingManager: binding.NewManager(),
eventManager: newEventManager(), eventManager: event.NewManager(),
log: newCustomLogger("App"), log: logger.NewCustomLogger("App"),
} }
appconfig, err := newAppConfig(userConfig) appconfig, err := newConfig(userConfig)
if err != nil { if err != nil {
result.log.Fatalf("Cannot use custom HTML: %s", err.Error()) result.log.Fatalf("Cannot use custom HTML: %s", err.Error())
} }
@@ -75,14 +82,14 @@ func (a *App) Run() error {
func (a *App) start() error { func (a *App) start() error {
// Set the log level // Set the log level
setLogLevel(a.logLevel) logger.SetLogLevel(a.logLevel)
// Log starup // Log starup
a.log.Info("Starting") a.log.Info("Starting")
// Check if we are to run in headless mode // Check if we are to run in headless mode
if BuildMode == cmd.BuildModeBridge { if BuildMode == cmd.BuildModeBridge {
a.renderer = &Headless{} a.renderer = &renderer.Headless{}
} }
// Initialise the renderer // Initialise the renderer
@@ -92,16 +99,16 @@ func (a *App) start() error {
} }
// Start event manager and give it our renderer // Start event manager and give it our renderer
a.eventManager.start(a.renderer) a.eventManager.Start(a.renderer)
// Start the IPC Manager and give it the event manager and binding manager // Start the IPC Manager and give it the event manager and binding manager
a.ipc.start(a.eventManager, a.bindingManager) a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime // Create the runtime
a.runtime = newRuntime(a.eventManager, a.renderer) a.runtime = runtime.NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer // Start binding manager and give it our renderer
err = a.bindingManager.start(a.renderer, a.runtime) err = a.bindingManager.Start(a.renderer, a.runtime)
if err != nil { if err != nil {
return err return err
} }
@@ -113,5 +120,5 @@ func (a *App) start() error {
// Bind allows the user to bind the given object // Bind allows the user to bind the given object
// with the application // with the application
func (a *App) Bind(object interface{}) { func (a *App) Bind(object interface{}) {
a.bindingManager.bind(object) a.bindingManager.Bind(object)
} }

File diff suppressed because one or more lines are too long

View File

@@ -271,7 +271,7 @@ func InstallBridge(caller string, projectDir string, projectOptions *ProjectOpti
} }
// Copy bridge to project // Copy bridge to project
bridgeAssets := mewn.Group("../wailsruntimeassets/bridge/") bridgeAssets := mewn.Group("../runtime/bridge/")
bridgeFileData := bridgeAssets.Bytes(bridgeFile) bridgeFileData := bridgeAssets.Bytes(bridgeFile)
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js") bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js")
err := fs.CreateFile(bridgeFileTarget, bridgeFileData) err := fs.CreateFile(bridgeFileTarget, bridgeFileData)

View File

@@ -3,9 +3,12 @@ package cmd
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url"
"os" "os"
"regexp" "runtime"
"strings" "strings"
"github.com/pkg/browser"
) )
// LinuxDistribution is of type int // LinuxDistribution is of type int
@@ -20,6 +23,12 @@ const (
Arch Arch
// RedHat linux distribution // RedHat linux distribution
RedHat RedHat
// Debian distribution
Debian
// Gentoo distribution
Gentoo
// Zorin distribution
Zorin
) )
// DistroInfo contains all the information relating to a linux distribution // DistroInfo contains all the information relating to a linux distribution
@@ -29,6 +38,7 @@ type DistroInfo struct {
Release string Release string
Codename string Codename string
DistributorID string DistributorID string
DiscoveredBy string
} }
// GetLinuxDistroInfo returns information about the running linux distribution // GetLinuxDistroInfo returns information about the running linux distribution
@@ -43,7 +53,7 @@ func GetLinuxDistroInfo() *DistroInfo {
if err != nil { if err != nil {
return result return result
} }
result.DiscoveredBy = "lsb"
for _, line := range strings.Split(stdout, "\n") { for _, line := range strings.Split(stdout, "\n") {
if strings.Contains(line, ":") { if strings.Contains(line, ":") {
// Iterate lines a // Iterate lines a
@@ -58,6 +68,14 @@ func GetLinuxDistroInfo() *DistroInfo {
result.Distribution = Ubuntu result.Distribution = Ubuntu
case "Arch", "ManjaroLinux": case "Arch", "ManjaroLinux":
result.Distribution = Arch result.Distribution = Arch
case "Debian":
result.Distribution = Debian
case "Gentoo":
result.Distribution = Gentoo
case "Zorin":
result.Distribution = Zorin
case "Fedora":
result.Distribution = RedHat
} }
case "Description": case "Description":
result.Description = value result.Description = value
@@ -65,21 +83,37 @@ func GetLinuxDistroInfo() *DistroInfo {
result.Release = value result.Release = value
case "Codename": case "Codename":
result.Codename = value result.Codename = value
} }
} }
} }
// check if /etc/os-release exists // check if /etc/os-release exists
} else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) { } else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) {
// Default value
osName := "Unknown"
version := ""
// read /etc/os-release // read /etc/os-release
osRelease, _ := ioutil.ReadFile("/etc/os-release") osRelease, _ := ioutil.ReadFile("/etc/os-release")
// compile a regex to find NAME=distro // Split into lines
re := regexp.MustCompile(`^NAME=(.*)\n`) lines := strings.Split(string(osRelease), "\n")
// extract the distro name // Iterate lines
osName := string(re.FindSubmatch(osRelease)[1]) for _, line := range lines {
// strip quotations // Split each line by the equals char
osName = strings.Trim(osName, "\"") splitLine := strings.SplitN(line, "=", 2)
// Check we have
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "NAME":
osName = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
version = strings.Trim(splitLine[1], "\"")
}
}
// Check distro name against list of distros // Check distro name against list of distros
result.Release = version
result.DiscoveredBy = "os-release"
switch osName { switch osName {
case "Fedora": case "Fedora":
result.Distribution = RedHat result.Distribution = RedHat
@@ -87,11 +121,29 @@ func GetLinuxDistroInfo() *DistroInfo {
result.Distribution = RedHat result.Distribution = RedHat
case "Arch Linux": case "Arch Linux":
result.Distribution = Arch result.Distribution = Arch
case "Debian GNU/Linux":
result.Distribution = Debian
case "Gentoo/Linux":
result.Distribution = Gentoo
default:
result.Distribution = Unknown
result.DistributorID = osName
} }
} }
return result return result
} }
// EqueryInstalled uses equery to see if a package is installed
func EqueryInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
equery := program.FindProgram("equery")
if equery == nil {
return false, fmt.Errorf("cannont check dependencies: equery not found")
}
_, _, exitCode, _ := equery.Run("l", packageName)
return exitCode == 0, nil
}
// DpkgInstalled uses dpkg to see if a package is installed // DpkgInstalled uses dpkg to see if a package is installed
func DpkgInstalled(packageName string) (bool, error) { func DpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper() program := NewProgramHelper()
@@ -124,3 +176,45 @@ func RpmInstalled(packageName string) (bool, error) {
_, _, exitCode, _ := rpm.Run("--query", packageName) _, _, exitCode, _ := rpm.Run("--query", packageName)
return exitCode == 0, nil return exitCode == 0, nil
} }
// RequestSupportForDistribution promts the user to submit a request to support their
// currently unsupported distribution
func RequestSupportForDistribution(distroInfo *DistroInfo, libraryName string) error {
var logger = NewLogger()
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, libraryName)
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.DistributorID)
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.DistributorID)
result := Prompt(q, "yes")
if strings.ToLower(result) != "yes" {
return defaultError
}
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.DistributorID)
var str strings.Builder
gomodule, exists := os.LookupEnv("GO111MODULE")
if !exists {
gomodule = "(Not Set)"
}
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", Version))
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.DistributorID))
str.WriteString(fmt.Sprintf("| Distribution Version | %s |\n", distroInfo.Release))
str.WriteString(fmt.Sprintf("| Discovered by | %s |\n", distroInfo.DiscoveredBy))
body := fmt.Sprintf("**Description**\nDistribution '%s' is currently unsupported.\n\n**Further Information**\n\n%s\n\n*Please add any extra information here, EG: libraries that are needed to make the distribution work, or commands to install them*", distroInfo.DistributorID, str.String())
fullURL := "https://github.com/wailsapp/wails/issues/new?"
params := "title=" + title + "&body=" + body
fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params))
return nil
}

View File

@@ -49,11 +49,10 @@ func getRequiredProgramsLinux() *Prerequisites {
result := &Prerequisites{} result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo() distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution { switch distroInfo.Distribution {
case Ubuntu: case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again")) result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again")) result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again"))
result.Add(newPrerequisite("npm", "Please install with `sudo snap install node --channel=12/stable --classic` and try again")) result.Add(newPrerequisite("npm", "Please install with `sudo snap install node --channel=12/stable --classic` and try again"))
default: default:
result.Add(newPrerequisite("gcc", "Please install with your system package manager and try again")) result.Add(newPrerequisite("gcc", "Please install with your system package manager and try again"))
result.Add(newPrerequisite("pkg-config", "Please install with your system package manager and try again")) result.Add(newPrerequisite("pkg-config", "Please install with your system package manager and try again"))
@@ -94,9 +93,12 @@ func getRequiredLibrariesLinux() (*Prerequisites, error) {
result := &Prerequisites{} result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo() distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution { switch distroInfo.Distribution {
case Ubuntu: case Ubuntu, Debian, Zorin:
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again")) result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again")) result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again"))
case Gentoo:
result.Add(newPrerequisite("gtk+:3", "Please install with `sudo emerge gtk+:3` and try again"))
result.Add(newPrerequisite("webkit-gtk", "Please install with `sudo emerge webkit-gtk` and try again"))
case Arch: case Arch:
result.Add(newPrerequisite("gtk3", "Please install with `sudo pacman -S gtk3` and try again")) result.Add(newPrerequisite("gtk3", "Please install with `sudo pacman -S gtk3` and try again"))
result.Add(newPrerequisite("webkit2gtk", "Please install with `sudo pacman -S webkit2gtk` and try again")) result.Add(newPrerequisite("webkit2gtk", "Please install with `sudo pacman -S webkit2gtk` and try again"))

View File

@@ -7,6 +7,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort"
"strings" "strings"
"github.com/leaanthony/slicer" "github.com/leaanthony/slicer"
@@ -143,11 +144,13 @@ type ProjectOptions struct {
log *Logger log *Logger
templates *TemplateHelper templates *TemplateHelper
selectedTemplate *TemplateDetails selectedTemplate *TemplateDetails
WailsVersion string
} }
// Defaults sets the default project template // Defaults sets the default project template
func (po *ProjectOptions) Defaults() { func (po *ProjectOptions) Defaults() {
po.Template = "vuebasic" po.Template = "vuebasic"
po.WailsVersion = Version
} }
// PromptForInputs asks the user to input project details // PromptForInputs asks the user to input project details
@@ -182,7 +185,13 @@ func (po *ProjectOptions) PromptForInputs() error {
po.selectedTemplate = templateDetails[po.Template] po.selectedTemplate = templateDetails[po.Template]
} else { } else {
for _, templateDetail := range templateDetails { keys := make([]string, 0)
for k := range templateDetails {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
templateDetail := templateDetails[k]
templateList.Add(templateDetail) templateList.Add(templateDetail)
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription)) options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
} }

View File

@@ -272,7 +272,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
distroInfo := GetLinuxDistroInfo() distroInfo := GetLinuxDistroInfo()
for _, library := range *requiredLibraries { for _, library := range *requiredLibraries {
switch distroInfo.Distribution { switch distroInfo.Distribution {
case Ubuntu: case Ubuntu, Zorin, Debian:
installed, err := DpkgInstalled(library.Name) installed, err := DpkgInstalled(library.Name)
if err != nil { if err != nil {
return false, err return false, err
@@ -295,7 +295,6 @@ func CheckDependencies(logger *Logger) (bool, error) {
logger.Green("Library '%s' installed.", library.Name) logger.Green("Library '%s' installed.", library.Name)
} }
case RedHat: case RedHat:
installed, err := RpmInstalled(library.Name) installed, err := RpmInstalled(library.Name)
if err != nil { if err != nil {
return false, err return false, err
@@ -306,8 +305,19 @@ func CheckDependencies(logger *Logger) (bool, error) {
} else { } else {
logger.Green("Library '%s' installed.", library.Name) logger.Green("Library '%s' installed.", library.Name)
} }
case Gentoo:
installed, err := EqueryInstalled(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
default: default:
return false, fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, library.Name) return false, RequestSupportForDistribution(distroInfo, library.Name)
} }
} }
} }

View File

@@ -16,18 +16,26 @@ import (
// TemplateMetadata holds all the metadata for a Wails template // TemplateMetadata holds all the metadata for a Wails template
type TemplateMetadata struct { type TemplateMetadata struct {
Name string `json:"name"` Name string `json:"name"`
Version string `json:"version"` Version string `json:"version"`
ShortDescription string `json:"shortdescription"` ShortDescription string `json:"shortdescription"`
Description string `json:"description"` Description string `json:"description"`
Install string `json:"install"` Install string `json:"install"`
Build string `json:"build"` Build string `json:"build"`
Author string `json:"author"` Author string `json:"author"`
Created string `json:"created"` Created string `json:"created"`
FrontendDir string `json:"frontenddir"` FrontendDir string `json:"frontenddir"`
Serve string `json:"serve"` Serve string `json:"serve"`
Bridge string `json:"bridge"` Bridge string `json:"bridge"`
WailsDir string `json:"wailsdir"` WailsDir string `json:"wailsdir"`
TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"`
}
// TemplateDependency defines a binary dependency for the template
// EG: ng for angular
type TemplateDependency struct {
Bin string `json:"bin"`
Help string `json:"help"`
} }
// TemplateDetails holds information about a specific template // TemplateDetails holds information about a specific template
@@ -152,6 +160,31 @@ func (t *TemplateHelper) GetTemplateFilenames(template *TemplateDetails) (*slice
// project path given // project path given
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error { func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
// Check dependencies before installing
dependencies := projectOptions.selectedTemplate.Metadata.TemplateDependencies
if dependencies != nil {
programHelper := NewProgramHelper()
logger := NewLogger()
errors := []string{}
for _, dep := range dependencies {
program := programHelper.FindProgram(dep.Bin)
if program == nil {
errors = append(errors, dep.Help)
}
}
if len(errors) > 0 {
mainError := "template dependencies not installed"
if len(errors) == 1 {
mainError = errors[0]
} else {
for _, error := range errors {
logger.Red(error)
}
}
return fmt.Errorf(mainError)
}
}
// Get template files // Get template files
templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate) templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate)
if err != nil { if err != nil {
@@ -160,6 +193,9 @@ func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *Pro
templatePath := projectOptions.selectedTemplate.Path templatePath := projectOptions.selectedTemplate.Path
// Save the version
projectOptions.WailsVersion = Version
templateJSONFilename := filepath.Join(templatePath, t.metadataFilename) templateJSONFilename := filepath.Join(templatePath, t.metadataFilename)
templateFiles := templateFilenames.Filter(func(filename string) bool { templateFiles := templateFilenames.Filter(func(filename string) bool {

View File

@@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View File

@@ -0,0 +1,47 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
.editorcinfig

View File

@@ -0,0 +1,27 @@
# MyApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.3.
## Development server
Run `npx ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `npx ng generate component component-name` to generate a new component. You can also use `npx ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `npx ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `npx ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `npx ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `npx ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -0,0 +1,121 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"my-app": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "ngx-build-plus:browser",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "ngx-build-plus:dev-server",
"options": {
"browserTarget": "my-app:build"
},
"configurations": {
"production": {
"browserTarget": "my-app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "my-app:build"
}
},
"test": {
"builder": "ngx-build-plus:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "my-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "my-app:serve:production"
}
}
}
}
}
},
"defaultProject": "my-app"
}

View File

@@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.

View File

@@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to my-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View File

@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View File

@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View File

@@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/my-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@@ -0,0 +1,50 @@
{
"name": "my-app",
"version": "0.0.0",
"scripts": {
"ng": "npx ng",
"start": "npx ng serve --poll=2000",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"test": "npx ng test",
"lint": "npx ng lint",
"e2e": "npx ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.0.2",
"@angular/cdk": "^8.0.1",
"@angular/common": "~8.0.1",
"@angular/compiler": "~8.0.1",
"@angular/core": "~8.0.1",
"@angular/forms": "~8.0.1",
"@angular/material": "^8.0.1",
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.3",
"@angular/compiler-cli": "~8.0.1",
"@angular/language-service": "~8.0.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

View File

@@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [RouterModule]
})
export class AppRoutingModule { }

View File

@@ -0,0 +1,14 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
<br />
<button (click)="onClickMe()">Hello</button>
<p>{{clickMessage}}</p>
</div>
<router-outlet></router-outlet>

View File

@@ -0,0 +1,35 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'my-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('my-app');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to my-app!');
});
});

View File

@@ -0,0 +1,19 @@
import { Component } from '@angular/core';
@Component({
selector: '[id="app"]',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
clickMessage = '';
onClickMe() {
// @ts-ignore
window.backend.basic().then(result =>
this.clickMessage = result
);
}
}

View File

@@ -0,0 +1,20 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_BASE_HREF } from '@angular/common';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [{provide: APP_BASE_HREF, useValue : '/' }],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>my-app</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,18 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import 'zone.js'
import Bridge from './wailsbridge';
if (environment.production) {
enableProdMode();
}
Bridge.Start(() => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
});

View File

@@ -0,0 +1,63 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
//import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -0,0 +1,24 @@
/* You can add global styles to this file, and also import other style files */
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #282c34;
}
p {
color: white
}
h1 {
color: white
}
button {
background-color: white;
color: black;
}

View File

@@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/test.ts",
"src/**/*.spec.ts"
]
}

View File

@@ -0,0 +1,23 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2018",
"dom"
]
}
}

View File

@@ -0,0 +1,18 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -0,0 +1,92 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warn"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-use-before-declare": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}

View File

@@ -0,0 +1,5 @@
module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -0,0 +1,27 @@
package main
import (
"github.com/leaanthony/mewn"
"github.com/wailsapp/wails"
)
func basic() string {
return "World!"
}
func main() {
js := mewn.String("./frontend/dist/my-app/main-es2015.js")
css := mewn.String("./frontend/dist/my-app/styles.css")
app := wails.CreateApp(&wails.AppConfig{
Width: 1024,
Height: 768,
Title: "{{.Name}}",
JS: js,
CSS: css,
Colour: "#131313",
})
app.Bind(basic)
app.Run()
}

View File

@@ -0,0 +1,20 @@
{
"name": "Angular",
"version": "1.0.0",
"shortdescription": "Angular 8 template (Requires node 10.8+)",
"description": "Angular projects w/ @angular/cli - Note: in order to reach the cli use npx like this: npx ng",
"dependencies": [
{
"bin": "npx",
"help": "This template requires 'npx'. Please install with 'npm install -g npx'"
}
],
"install": "npm install",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"author": "bh90210 <ktc@pm.me>",
"created": "2019-06-15 18:23:48.666414555 +0300 EEST m=+223.934866008",
"frontenddir": "frontend",
"serve": "npx ng serve --poll=2000",
"bridge": "src",
"wailsdir": ""
}

View File

@@ -0,0 +1,3 @@
{
"esversion": 6
}

View File

@@ -26,22 +26,8 @@ class HelloWorld extends React.Component {
this.setState({ showModal: false }); this.setState({ showModal: false });
} }
startAsync() {
this.setState({
loading: true
});
window.backend.basic().then(result =>
this.setState({
loading: false,
result
})
);
}
render() { render() {
const { loading, result } = this.state; const { result } = this.state;
return ( return (
<div className="App"> <div className="App">
<button onClick={this.handleOpenModal} type="button"> <button onClick={this.handleOpenModal} type="button">

View File

@@ -9,9 +9,9 @@
export default { export default {
// The main function // The main function
// Passes the main Wails object to the callback if given. // Passes the main Wails object to the callback if given.
Start: function(callback) { Start: function (callback) {
if (callback) { if (callback) {
window.wails.events.on("wails:ready", callback); window.wails.Events.On("wails:ready", callback);
} }
} }
}; };

View File

@@ -1 +1,5 @@
module {{.BinaryName}} module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -0,0 +1,3 @@
{
"esversion": 6
}

View File

@@ -9,9 +9,9 @@
export default { export default {
// The main function // The main function
// Passes the main Wails object to the callback if given. // Passes the main Wails object to the callback if given.
Start: function(callback) { Start: function (callback) {
if (callback) { if (callback) {
window.wails.events.on("wails:ready", callback); window.wails.Events.On("wails:ready", callback);
} }
} }
}; };

View File

@@ -1 +1,5 @@
module {{.BinaryName}} module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -1 +1,5 @@
module {{.BinaryName}} module {{.BinaryName}}
require (
github.com/wailsapp/wails {{.WailsVersion}}
)

View File

@@ -1,4 +1,4 @@
package cmd package cmd
// Version - Wails version // Version - Wails version
const Version = "v0.16.0" const Version = "v0.17.4-pre"

View File

@@ -96,7 +96,7 @@ func checkLibraries() (errors bool, err error) {
distroInfo := cmd.GetLinuxDistroInfo() distroInfo := cmd.GetLinuxDistroInfo()
for _, library := range *requiredLibraries { for _, library := range *requiredLibraries {
switch distroInfo.Distribution { switch distroInfo.Distribution {
case cmd.Ubuntu: case cmd.Ubuntu, cmd.Zorin, cmd.Debian:
installed, err := cmd.DpkgInstalled(library.Name) installed, err := cmd.DpkgInstalled(library.Name)
if err != nil { if err != nil {
return false, err return false, err
@@ -108,7 +108,7 @@ func checkLibraries() (errors bool, err error) {
logger.Green("Library '%s' installed.", library.Name) logger.Green("Library '%s' installed.", library.Name)
} }
default: default:
return false, fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, library.Name) return false, cmd.RequestSupportForDistribution(distroInfo, library.Name)
} }
} }
} }

View File

@@ -1,11 +1,5 @@
package wails package wails
import (
"strings"
"github.com/dchest/htmlmin"
"github.com/leaanthony/mewn"
)
// AppConfig is the configuration structure used when creating a Wails App object // AppConfig is the configuration structure used when creating a Wails App object
type AppConfig struct { type AppConfig struct {
@@ -18,7 +12,51 @@ type AppConfig struct {
Colour string Colour string
Resizable bool Resizable bool
DisableInspector bool DisableInspector bool
isHTMLFragment bool }
// GetWidth returns the desired width
func (a *AppConfig) GetWidth() int {
return a.Width
}
// GetHeight returns the desired height
func (a *AppConfig) GetHeight() int {
return a.Height
}
// GetTitle returns the desired window title
func (a *AppConfig) GetTitle() string {
return a.Title
}
// GetDefaultHTML returns the desired window title
func (a *AppConfig) GetDefaultHTML() string {
return a.defaultHTML
}
// GetResizable returns true if the window should be resizable
func (a *AppConfig) GetResizable() bool {
return a.Resizable
}
// GetDisableInspector returns true if the inspector should be disabled
func (a *AppConfig) GetDisableInspector() bool {
return a.DisableInspector
}
// GetColour returns the colour
func (a *AppConfig) GetColour() string {
return a.Colour
}
// GetCSS returns the user CSS
func (a *AppConfig) GetCSS() string {
return a.CSS
}
// GetJS returns the user Javascript
func (a *AppConfig) GetJS() string {
return a.JS
} }
func (a *AppConfig) merge(in *AppConfig) error { func (a *AppConfig) merge(in *AppConfig) error {
@@ -28,32 +66,6 @@ func (a *AppConfig) merge(in *AppConfig) error {
if in.Title != "" { if in.Title != "" {
a.Title = in.Title a.Title = in.Title
} }
if in.HTML != "" {
minified, err := htmlmin.Minify([]byte(in.HTML), &htmlmin.Options{
MinifyScripts: true,
})
if err != nil {
return err
}
inlineHTML := string(minified)
inlineHTML = strings.Replace(inlineHTML, "'", "\\'", -1)
inlineHTML = strings.Replace(inlineHTML, "\n", " ", -1)
a.HTML = strings.TrimSpace(inlineHTML)
// Deduce whether this is a full html page or a fragment
// The document is determined to be a fragment if an HTML
// tag exists and is located before the first div tag
HTMLTagIndex := strings.Index(a.HTML, "<html")
DivTagIndex := strings.Index(a.HTML, "<div")
if HTMLTagIndex == -1 {
a.isHTMLFragment = true
} else {
if DivTagIndex < HTMLTagIndex {
a.isHTMLFragment = true
}
}
}
if in.Colour != "" { if in.Colour != "" {
a.Colour = in.Colour a.Colour = in.Colour
@@ -76,14 +88,13 @@ func (a *AppConfig) merge(in *AppConfig) error {
} }
// Creates the default configuration // Creates the default configuration
func newAppConfig(userConfig *AppConfig) (*AppConfig, error) { func newConfig(userConfig *AppConfig) (*AppConfig, error) {
result := &AppConfig{ result := &AppConfig{
Width: 800, Width: 800,
Height: 600, Height: 600,
Resizable: true, Resizable: true,
Title: "My Wails App", Title: "My Wails App",
Colour: "#FFF", // White by default Colour: "#FFF", // White by default
HTML: mewn.String("./wailsruntimeassets/default/default.html"),
} }
if userConfig != nil { if userConfig != nil {

2
go.mod
View File

@@ -12,7 +12,7 @@ require (
github.com/kennygrant/sanitize v1.2.4 github.com/kennygrant/sanitize v1.2.4
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/leaanthony/mewn v0.10.7 github.com/leaanthony/mewn v0.10.7
github.com/leaanthony/slicer v1.3.1 github.com/leaanthony/slicer v1.3.2
github.com/leaanthony/spinner v0.5.3 github.com/leaanthony/spinner v0.5.3
github.com/masterminds/semver v1.4.2 github.com/masterminds/semver v1.4.2
github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-colorable v0.1.1 // indirect

4
go.sum
View File

@@ -33,8 +33,8 @@ github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/leaanthony/mewn v0.10.7 h1:jCcNJyIUOpwj+I5SuATvCugDjHkoo+j6ubEOxxrxmPA= github.com/leaanthony/mewn v0.10.7 h1:jCcNJyIUOpwj+I5SuATvCugDjHkoo+j6ubEOxxrxmPA=
github.com/leaanthony/mewn v0.10.7/go.mod h1:CRkTx8unLiSSilu/Sd7i1LwrdaAL+3eQ3ses99qGMEQ= github.com/leaanthony/mewn v0.10.7/go.mod h1:CRkTx8unLiSSilu/Sd7i1LwrdaAL+3eQ3ses99qGMEQ=
github.com/leaanthony/slicer v1.3.1 h1:n2iIV2sxvL/3bpnmVY0vBjXf3yYFWcB6CYLVMrzJxRw= github.com/leaanthony/slicer v1.3.2 h1:kGWWFoyaY5WzwGrUsHXMmGbssuYthP4qYBNlkNpNAB8=
github.com/leaanthony/slicer v1.3.1/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ= github.com/leaanthony/slicer v1.3.2/go.mod h1:VMB/HGvr3uR3MRpFWHWAm0w+DHQLzPHYe2pKfpFlQIQ=
github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y= github.com/leaanthony/spinner v0.5.3 h1:IMTvgdQCec5QA4qRy0wil4XsRP+QcG1OwLWVK/LPZ5Y=
github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo= github.com/leaanthony/spinner v0.5.3/go.mod h1:oHlrvWicr++CVV7ALWYi+qHk/XNA91D9IJ48IqmpVUo=
github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8= github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8=

View File

@@ -1,4 +1,4 @@
package wails package binding
import ( import (
"bytes" "bytes"
@@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"runtime" "runtime"
"github.com/wailsapp/wails/lib/logger"
) )
type boundFunction struct { type boundFunction struct {
@@ -14,7 +16,7 @@ type boundFunction struct {
functionType reflect.Type functionType reflect.Type
inputs []reflect.Type inputs []reflect.Type
returnTypes []reflect.Type returnTypes []reflect.Type
log *CustomLogger log *logger.CustomLogger
hasErrorReturnType bool hasErrorReturnType bool
} }
@@ -30,7 +32,7 @@ func newBoundFunction(object interface{}) (*boundFunction, error) {
fullName: name, fullName: name,
function: objectValue, function: objectValue,
functionType: objectType, functionType: objectType,
log: newCustomLogger(name), log: logger.NewCustomLogger(name),
} }
err := result.processParameters() err := result.processParameters()
@@ -55,7 +57,7 @@ func (b *boundFunction) processParameters() error {
b.inputs[index] = param b.inputs[index] = param
typ := param typ := param
index := index index := index
b.log.DebugFields("Input param", Fields{ b.log.DebugFields("Input param", logger.Fields{
"index": index, "index": index,
"name": name, "name": name,
"kind": kind, "kind": kind,

View File

@@ -1,27 +1,33 @@
package wails package binding
import "strings" import (
import "fmt" "fmt"
"strings"
type internalMethods struct{ "github.com/wailsapp/wails/lib/logger"
log *CustomLogger "github.com/wailsapp/wails/lib/messages"
browser *RuntimeBrowser "github.com/wailsapp/wails/runtime/go/runtime"
)
type internalMethods struct {
log *logger.CustomLogger
browser *runtime.Browser
} }
func newInternalMethods() *internalMethods { func newInternalMethods() *internalMethods {
return &internalMethods{ return &internalMethods{
log: newCustomLogger("InternalCall"), log: logger.NewCustomLogger("InternalCall"),
browser: newRuntimeBrowser(), browser: runtime.NewBrowser(),
} }
} }
func (i *internalMethods) processCall(callData *callData) (interface{}, error) { func (i *internalMethods) processCall(callData *messages.CallData) (interface{}, error) {
if !strings.HasPrefix(callData.BindingName, ".wails.") { if !strings.HasPrefix(callData.BindingName, ".wails.") {
return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName) return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName)
} }
// Strip prefix // Strip prefix
var splitCall = strings.Split(callData.BindingName,".")[2:] var splitCall = strings.Split(callData.BindingName, ".")[2:]
if len(splitCall) != 2 { if len(splitCall) != 2 {
return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName) return nil, fmt.Errorf("Invalid call signature '%s'", callData.BindingName)
} }
@@ -37,14 +43,14 @@ func (i *internalMethods) processCall(callData *callData) (interface{}, error) {
func (i *internalMethods) processBrowserCommand(command string, data interface{}) (interface{}, error) { func (i *internalMethods) processBrowserCommand(command string, data interface{}) (interface{}, error) {
switch command { switch command {
case "OpenURL": case "OpenURL":
url := data.(string) url := data.(string)
// Strip string quotes. Credit: https://stackoverflow.com/a/44222648 // Strip string quotes. Credit: https://stackoverflow.com/a/44222648
if url[0] == '"' { if url[0] == '"' {
url = url[1:] url = url[1:]
} }
if i := len(url)-1; url[i] == '"' { if i := len(url) - 1; url[i] == '"' {
url = url[:i] url = url[:i]
} }
i.log.Debugf("Calling Browser.OpenURL with '%s'", url) i.log.Debugf("Calling Browser.OpenURL with '%s'", url)
return nil, i.browser.OpenURL(url) return nil, i.browser.OpenURL(url)
@@ -54,12 +60,12 @@ func (i *internalMethods) processBrowserCommand(command string, data interface{}
if filename[0] == '"' { if filename[0] == '"' {
filename = filename[1:] filename = filename[1:]
} }
if i := len(filename)-1; filename[i] == '"' { if i := len(filename) - 1; filename[i] == '"' {
filename = filename[:i] filename = filename[:i]
} }
i.log.Debugf("Calling Browser.OpenFile with '%s'", filename) i.log.Debugf("Calling Browser.OpenFile with '%s'", filename)
return nil, i.browser.OpenFile(filename) return nil, i.browser.OpenFile(filename)
default: default:
return nil, fmt.Errorf("Unknown Browser command '%s'", command) return nil, fmt.Errorf("Unknown Browser command '%s'", command)
} }
} }

View File

@@ -1,47 +1,46 @@
package wails package binding
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"unicode" "unicode"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
) )
/** // Manager handles method binding
type Manager struct {
binding:
Name() // Full name (package+name)
Call(params)
**/
type bindingManager struct {
methods map[string]*boundMethod methods map[string]*boundMethod
functions map[string]*boundFunction functions map[string]*boundFunction
internalMethods *internalMethods internalMethods *internalMethods
initMethods []*boundMethod initMethods []*boundMethod
log *CustomLogger log *logger.CustomLogger
renderer Renderer renderer interfaces.Renderer
runtime *Runtime // The runtime object to pass to bound structs runtime interfaces.Runtime // The runtime object to pass to bound structs
objectsToBind []interface{} objectsToBind []interface{}
bindPackageNames bool // Package name should be considered when binding bindPackageNames bool // Package name should be considered when binding
} }
func newBindingManager() *bindingManager { // NewManager creates a new Manager struct
result := &bindingManager{ func NewManager() interfaces.BindingManager {
result := &Manager{
methods: make(map[string]*boundMethod), methods: make(map[string]*boundMethod),
functions: make(map[string]*boundFunction), functions: make(map[string]*boundFunction),
log: newCustomLogger("Bind"), log: logger.NewCustomLogger("Bind"),
internalMethods: newInternalMethods(), internalMethods: newInternalMethods(),
} }
return result return result
} }
// Sets flag to indicate package names should be considered when binding // BindPackageNames sets a flag to indicate package names should be considered when binding
func (b *bindingManager) BindPackageNames() { func (b *Manager) BindPackageNames() {
b.bindPackageNames = true b.bindPackageNames = true
} }
func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error { // Start the binding manager
func (b *Manager) Start(renderer interfaces.Renderer, runtime interfaces.Runtime) error {
b.log.Info("Starting") b.log.Info("Starting")
b.renderer = renderer b.renderer = renderer
b.runtime = runtime b.runtime = runtime
@@ -54,7 +53,7 @@ func (b *bindingManager) start(renderer Renderer, runtime *Runtime) error {
return err return err
} }
func (b *bindingManager) initialise() error { func (b *Manager) initialise() error {
var err error var err error
// var binding *boundMethod // var binding *boundMethod
@@ -92,7 +91,7 @@ func (b *bindingManager) initialise() error {
} }
// bind the given struct method // bind the given struct method
func (b *bindingManager) bindMethod(object interface{}) error { func (b *Manager) bindMethod(object interface{}) error {
objectType := reflect.TypeOf(object) objectType := reflect.TypeOf(object)
baseName := objectType.String() baseName := objectType.String()
@@ -142,7 +141,7 @@ func (b *bindingManager) bindMethod(object interface{}) error {
} }
// bind the given function object // bind the given function object
func (b *bindingManager) bindFunction(object interface{}) error { func (b *Manager) bindFunction(object interface{}) error {
newFunction, err := newBoundFunction(object) newFunction, err := newBoundFunction(object)
if err != nil { if err != nil {
@@ -159,18 +158,18 @@ func (b *bindingManager) bindFunction(object interface{}) error {
return nil return nil
} }
// Save the given object to be bound at start time // Bind saves the given object to be bound at start time
func (b *bindingManager) bind(object interface{}) { func (b *Manager) Bind(object interface{}) {
// Store binding // Store binding
b.objectsToBind = append(b.objectsToBind, object) b.objectsToBind = append(b.objectsToBind, object)
} }
func (b *bindingManager) processInternalCall(callData *callData) (interface{}, error) { func (b *Manager) processInternalCall(callData *messages.CallData) (interface{}, error) {
// Strip prefix // Strip prefix
return b.internalMethods.processCall(callData) return b.internalMethods.processCall(callData)
} }
func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, error) { func (b *Manager) processFunctionCall(callData *messages.CallData) (interface{}, error) {
// Return values // Return values
var result []reflect.Value var result []reflect.Value
var err error var err error
@@ -199,7 +198,7 @@ func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, e
return result[0].Interface(), nil return result[0].Interface(), nil
} }
func (b *bindingManager) processMethodCall(callData *callData) (interface{}, error) { func (b *Manager) processMethodCall(callData *messages.CallData) (interface{}, error) {
// Return values // Return values
var result []reflect.Value var result []reflect.Value
var err error var err error
@@ -233,8 +232,8 @@ func (b *bindingManager) processMethodCall(callData *callData) (interface{}, err
return nil, nil return nil, nil
} }
// process an incoming call request // ProcessCall processes the given call request
func (b *bindingManager) processCall(callData *callData) (result interface{}, err error) { func (b *Manager) ProcessCall(callData *messages.CallData) (result interface{}, err error) {
b.log.Debugf("Wanting to call %s", callData.BindingName) b.log.Debugf("Wanting to call %s", callData.BindingName)
// Determine if this is function call or method call by the number of // Determine if this is function call or method call by the number of
@@ -272,7 +271,7 @@ func (b *bindingManager) processCall(callData *callData) (result interface{}, er
// callWailsInitMethods calls all of the WailsInit methods that were // callWailsInitMethods calls all of the WailsInit methods that were
// registered with the runtime object // registered with the runtime object
func (b *bindingManager) callWailsInitMethods() error { func (b *Manager) callWailsInitMethods() error {
// Create reflect value for runtime object // Create reflect value for runtime object
runtimeValue := reflect.ValueOf(b.runtime) runtimeValue := reflect.ValueOf(b.runtime)
params := []reflect.Value{runtimeValue} params := []reflect.Value{runtimeValue}

View File

@@ -1,10 +1,12 @@
package wails package binding
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"github.com/wailsapp/wails/lib/logger"
) )
type boundMethod struct { type boundMethod struct {
@@ -13,7 +15,7 @@ type boundMethod struct {
method reflect.Value method reflect.Value
inputs []reflect.Type inputs []reflect.Type
returnTypes []reflect.Type returnTypes []reflect.Type
log *CustomLogger log *logger.CustomLogger
hasErrorReturnType bool // Indicates if there is an error return type hasErrorReturnType bool // Indicates if there is an error return type
isWailsInit bool isWailsInit bool
} }
@@ -27,7 +29,7 @@ func newBoundMethod(name string, fullName string, method reflect.Value, objectTy
} }
// Setup logger // Setup logger
result.log = newCustomLogger(result.fullName) result.log = logger.NewCustomLogger(result.fullName)
// Check if Parameters are valid // Check if Parameters are valid
err := result.processParameters() err := result.processParameters()
@@ -57,7 +59,7 @@ func (b *boundMethod) processParameters() error {
b.inputs[index] = param b.inputs[index] = param
typ := param typ := param
index := index index := index
b.log.DebugFields("Input param", Fields{ b.log.DebugFields("Input param", logger.Fields{
"index": index, "index": index,
"name": name, "name": name,
"kind": kind, "kind": kind,
@@ -166,10 +168,10 @@ func (b *boundMethod) setInputValue(index int, typ reflect.Type, val interface{}
reflect.Map, reflect.Map,
reflect.Ptr, reflect.Ptr,
reflect.Slice: reflect.Slice:
logger.Debug("Converting nil to type") b.log.Debug("Converting nil to type")
result = reflect.ValueOf(val).Convert(typ) result = reflect.ValueOf(val).Convert(typ)
default: default:
logger.Debug("Cannot convert nil to type, returning error") b.log.Debug("Cannot convert nil to type, returning error")
return reflect.Zero(typ), fmt.Errorf("Unable to use null value for parameter %d of method %s", index+1, b.fullName) return reflect.Zero(typ), fmt.Errorf("Unable to use null value for parameter %d of method %s", index+1, b.fullName)
} }
} else { } else {

View File

@@ -1,31 +1,34 @@
package wails package event
import ( import (
"fmt" "fmt"
"sync" "sync"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
) )
// eventManager handles and processes events // Manager handles and processes events
type eventManager struct { type Manager struct {
incomingEvents chan *eventData incomingEvents chan *messages.EventData
listeners map[string][]*eventListener listeners map[string][]*eventListener
exit bool exit bool
log *CustomLogger log *logger.CustomLogger
renderer Renderer // Messages will be dispatched to the frontend renderer interfaces.Renderer // Messages will be dispatched to the frontend
} }
// newEventManager creates a new event manager with a 100 event buffer // NewManager creates a new event manager with a 100 event buffer
func newEventManager() *eventManager { func NewManager() interfaces.EventManager {
return &eventManager{ return &Manager{
incomingEvents: make(chan *eventData, 100), incomingEvents: make(chan *messages.EventData, 100),
listeners: make(map[string][]*eventListener), listeners: make(map[string][]*eventListener),
exit: false, exit: false,
log: newCustomLogger("Events"), log: logger.NewCustomLogger("Events"),
} }
} }
// PushEvent places the given event on to the event queue // PushEvent places the given event on to the event queue
func (e *eventManager) PushEvent(eventData *eventData) { func (e *Manager) PushEvent(eventData *messages.EventData) {
e.incomingEvents <- eventData e.incomingEvents <- eventData
} }
@@ -40,7 +43,7 @@ type eventListener struct {
} }
// Creates a new event listener from the given callback function // Creates a new event listener from the given callback function
func (e *eventManager) addEventListener(eventName string, callback func(...interface{}), counter int) error { func (e *Manager) addEventListener(eventName string, callback func(...interface{}), counter int) error {
// Sanity check inputs // Sanity check inputs
if callback == nil { if callback == nil {
@@ -65,18 +68,19 @@ func (e *eventManager) addEventListener(eventName string, callback func(...inter
return nil return nil
} }
func (e *eventManager) On(eventName string, callback func(...interface{})) { // On adds a listener for the given event
func (e *Manager) On(eventName string, callback func(...interface{})) {
// Add a persistent eventListener (counter = 0) // Add a persistent eventListener (counter = 0)
e.addEventListener(eventName, callback, 0) e.addEventListener(eventName, callback, 0)
} }
// Emit broadcasts the given event to the subscribed listeners // Emit broadcasts the given event to the subscribed listeners
func (e *eventManager) Emit(eventName string, optionalData ...interface{}) { func (e *Manager) Emit(eventName string, optionalData ...interface{}) {
e.incomingEvents <- &eventData{Name: eventName, Data: optionalData} e.incomingEvents <- &messages.EventData{Name: eventName, Data: optionalData}
} }
// Starts the event manager's queue processing // Start the event manager's queue processing
func (e *eventManager) start(renderer Renderer) { func (e *Manager) Start(renderer interfaces.Renderer) {
e.log.Info("Starting") e.log.Info("Starting")
@@ -95,7 +99,7 @@ func (e *eventManager) start(renderer Renderer) {
// TODO: Listen for application exit // TODO: Listen for application exit
select { select {
case event := <-e.incomingEvents: case event := <-e.incomingEvents:
e.log.DebugFields("Got Event", Fields{ e.log.DebugFields("Got Event", logger.Fields{
"data": event.Data, "data": event.Data,
"name": event.Name, "name": event.Name,
}) })
@@ -143,6 +147,6 @@ func (e *eventManager) start(renderer Renderer) {
wg.Wait() wg.Wait()
} }
func (e *eventManager) stop() { func (e *Manager) stop() {
e.exit = true e.exit = true
} }

View File

@@ -0,0 +1,14 @@
package interfaces
// AppConfig is the application config interface
type AppConfig interface {
GetWidth() int
GetHeight() int
GetTitle() string
GetResizable() bool
GetDefaultHTML() string
GetDisableInspector() bool
GetColour() string
GetCSS() string
GetJS() string
}

View File

@@ -0,0 +1,10 @@
package interfaces
import "github.com/wailsapp/wails/lib/messages"
// BindingManager is the binding manager interface
type BindingManager interface {
Bind(object interface{})
Start(renderer Renderer, runtime Runtime) error
ProcessCall(callData *messages.CallData) (result interface{}, err error)
}

View File

@@ -0,0 +1,11 @@
package interfaces
import "github.com/wailsapp/wails/lib/messages"
// EventManager is the event manager interface
type EventManager interface {
PushEvent(*messages.EventData)
Emit(eventName string, optionalData ...interface{})
On(eventName string, callback func(...interface{}))
Start(Renderer)
}

View File

@@ -0,0 +1,8 @@
package interfaces
// IPCManager is the event manager interface
type IPCManager interface {
BindRenderer(Renderer)
Dispatch(message string)
Start(eventManager EventManager, bindingManager BindingManager)
}

View File

@@ -1,8 +1,11 @@
package wails package interfaces
import (
"github.com/wailsapp/wails/lib/messages"
)
// Renderer is an interface describing a Wails target to render the app to // Renderer is an interface describing a Wails target to render the app to
type Renderer interface { type Renderer interface {
Initialise(*AppConfig, *ipcManager, *eventManager) error Initialise(AppConfig, IPCManager, EventManager) error
Run() error Run() error
// Binding // Binding
@@ -10,7 +13,7 @@ type Renderer interface {
Callback(data string) error Callback(data string) error
// Events // Events
NotifyEvent(eventData *eventData) error NotifyEvent(eventData *messages.EventData) error
// Dialog Runtime // Dialog Runtime
SelectFile() string SelectFile() string

View File

@@ -0,0 +1,4 @@
package interfaces
// Runtime interface
type Runtime interface {}

View File

@@ -1,13 +1,10 @@
package wails package ipc
import ( import (
"fmt" "fmt"
)
type callData struct { "github.com/wailsapp/wails/lib/messages"
BindingName string `json:"bindingName"` )
Data string `json:"data,omitempty"`
}
func init() { func init() {
messageProcessors["call"] = processCallData messageProcessors["call"] = processCallData
@@ -15,7 +12,7 @@ func init() {
func processCallData(message *ipcMessage) (*ipcMessage, error) { func processCallData(message *ipcMessage) (*ipcMessage, error) {
var payload callData var payload messages.CallData
// Decode binding call data // Decode binding call data
payloadMap := message.Payload.(map[string]interface{}) payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,13 +1,10 @@
package wails package ipc
import ( import (
"encoding/json" "encoding/json"
)
type eventData struct { "github.com/wailsapp/wails/lib/messages"
Name string `json:"name"` )
Data interface{} `json:"data"`
}
// Register the message handler // Register the message handler
func init() { func init() {
@@ -19,7 +16,7 @@ func processEventData(message *ipcMessage) (*ipcMessage, error) {
// TODO: Is it worth double checking this is actually an event message, // TODO: Is it worth double checking this is actually an event message,
// even though that's done by the caller? // even though that's done by the caller?
var payload eventData var payload messages.EventData
// Decode event data // Decode event data
payloadMap := message.Payload.(map[string]interface{}) payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,9 +1,6 @@
package wails package ipc
type logData struct { import "github.com/wailsapp/wails/lib/messages"
Level string `json:"level"`
Message string `json:"string"`
}
// Register the message handler // Register the message handler
func init() { func init() {
@@ -13,7 +10,7 @@ func init() {
// This processes the given log message // This processes the given log message
func processLogData(message *ipcMessage) (*ipcMessage, error) { func processLogData(message *ipcMessage) (*ipcMessage, error) {
var payload logData var payload messages.LogData
// Decode event data // Decode event data
payloadMap := message.Payload.(map[string]interface{}) payloadMap := message.Payload.(map[string]interface{})

View File

@@ -1,35 +1,42 @@
package wails package ipc
import ( import (
"fmt" "fmt"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
) )
type ipcManager struct { // Manager manages the IPC subsystem
renderer Renderer // The renderer type Manager struct {
renderer interfaces.Renderer // The renderer
messageQueue chan *ipcMessage messageQueue chan *ipcMessage
// quitChannel chan struct{} // quitChannel chan struct{}
// signals chan os.Signal // signals chan os.Signal
log *CustomLogger log *logger.CustomLogger
eventManager *eventManager eventManager interfaces.EventManager
bindingManager *bindingManager bindingManager interfaces.BindingManager
} }
func newIPCManager() *ipcManager { // NewManager creates a new IPC Manager
result := &ipcManager{ func NewManager() interfaces.IPCManager {
result := &Manager{
messageQueue: make(chan *ipcMessage, 100), messageQueue: make(chan *ipcMessage, 100),
// quitChannel: make(chan struct{}), // quitChannel: make(chan struct{}),
// signals: make(chan os.Signal, 1), // signals: make(chan os.Signal, 1),
log: newCustomLogger("IPC"), log: logger.NewCustomLogger("IPC"),
} }
return result return result
} }
// Sets the renderer, returns the dispatch function // BindRenderer sets the renderer, returns the dispatch function
func (i *ipcManager) bindRenderer(renderer Renderer) { func (i *Manager) BindRenderer(renderer interfaces.Renderer) {
i.renderer = renderer i.renderer = renderer
} }
func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingManager) { // Start the IPC Manager
func (i *Manager) Start(eventManager interfaces.EventManager, bindingManager interfaces.BindingManager) {
// Store manager references // Store manager references
i.eventManager = eventManager i.eventManager = eventManager
@@ -42,36 +49,36 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
for running { for running {
select { select {
case incomingMessage := <-i.messageQueue: case incomingMessage := <-i.messageQueue:
i.log.DebugFields("Processing message", Fields{ i.log.DebugFields("Processing message", logger.Fields{
"1D": &incomingMessage, "1D": &incomingMessage,
}) })
switch incomingMessage.Type { switch incomingMessage.Type {
case "call": case "call":
callData := incomingMessage.Payload.(*callData) callData := incomingMessage.Payload.(*messages.CallData)
i.log.DebugFields("Processing call", Fields{ i.log.DebugFields("Processing call", logger.Fields{
"1D": &incomingMessage, "1D": &incomingMessage,
"bindingName": callData.BindingName, "bindingName": callData.BindingName,
"data": callData.Data, "data": callData.Data,
}) })
go func() { go func() {
result, err := bindingManager.processCall(callData) result, err := bindingManager.ProcessCall(callData)
i.log.DebugFields("processed call", Fields{"result": result, "err": err}) i.log.DebugFields("processed call", logger.Fields{"result": result, "err": err})
if err != nil { if err != nil {
incomingMessage.ReturnError(err.Error()) incomingMessage.ReturnError(err.Error())
} else { } else {
incomingMessage.ReturnSuccess(result) incomingMessage.ReturnSuccess(result)
} }
i.log.DebugFields("Finished processing call", Fields{ i.log.DebugFields("Finished processing call", logger.Fields{
"1D": &incomingMessage, "1D": &incomingMessage,
}) })
}() }()
case "event": case "event":
// Extract event data // Extract event data
eventData := incomingMessage.Payload.(*eventData) eventData := incomingMessage.Payload.(*messages.EventData)
// Log // Log
i.log.DebugFields("Processing event", Fields{ i.log.DebugFields("Processing event", logger.Fields{
"name": eventData.Name, "name": eventData.Name,
"data": eventData.Data, "data": eventData.Data,
}) })
@@ -80,24 +87,24 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
i.eventManager.PushEvent(eventData) i.eventManager.PushEvent(eventData)
// Log // Log
i.log.DebugFields("Finished processing event", Fields{ i.log.DebugFields("Finished processing event", logger.Fields{
"name": eventData.Name, "name": eventData.Name,
}) })
case "log": case "log":
logdata := incomingMessage.Payload.(*logData) logdata := incomingMessage.Payload.(*messages.LogData)
switch logdata.Level { switch logdata.Level {
case "info": case "info":
logger.Info(logdata.Message) i.log.Info(logdata.Message)
case "debug": case "debug":
logger.Debug(logdata.Message) i.log.Debug(logdata.Message)
case "warning": case "warning":
logger.Warning(logdata.Message) i.log.Warn(logdata.Message)
case "error": case "error":
logger.Error(logdata.Message) i.log.Error(logdata.Message)
case "fatal": case "fatal":
logger.Fatal(logdata.Message) i.log.Fatal(logdata.Message)
default: default:
i.log.ErrorFields("Invalid log level sent", Fields{ i.log.ErrorFields("Invalid log level sent", logger.Fields{
"level": logdata.Level, "level": logdata.Level,
"message": logdata.Message, "message": logdata.Message,
}) })
@@ -107,7 +114,7 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
} }
// Log // Log
i.log.DebugFields("Finished processing message", Fields{ i.log.DebugFields("Finished processing message", logger.Fields{
"1D": &incomingMessage, "1D": &incomingMessage,
}) })
// case <-manager.quitChannel: // case <-manager.quitChannel:
@@ -125,7 +132,7 @@ func (i *ipcManager) start(eventManager *eventManager, bindingManager *bindingMa
// Dispatch receives JSON encoded messages from the renderer. // Dispatch receives JSON encoded messages from the renderer.
// It processes the message to ensure that it is valid and places // It processes the message to ensure that it is valid and places
// the processed message on the message queue // the processed message on the message queue
func (i *ipcManager) Dispatch(message string) { func (i *Manager) Dispatch(message string) {
// Create a new IPC Message // Create a new IPC Message
incomingMessage, err := newIPCMessage(message, i.SendResponse) incomingMessage, err := newIPCMessage(message, i.SendResponse)
@@ -148,7 +155,7 @@ func (i *ipcManager) Dispatch(message string) {
} }
// SendResponse sends the given response back to the frontend // SendResponse sends the given response back to the frontend
func (i *ipcManager) SendResponse(response *ipcResponse) error { func (i *Manager) SendResponse(response *ipcResponse) error {
// Serialise the Message // Serialise the Message
data, err := response.Serialise() data, err := response.Serialise()

View File

@@ -1,4 +1,4 @@
package wails package ipc
import ( import (
"encoding/json" "encoding/json"

View File

@@ -1,4 +1,4 @@
package wails package ipc
import ( import (
"encoding/hex" "encoding/hex"

View File

@@ -1,4 +1,4 @@
package wails package logger
// CustomLogger is a wrapper object to logrus // CustomLogger is a wrapper object to logrus
type CustomLogger struct { type CustomLogger struct {
@@ -6,7 +6,8 @@ type CustomLogger struct {
errorOnly bool errorOnly bool
} }
func newCustomLogger(prefix string) *CustomLogger { // NewCustomLogger creates a new custom logger with the given prefix
func NewCustomLogger(prefix string) *CustomLogger {
return &CustomLogger{ return &CustomLogger{
prefix: "[" + prefix + "] ", prefix: "[" + prefix + "] ",
} }

View File

@@ -1,14 +1,14 @@
package wails package logger
import ( import (
"os" "os"
"strings" "strings"
log "github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// Global logger reference // Global logger reference
var logger = log.New() var logger = logrus.New()
// Fields is used by the customLogger object to output // Fields is used by the customLogger object to output
// fields along with a message // fields along with a message
@@ -17,26 +17,26 @@ type Fields map[string]interface{}
// Default options for the global logger // Default options for the global logger
func init() { func init() {
logger.SetOutput(os.Stdout) logger.SetOutput(os.Stdout)
logger.SetLevel(log.DebugLevel) logger.SetLevel(logrus.DebugLevel)
} }
// Sets the log level to the given level // SetLogLevel sets the log level to the given level
func setLogLevel(level string) { func SetLogLevel(level string) {
switch strings.ToLower(level) { switch strings.ToLower(level) {
case "info": case "info":
logger.SetLevel(log.InfoLevel) logger.SetLevel(logrus.InfoLevel)
case "debug": case "debug":
logger.SetLevel(log.DebugLevel) logger.SetLevel(logrus.DebugLevel)
case "warn": case "warn":
logger.SetLevel(log.WarnLevel) logger.SetLevel(logrus.WarnLevel)
case "error": case "error":
logger.SetLevel(log.ErrorLevel) logger.SetLevel(logrus.ErrorLevel)
case "fatal": case "fatal":
logger.SetLevel(log.FatalLevel) logger.SetLevel(logrus.FatalLevel)
case "panic": case "panic":
logger.SetLevel(log.PanicLevel) logger.SetLevel(logrus.PanicLevel)
default: default:
logger.SetLevel(log.DebugLevel) logger.SetLevel(logrus.DebugLevel)
logger.Warnf("Log level '%s' not recognised. Setting to Debug.", level) logger.Warnf("Log level '%s' not recognised. Setting to Debug.", level)
} }
} }

7
lib/messages/calldata.go Normal file
View File

@@ -0,0 +1,7 @@
package messages
// CallData represents a call to a Go function/method
type CallData struct {
BindingName string `json:"bindingName"`
Data string `json:"data,omitempty"`
}

View File

@@ -0,0 +1,7 @@
package messages
// EventData represents an event sent from the frontend
type EventData struct {
Name string `json:"name"`
Data interface{} `json:"data"`
}

7
lib/messages/logdata.go Normal file
View File

@@ -0,0 +1,7 @@
package messages
// LogData represents a call to log from the frontend
type LogData struct {
Level string `json:"level"`
Message string `json:"string"`
}

View File

@@ -1,4 +1,4 @@
package wails package renderer
import ( import (
"encoding/json" "encoding/json"
@@ -10,6 +10,9 @@ import (
"github.com/dchest/htmlmin" "github.com/dchest/htmlmin"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
) )
type messageType int type messageType int
@@ -32,10 +35,10 @@ func (m messageType) toString() string {
// and renders the files over a websocket // and renders the files over a websocket
type Headless struct { type Headless struct {
// Common // Common
log *CustomLogger log *logger.CustomLogger
ipcManager *ipcManager ipcManager interfaces.IPCManager
appConfig *AppConfig appConfig interfaces.AppConfig
eventManager *eventManager eventManager interfaces.EventManager
bindingCache []string bindingCache []string
// Headless specific // Headless specific
@@ -48,12 +51,12 @@ type Headless struct {
} }
// Initialise the Headless Renderer // Initialise the Headless Renderer
func (h *Headless) Initialise(appConfig *AppConfig, ipcManager *ipcManager, eventManager *eventManager) error { func (h *Headless) Initialise(appConfig interfaces.AppConfig, ipcManager interfaces.IPCManager, eventManager interfaces.EventManager) error {
h.ipcManager = ipcManager h.ipcManager = ipcManager
h.appConfig = appConfig h.appConfig = appConfig
h.eventManager = eventManager h.eventManager = eventManager
ipcManager.bindRenderer(h) ipcManager.BindRenderer(h)
h.log = newCustomLogger("Bridge") h.log = logger.NewCustomLogger("Bridge")
return nil return nil
} }
@@ -83,7 +86,7 @@ func (h *Headless) injectCSS(css string) {
minifiedCSS = strings.Replace(minifiedCSS, "\\", "\\\\", -1) minifiedCSS = strings.Replace(minifiedCSS, "\\", "\\\\", -1)
minifiedCSS = strings.Replace(minifiedCSS, "'", "\\'", -1) minifiedCSS = strings.Replace(minifiedCSS, "'", "\\'", -1)
minifiedCSS = strings.Replace(minifiedCSS, "\n", " ", -1) minifiedCSS = strings.Replace(minifiedCSS, "\n", " ", -1)
inject := fmt.Sprintf("wails._.injectCSS('%s')", minifiedCSS) inject := fmt.Sprintf("wails._.InjectCSS('%s')", minifiedCSS)
h.evalJS(inject, cssMessage) h.evalJS(inject, cssMessage)
} }
@@ -117,7 +120,7 @@ func (h *Headless) start(conn *websocket.Conn) {
// set external.invoke // set external.invoke
h.log.Infof("Connected to frontend.") h.log.Infof("Connected to frontend.")
wailsRuntime := mewn.String("./wailsruntimeassets/default/wails.min.js") wailsRuntime := mewn.String("../../runtime/js/dist/wails.js")
h.evalJS(wailsRuntime, wailsRuntimeMessage) h.evalJS(wailsRuntime, wailsRuntimeMessage)
// Inject bindings // Inject bindings
@@ -192,13 +195,13 @@ func (h *Headless) Callback(data string) error {
} }
// NotifyEvent notifies the frontend of an event // NotifyEvent notifies the frontend of an event
func (h *Headless) NotifyEvent(event *eventData) error { func (h *Headless) NotifyEvent(event *messages.EventData) error {
// Look out! Nils about! // Look out! Nils about!
var err error var err error
if event == nil { if event == nil {
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer") err = fmt.Errorf("Sent nil event to renderer.webViewRenderer")
logger.Error(err) h.log.Error(err.Error())
return err return err
} }
@@ -215,14 +218,14 @@ func (h *Headless) NotifyEvent(event *eventData) error {
} }
} }
message := fmt.Sprintf("window.wails._.notify('%s','%s')", event.Name, data) message := fmt.Sprintf("window.wails._.Notify('%s','%s')", event.Name, data)
return h.evalJS(message, notifyMessage) return h.evalJS(message, notifyMessage)
} }
// SetColour is unsupported for Headless but required // SetColour is unsupported for Headless but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SetColour(colour string) error { func (h *Headless) SetColour(colour string) error {
h.log.WarnFields("SetColour ignored for headless more", Fields{"col": colour}) h.log.WarnFields("SetColour ignored for headless more", logger.Fields{"col": colour})
return nil return nil
} }
@@ -241,7 +244,7 @@ func (h *Headless) UnFullscreen() {
// SetTitle is currently unsupported for Headless but required // SetTitle is currently unsupported for Headless but required
// for the Renderer interface // for the Renderer interface
func (h *Headless) SetTitle(title string) { func (h *Headless) SetTitle(title string) {
h.log.WarnFields("SetTitle() unsupported in bridge mode", Fields{"title": title}) h.log.WarnFields("SetTitle() unsupported in bridge mode", logger.Fields{"title": title})
} }
// Close is unsupported for Headless but required // Close is unsupported for Headless but required

File diff suppressed because one or more lines are too long

View File

@@ -1,52 +1,61 @@
package wails package renderer
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"strings"
"sync" "sync"
"time" "time"
"github.com/go-playground/colors" "github.com/go-playground/colors"
"github.com/leaanthony/mewn" "github.com/leaanthony/mewn"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/messages"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/webview" "github.com/wailsapp/webview"
) )
// Window defines the main application window // WebView defines the main webview application window
// Default values in [] // Default values in []
type webViewRenderer struct { type WebView struct {
window webview.WebView // The webview object window webview.WebView // The webview object
ipc *ipcManager ipc interfaces.IPCManager
log *CustomLogger log *logger.CustomLogger
config *AppConfig config interfaces.AppConfig
eventManager *eventManager eventManager interfaces.EventManager
bindingCache []string bindingCache []string
} }
// NewWebView returns a new WebView struct
func NewWebView() *WebView {
return &WebView{};
}
// Initialise sets up the WebView // Initialise sets up the WebView
func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventManager *eventManager) error { func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCManager, eventManager interfaces.EventManager) error {
// Store reference to eventManager // Store reference to eventManager
w.eventManager = eventManager w.eventManager = eventManager
// Set up logger // Set up logger
w.log = newCustomLogger("WebView") w.log = logger.NewCustomLogger("WebView")
// Set up the dispatcher function // Set up the dispatcher function
w.ipc = ipc w.ipc = ipc
ipc.bindRenderer(w) ipc.BindRenderer(w)
// Save the config // Save the config
w.config = config w.config = config
// Create the WebView instance // Create the WebView instance
w.window = webview.NewWebview(webview.Settings{ w.window = webview.NewWebview(webview.Settings{
Width: config.Width, Width: config.GetWidth(),
Height: config.Height, Height: config.GetHeight(),
Title: config.Title, Title: config.GetTitle(),
Resizable: config.Resizable, Resizable: config.GetResizable(),
URL: config.defaultHTML, URL: config.GetDefaultHTML(),
Debug: !config.DisableInspector, Debug: !config.GetDisableInspector(),
ExternalInvokeCallback: func(_ webview.WebView, message string) { ExternalInvokeCallback: func(_ webview.WebView, message string) {
w.ipc.Dispatch(message) w.ipc.Dispatch(message)
}, },
@@ -55,7 +64,7 @@ func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventMa
// SignalManager.OnExit(w.Exit) // SignalManager.OnExit(w.Exit)
// Set colour // Set colour
err := w.SetColour(config.Colour) err := w.SetColour(config.GetColour())
if err != nil { if err != nil {
return err return err
} }
@@ -64,7 +73,8 @@ func (w *webViewRenderer) Initialise(config *AppConfig, ipc *ipcManager, eventMa
return nil return nil
} }
func (w *webViewRenderer) SetColour(colour string) error { // SetColour sets the window colour
func (w *WebView) SetColour(colour string) error {
color, err := colors.Parse(colour) color, err := colors.Parse(colour)
if err != nil { if err != nil {
return err return err
@@ -80,12 +90,12 @@ func (w *webViewRenderer) SetColour(colour string) error {
// evalJS evaluates the given js in the WebView // evalJS evaluates the given js in the WebView
// I should rename this to evilJS lol // I should rename this to evilJS lol
func (w *webViewRenderer) evalJS(js string) error { func (w *WebView) evalJS(js string) error {
outputJS := fmt.Sprintf("%.45s", js) outputJS := fmt.Sprintf("%.45s", js)
if len(js) > 45 { if len(js) > 45 {
outputJS += "..." outputJS += "..."
} }
w.log.DebugFields("Eval", Fields{"js": outputJS}) w.log.DebugFields("Eval", logger.Fields{"js": outputJS})
// //
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.Eval(js) w.window.Eval(js)
@@ -93,12 +103,21 @@ func (w *webViewRenderer) evalJS(js string) error {
return nil return nil
} }
// Escape the Javascripts!
func escapeJS(js string) (string, error) {
result := strings.Replace(js, "\\", "\\\\", -1)
result = strings.Replace(result, "'", "\\'", -1)
result = strings.Replace(result, "\n", "\\n", -1)
return result, nil
}
// evalJSSync evaluates the given js in the WebView synchronously // evalJSSync evaluates the given js in the WebView synchronously
// Do not call this from the main thread or you'll nuke your app because // Do not call this from the main thread or you'll nuke your app because
// you won't get the callback. // you won't get the callback.
func (w *webViewRenderer) evalJSSync(js string) error { func (w *WebView) evalJSSync(js string) error {
minified, err := escapeJS(js) minified, err := escapeJS(js)
if err != nil { if err != nil {
return err return err
} }
@@ -107,7 +126,7 @@ func (w *webViewRenderer) evalJSSync(js string) error {
if len(js) > 45 { if len(js) > 45 {
outputJS += "..." outputJS += "..."
} }
w.log.DebugFields("EvalSync", Fields{"js": outputJS}) w.log.DebugFields("EvalSync", logger.Fields{"js": outputJS})
ID := fmt.Sprintf("syncjs:%d:%d", time.Now().Unix(), rand.Intn(9999)) ID := fmt.Sprintf("syncjs:%d:%d", time.Now().Unix(), rand.Intn(9999))
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -122,7 +141,7 @@ func (w *webViewRenderer) evalJSSync(js string) error {
wg.Done() wg.Done()
exit = true exit = true
}) })
command := fmt.Sprintf("wails._.addScript('%s', '%s')", minified, ID) command := fmt.Sprintf("wails._.AddScript('%s', '%s')", minified, ID)
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.Eval(command) w.window.Eval(command)
}) })
@@ -137,24 +156,24 @@ func (w *webViewRenderer) evalJSSync(js string) error {
} }
// injectCSS adds the given CSS to the WebView // injectCSS adds the given CSS to the WebView
func (w *webViewRenderer) injectCSS(css string) { func (w *WebView) injectCSS(css string) {
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.InjectCSS(css) w.window.InjectCSS(css)
}) })
} }
// Quit the window // Exit closes the window
func (w *webViewRenderer) Exit() { func (w *WebView) Exit() {
w.window.Exit() w.window.Exit()
} }
// Run the window main loop // Run the window main loop
func (w *webViewRenderer) Run() error { func (w *WebView) Run() error {
w.log.Info("Run()") w.log.Info("Run()")
// Runtime assets // Runtime assets
wailsRuntime := mewn.String("./wailsruntimeassets/default/wails.min.js") wailsRuntime := mewn.String("../../runtime/js/dist/wails.js")
w.evalJS(wailsRuntime) w.evalJS(wailsRuntime)
// Ping the wait channel when the wails runtime is loaded // Ping the wait channel when the wails runtime is loaded
@@ -168,38 +187,30 @@ func (w *webViewRenderer) Run() error {
w.evalJSSync(binding) w.evalJSSync(binding)
} }
// // Inject Framework
// if w.frameworkJS != "" {
// w.evalJSSync(w.frameworkJS)
// }
// if w.frameworkCSS != "" {
// w.injectCSS(w.frameworkCSS)
// }
// Inject user CSS // Inject user CSS
if w.config.CSS != "" { if w.config.GetCSS() != "" {
outputCSS := fmt.Sprintf("%.45s", w.config.CSS) outputCSS := fmt.Sprintf("%.45s", w.config.GetCSS())
if len(outputCSS) > 45 { if len(outputCSS) > 45 {
outputCSS += "..." outputCSS += "..."
} }
w.log.DebugFields("Inject User CSS", Fields{"css": outputCSS}) w.log.DebugFields("Inject User CSS", logger.Fields{"css": outputCSS})
w.injectCSS(w.config.CSS) w.injectCSS(w.config.GetCSS())
} else { } else {
// Use default wails css // Use default wails css
w.log.Debug("Injecting Default Wails CSS") w.log.Debug("Injecting Default Wails CSS")
defaultCSS := mewn.String("./wailsruntimeassets/default/wails.css") defaultCSS := mewn.String("../../runtime/assets/wails.css")
w.injectCSS(defaultCSS) w.injectCSS(defaultCSS)
} }
// Inject user JS // Inject user JS
if w.config.JS != "" { if w.config.GetJS() != "" {
outputJS := fmt.Sprintf("%.45s", w.config.JS) outputJS := fmt.Sprintf("%.45s", w.config.GetJS())
if len(outputJS) > 45 { if len(outputJS) > 45 {
outputJS += "..." outputJS += "..."
} }
w.log.DebugFields("Inject User JS", Fields{"js": outputJS}) w.log.DebugFields("Inject User JS", logger.Fields{"js": outputJS})
w.evalJSSync(w.config.JS) w.evalJSSync(w.config.GetJS())
} }
// Emit that everything is loaded and ready // Emit that everything is loaded and ready
@@ -213,14 +224,15 @@ func (w *webViewRenderer) Run() error {
return nil return nil
} }
// Binds the given method name with the front end // NewBinding registers a new binding with the frontend
func (w *webViewRenderer) NewBinding(methodName string) error { func (w *WebView) NewBinding(methodName string) error {
objectCode := fmt.Sprintf("window.wails._.newBinding('%s');", methodName) objectCode := fmt.Sprintf("window.wails._.NewBinding('%s');", methodName)
w.bindingCache = append(w.bindingCache, objectCode) w.bindingCache = append(w.bindingCache, objectCode)
return nil return nil
} }
func (w *webViewRenderer) SelectFile() string { // SelectFile opens a dialog that allows the user to select a file
func (w *WebView) SelectFile() string {
var result string var result string
// We need to run this on the main thread, however Dispatch is // We need to run this on the main thread, however Dispatch is
@@ -238,7 +250,8 @@ func (w *webViewRenderer) SelectFile() string {
return result return result
} }
func (w *webViewRenderer) SelectDirectory() string { // SelectDirectory opens a dialog that allows the user to select a directory
func (w *WebView) SelectDirectory() string {
var result string var result string
// We need to run this on the main thread, however Dispatch is // We need to run this on the main thread, however Dispatch is
// non-blocking so we launch this in a goroutine and wait for // non-blocking so we launch this in a goroutine and wait for
@@ -255,7 +268,8 @@ func (w *webViewRenderer) SelectDirectory() string {
return result return result
} }
func (w *webViewRenderer) SelectSaveFile() string { // SelectSaveFile opens a dialog that allows the user to select a file to save
func (w *WebView) SelectSaveFile() string {
var result string var result string
// We need to run this on the main thread, however Dispatch is // We need to run this on the main thread, however Dispatch is
// non-blocking so we launch this in a goroutine and wait for // non-blocking so we launch this in a goroutine and wait for
@@ -273,18 +287,19 @@ func (w *webViewRenderer) SelectSaveFile() string {
} }
// Callback sends a callback to the frontend // Callback sends a callback to the frontend
func (w *webViewRenderer) Callback(data string) error { func (w *WebView) Callback(data string) error {
callbackCMD := fmt.Sprintf("window.wails._.callback('%s');", data) callbackCMD := fmt.Sprintf("window.wails._.Callback('%s');", data)
return w.evalJS(callbackCMD) return w.evalJS(callbackCMD)
} }
func (w *webViewRenderer) NotifyEvent(event *eventData) error { // NotifyEvent notifies the frontend about a backend runtime event
func (w *WebView) NotifyEvent(event *messages.EventData) error {
// Look out! Nils about! // Look out! Nils about!
var err error var err error
if event == nil { if event == nil {
err = fmt.Errorf("Sent nil event to renderer.webViewRenderer") err = fmt.Errorf("Sent nil event to renderer.WebView")
logger.Error(err) w.log.Error(err.Error())
return err return err
} }
@@ -301,13 +316,13 @@ func (w *webViewRenderer) NotifyEvent(event *eventData) error {
} }
} }
message := fmt.Sprintf("wails._.notify('%s','%s')", event.Name, data) message := fmt.Sprintf("wails._.Notify('%s','%s')", event.Name, data)
return w.evalJS(message) return w.evalJS(message)
} }
// Window // Fullscreen makes the main window go fullscreen
func (w *webViewRenderer) Fullscreen() { func (w *WebView) Fullscreen() {
if w.config.Resizable == false { if w.config.GetResizable() == false {
w.log.Warn("Cannot call Fullscreen() - App.Resizable = false") w.log.Warn("Cannot call Fullscreen() - App.Resizable = false")
return return
} }
@@ -316,8 +331,9 @@ func (w *webViewRenderer) Fullscreen() {
}) })
} }
func (w *webViewRenderer) UnFullscreen() { // UnFullscreen returns the window to the position prior to a fullscreen call
if w.config.Resizable == false { func (w *WebView) UnFullscreen() {
if w.config.GetResizable() == false {
w.log.Warn("Cannot call UnFullscreen() - App.Resizable = false") w.log.Warn("Cannot call UnFullscreen() - App.Resizable = false")
return return
} }
@@ -326,13 +342,15 @@ func (w *webViewRenderer) UnFullscreen() {
}) })
} }
func (w *webViewRenderer) SetTitle(title string) { // SetTitle sets the window title
func (w *WebView) SetTitle(title string) {
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.SetTitle(title) w.window.SetTitle(title)
}) })
} }
func (w *webViewRenderer) Close() { // Close closes the window
func (w *WebView) Close() {
w.window.Dispatch(func() { w.window.Dispatch(func() {
w.window.Terminate() w.window.Terminate()
}) })

View File

@@ -1,22 +0,0 @@
package wails
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
type Runtime struct {
Events *RuntimeEvents
Log *RuntimeLog
Dialog *RuntimeDialog
Window *RuntimeWindow
Browser *RuntimeBrowser
FileSystem *RuntimeFileSystem
}
func newRuntime(eventManager *eventManager, renderer Renderer) *Runtime {
return &Runtime{
Events: newRuntimeEvents(eventManager),
Log: newRuntimeLog(),
Dialog: newRuntimeDialog(renderer),
Window: newRuntimeWindow(renderer),
Browser: newRuntimeBrowser(),
FileSystem: newRuntimeFileSystem(),
}
}

View File

@@ -0,0 +1,216 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This library creates a bridge between your application
and the frontend, allowing you to develop your app using
standard tooling (browser extensions, live reload, etc).
Usage:
```
import Bridge from "./wailsbridge";
Bridge.Start(startApp);
```
The given callback (startApp in the example) will be called
when the bridge has successfully initialised. It passes the
window.wails object back, in case it is not accessible directly.
*/
// Bridge object
window.wailsbridge = {
reconnectOverlay: null,
reconnectTimer: 300,
wsURL: 'ws://localhost:34115/bridge',
connectionState: null,
config: {},
websocket: null,
callback: null,
overlayHTML:
'<div class="wails-reconnect-overlay"><div class="wails-reconnect-overlay-content"><div class="wails-reconnect-overlay-title">Wails Bridge</div><br><div class="wails-reconnect-overlay-loadingspinner"></div><br><div id="wails-reconnect-overlay-message">Waiting for backend</div></div></div>',
overlayCSS:
'.wails-reconnect-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.6);font-family:sans-serif;display:none;z-index:999999}.wails-reconnect-overlay-content{padding:20px 30px;text-align:center;width:20em;position:relative;height:14em;border-radius:1em;margin:5% auto 0;background-color:#fff;box-shadow:1px 1px 20px 3px;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAMAAACPpbA7AAAAqFBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQAAAAAAAAEBAQAAAAAAAAAAAAEBAQEBAQDAwMBAQEAAAABAQEAAAAAAAAAAAABAQEAAAAAAAACAgICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAABAQEAAAACAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBQWKCj6oAAAAN3RSTlMALiIqDhkGBAswJjP0GxP6NR4W9/ztjRDMhWU50G9g5eHXvbZ9XEI9xZTcqZl2aldKo55QwoCvZUgzhAAAAs9JREFUSMeNleeWqjAUhU0BCaH3Itiw9zKT93+zG02QK1hm/5HF+jzZJ6fQe6cyXE+jg9X7o9wxuylIIf4Tv2V3+bOrEXnf8dwQ/KQIGDN2/S+4OmVCVXL/ScBnfibxURqIByP/hONE8r8T+bDMlQ98KSl7Y8hzjpS8v1qtDh8u5f8KQpGpfnPPhqG8JeogN37Hq9eaN2xRhIwAaGnvws8F1ShxqK5ob2twYi1FAMD4rXsYtnC/JEiRbl4cUrCWhnMCLRFemXezXbb59QK4WASOsm6n2W1+4CBT2JmtzQ6fsrbGubR/NFbd2g5Y179+5w/GEHaKsHjYCet7CgrXU3txarNC7YxOVJtIj4/ERzMdZfzc31hp+8cD6eGILgarZY9uZ12hAs03vfBD9C171gS5Omz7OcvxALQIn4u8RRBBBcsi9WW2woO9ipLgfzpYlggg3ZRdROUC8KT7QLqq3W9KB5BbdFVg4929kdwp6+qaZnMCCNBdj+NyN1W885Ry/AL3D4AQbsVV4noCiM/C83kyYq80XlDAYQtralOiDzoRAHlotWl8q2tjvYlOgcg1A8jEApZa+C06TBdAz2Qv0wu11I/zZOyJQ6EwGez2P2b8PIQr1hwwnAZsAxwA4UAYOyXUxM/xp6tHAn4GUmPGM9R28oVxgC0e/zQJJI6DyhyZ1r7uzRQhpcW7x7vTaWSzKSG6aep77kroTEl3U81uSVaUTtgEINfC8epx+Q4F9SpplHG84Ek6m4RAq9/TLkOBrxyeuddZhHvGIp1XXfFy3Z3vtwNblKGiDn+J+92vwwABHghj7HnzlS1H5kB49AZvdGCFgiBPq69qfXPr3y++yilF0ON4R8eR7spAsLpZ95NqAW5tab1c4vkZm6aleajchMwYTdILQQTwE2OV411ZM9WztDjPql12caBi6gDpUKmDd4U1XNdQxZ4LIXQ5/Tr4P7I9tYcFrDK3AAAAAElFTkSuQmCC);background-repeat:no-repeat;background-position:center}.wails-reconnect-overlay-title{font-size:2em}.wails-reconnect-overlay-message{font-size:1.3em}.wails-reconnect-overlay-loadingspinner{pointer-events:none;width:2.5em;height:2.5em;border:.4em solid transparent;border-color:#3E67EC #eee #eee;border-radius:50%;animation:loadingspin 1s linear infinite;margin:auto;padding:2.5em}@keyframes loadingspin{100%{transform:rotate(360deg)}}',
log: function (message) {
// eslint-disable-next-line
console.log(
'%c wails bridge %c ' + message + ' ',
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
);
}
};
// Adapted from webview - thanks zserge!
function injectCSS(css) {
var elem = document.createElement('style');
elem.setAttribute('type', 'text/css');
if (elem.styleSheet) {
elem.styleSheet.cssText = css;
} else {
elem.appendChild(document.createTextNode(css));
}
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(elem);
}
// Creates a node in the Dom
function createNode(parent, elementType, id, className, content) {
var d = document.createElement(elementType);
if (id) {
d.id = id;
}
if (className) {
d.className = className;
}
if (content) {
d.innerHTML = content;
}
parent.appendChild(d);
return d;
}
// Sets up the overlay
function setupOverlay() {
var body = document.body;
var wailsBridgeNode = createNode(body, 'div', 'wails-bridge');
wailsBridgeNode.innerHTML = window.wailsbridge.overlayHTML;
// Inject the overlay CSS
injectCSS(window.wailsbridge.overlayCSS);
}
// Start the Wails Bridge
function startBridge() {
// Setup the overlay
setupOverlay();
window.wailsbridge.websocket = null;
window.wailsbridge.connectTimer = null;
window.wailsbridge.reconnectOverlay = document.querySelector(
'.wails-reconnect-overlay'
);
window.wailsbridge.connectionState = 'disconnected';
// Shows the overlay
function showReconnectOverlay() {
window.wailsbridge.reconnectOverlay.style.display = 'block';
}
// Hides the overlay
function hideReconnectOverlay() {
window.wailsbridge.reconnectOverlay.style.display = 'none';
}
// Bridge external.invoke
window.external = {
invoke: function (msg) {
window.wailsbridge.websocket.send(msg);
}
};
// Adds a script to the Dom.
// Removes it if second parameter is true.
function addScript(script, remove) {
var s = document.createElement('script');
s.setAttribute('type', 'text/javascript');
s.textContent = script;
document.head.appendChild(s);
// Remove internal messages from the DOM
if (remove) {
s.parentNode.removeChild(s);
}
}
// Handles incoming websocket connections
function handleConnect() {
window.wailsbridge.log('Connected to backend');
hideReconnectOverlay();
clearInterval(window.wailsbridge.connectTimer);
window.wailsbridge.websocket.onclose = handleDisconnect;
window.wailsbridge.websocket.onmessage = handleMessage;
window.wailsbridge.connectionState = 'connected';
}
// Handles websocket disconnects
function handleDisconnect() {
window.wailsbridge.log('Disconnected from backend');
window.wailsbridge.websocket = null;
window.wailsbridge.connectionState = 'disconnected';
showReconnectOverlay();
connect();
}
// Try to connect to the backend every 300ms (default value).
// Change this value in the main wailsbridge object.
function connect() {
window.wailsbridge.connectTimer = setInterval(function () {
if (window.wailsbridge.websocket == null) {
window.wailsbridge.websocket = new WebSocket(window.wailsbridge.wsURL);
window.wailsbridge.websocket.onopen = handleConnect;
window.wailsbridge.websocket.onerror = function (e) {
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
window.wailsbridge.websocket = null;
return false;
};
}
}, window.wailsbridge.reconnectTimer);
}
function handleMessage(message) {
// As a bridge we ignore js and css injections
switch (message.data[0]) {
// Wails library - inject!
case 'w':
addScript(message.data.slice(1));
// Now wails runtime is loaded, wails for the ready event
// and callback to the main app
window.wails.Events.On('wails:loaded', function () {
window.wailsbridge.log('Wails Ready');
if (window.wailsbridge.callback) {
window.wailsbridge.log('Notifying application');
window.wailsbridge.callback(window.wails);
}
});
window.wailsbridge.log('Loaded Wails Runtime');
break;
// Notifications
case 'n':
addScript(message.data.slice(1), true);
break;
// Binding
case 'b':
var binding = message.data.slice(1);
//log("Binding: " + binding)
window.wails._.NewBinding(binding);
break;
// Call back
case 'c':
var callbackData = message.data.slice(1);
window.wails._.Callback(callbackData);
break;
default:
window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
}
}
// Start by showing the overlay...
showReconnectOverlay();
// ...and attempt to connect
connect();
}
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
// Save the callback
window.wailsbridge.callback = callback;
// Start Bridge
startBridge();
}
};

View File

@@ -0,0 +1,17 @@
/*
Wails Bridge (c) 2019-present Lea Anthony
This prod version is to get around having to rewrite your code
for production. When doing a release build, this file will be used
instead of the full version.
*/
export default {
// The main function
// Passes the main Wails object to the callback if given.
Start: function (callback) {
if (callback) {
window.wails.Events.On('wails:ready', callback);
}
}
};

View File

@@ -0,0 +1,21 @@
package runtime
import "github.com/pkg/browser"
// Browser exposes browser methods to the runtime
type Browser struct{}
// NewBrowser creates a new runtime Browser struct
func NewBrowser() *Browser {
return &Browser{}
}
// OpenURL opens the given url in the system's default browser
func (r *Browser) OpenURL(url string) error {
return browser.OpenURL(url)
}
// OpenFile opens the given file in the system's default browser
func (r *Browser) OpenFile(filePath string) error {
return browser.OpenFile(filePath)
}

View File

@@ -0,0 +1,30 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Dialog exposes an interface to native dialogs
type Dialog struct {
renderer interfaces.Renderer
}
// newDialog creates a new Dialog struct
func newDialog(renderer interfaces.Renderer) *Dialog {
return &Dialog{
renderer: renderer,
}
}
// SelectFile prompts the user to select a file
func (r *Dialog) SelectFile() string {
return r.renderer.SelectFile()
}
// SelectDirectory prompts the user to select a directory
func (r *Dialog) SelectDirectory() string {
return r.renderer.SelectDirectory()
}
// SelectSaveFile prompts the user to select a file for saving
func (r *Dialog) SelectSaveFile() string {
return r.renderer.SelectSaveFile()
}

View File

@@ -0,0 +1,24 @@
package runtime
import "github.com/wailsapp/wails/lib/interfaces"
// Events exposes the events interface
type Events struct {
eventManager interfaces.EventManager
}
func newEvents(eventManager interfaces.EventManager) *Events {
return &Events{
eventManager: eventManager,
}
}
// On pass through
func (r *Events) On(eventName string, callback func(optionalData ...interface{})) {
r.eventManager.On(eventName, callback)
}
// Emit pass through
func (r *Events) Emit(eventName string, optionalData ...interface{}) {
r.eventManager.Emit(eventName, optionalData...)
}

View File

@@ -0,0 +1,16 @@
package runtime
import homedir "github.com/mitchellh/go-homedir"
// FileSystem exposes file system utilities to the runtime
type FileSystem struct {}
// Creates a new FileSystem struct
func newFileSystem() *FileSystem {
return &FileSystem{}
}
// HomeDir returns the user's home directory
func (r *FileSystem) HomeDir() (string, error) {
return homedir.Dir()
}

16
runtime/go/runtime/log.go Normal file
View File

@@ -0,0 +1,16 @@
package runtime
import "github.com/wailsapp/wails/lib/logger"
// Log exposes the logging interface to the runtime
type Log struct{}
// newLog creates a new Log struct
func newLog() *Log {
return &Log{}
}
// New creates a new logger
func (r *Log) New(prefix string) *logger.CustomLogger {
return logger.NewCustomLogger(prefix)
}

Some files were not shown because too many files have changed in this diff Show More