mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 18:42:23 +08:00
Develop (#250)
This commit is contained in:
parent
694f80434a
commit
20428b0407
@ -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 -}}
|
|
@ -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
1
.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
runtime/assets/default.html
|
@ -1,7 +1,8 @@
|
|||||||
module.exports = {
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true
|
"es6": true,
|
||||||
|
"node": true,
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
@ -26,4 +27,4 @@ module.exports = {
|
|||||||
"always"
|
"always"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
}
|
19
.github/stale.yml
vendored
Normal file
19
.github/stale.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 30
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 7
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- pinned
|
||||||
|
- security
|
||||||
|
- onhold
|
||||||
|
- inprogress
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: wontfix
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: false
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
6
.hound.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
jshint:
|
||||||
|
config_file: .jshintrc
|
||||||
|
eslint:
|
||||||
|
enabled: true
|
||||||
|
config_file: .eslintrc
|
||||||
|
ignore_file: .eslintignore
|
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -4,6 +4,13 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Test cmd package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "test",
|
||||||
|
"program": "${workspaceFolder}/cmd/"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Wails Init",
|
"name": "Wails Init",
|
||||||
"type": "go",
|
"type": "go",
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
|
||||||
|
2019-07-20 **v0.17.6-pre**
|
||||||
|
* Significant refactor of runtime
|
||||||
|
* Removed wailsbridge file - now a unified approach taken
|
||||||
|
* Fixed React on Windows - Thanks [Florian Didran](https://github.com/fdidron)!
|
||||||
|
|
||||||
2019-06-18 **v0.16.0**
|
2019-06-18 **v0.16.0**
|
||||||
* React template FTW! - Thanks [admin_3.exe](https://github.com/bh90210)!
|
* React template FTW! - Thanks [admin_3.exe](https://github.com/bh90210)!
|
||||||
* Updated contributors
|
* Updated contributors
|
||||||
|
@ -6,6 +6,7 @@ Wails is what it is because of the time and effort given by these great people.
|
|||||||
* [Qais Patankar](https://github.com/qaisjp)
|
* [Qais Patankar](https://github.com/qaisjp)
|
||||||
* [Anthony Lee](https://github.com/alee792)
|
* [Anthony Lee](https://github.com/alee792)
|
||||||
* [Adrian Lanzafame](https://github.com/lanzafame)
|
* [Adrian Lanzafame](https://github.com/lanzafame)
|
||||||
|
* [Mattn](https://github.com/mattn)
|
||||||
* [0xflotus](https://github.com/0xflotus)
|
* [0xflotus](https://github.com/0xflotus)
|
||||||
* [Michael D Henderson](https://github.com/mdhender)
|
* [Michael D Henderson](https://github.com/mdhender)
|
||||||
* [fred2104](https://github.com/fishfishfish2104)
|
* [fred2104](https://github.com/fishfishfish2104)
|
||||||
@ -13,3 +14,7 @@ Wails is what it is because of the time and effort given by these great people.
|
|||||||
* [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)
|
* [iceleo-com](https://github.com/iceleo-com)
|
||||||
|
* [fallendusk](https://github.com/fallendusk)
|
||||||
|
* [Florian Didran](https://github.com/fdidron)
|
||||||
|
* [Nikolai Zimmermann](https://github.com/Chronophylos)
|
||||||
|
* [Toyam Cox](https://github.com/Vaelatern)
|
||||||
|
32
README.md
32
README.md
@ -47,19 +47,41 @@ Make sure you have the xcode command line tools installed. This can be done by r
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
#### Ubuntu 18.04, Debian 9
|
#### Debian/Ubuntu
|
||||||
|
|
||||||
`sudo apt install pkg-config build-essential libgtk-3-dev libwebkit2gtk-4.0-dev`
|
`sudo apt install libgtk-3-dev libwebkit2gtk-4.0-dev`
|
||||||
|
|
||||||
|
_Debian: 8, 9, 10_
|
||||||
|
|
||||||
|
_Ubuntu: 16.04, 18.04, 19.04_
|
||||||
|
|
||||||
|
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_
|
||||||
|
|
||||||
#### Arch Linux
|
#### Arch Linux
|
||||||
|
|
||||||
`sudo pacman -S webkit2gtk gtk3`
|
`sudo pacman -S webkit2gtk gtk3`
|
||||||
|
|
||||||
#### Red Hat Based Distros
|
_Also succesfully test on: Manjaro & ArcoLinux_
|
||||||
|
|
||||||
`sudo yum install webkit2gtk-devel gtk3-devel`
|
#### Centos
|
||||||
|
|
||||||
Note: If you have successfully installed these dependencies on a different flavour of Linux, please consider submitting a PR.
|
`sudo yum install webkitgtk3-devel gtk3-devel`
|
||||||
|
|
||||||
|
_CentOS 6, 7_
|
||||||
|
|
||||||
|
#### Fedora
|
||||||
|
|
||||||
|
`sudo yum install webkit2gtk3-devel gtk3-devel`
|
||||||
|
|
||||||
|
_Fedora 29, 30_
|
||||||
|
|
||||||
|
#### VoidLinux & VoidLinux-musl
|
||||||
|
|
||||||
|
`xbps-install gtk+3-devel webkit2gtk-devel`
|
||||||
|
|
||||||
|
#### Gentoo
|
||||||
|
|
||||||
|
`sudo emerge gtk+:3 webkit-gtk`
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
|
46
app.go
46
app.go
@ -2,6 +2,12 @@ package wails
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/wailsapp/wails/cmd"
|
"github.com/wailsapp/wails/cmd"
|
||||||
|
"github.com/wailsapp/wails/lib/binding"
|
||||||
|
"github.com/wailsapp/wails/lib/event"
|
||||||
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/ipc"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/lib/renderer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------------------------------- Compile time Flags ------------------------------
|
// -------------------------------- Compile time Flags ------------------------------
|
||||||
@ -15,13 +21,13 @@ var BuildMode = cmd.BuildModeProd
|
|||||||
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 +40,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 +81,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 bridge mode
|
||||||
if BuildMode == cmd.BuildModeBridge {
|
if BuildMode == cmd.BuildModeBridge {
|
||||||
a.renderer = &Headless{}
|
a.renderer = &renderer.Bridge{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the renderer
|
// Initialise the renderer
|
||||||
@ -92,16 +98,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 = 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 +119,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)
|
||||||
}
|
}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
package wails
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/dchest/htmlmin"
|
|
||||||
"github.com/leaanthony/mewn"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AppConfig is the configuration structure used when creating a Wails App object
|
|
||||||
type AppConfig struct {
|
|
||||||
Width, Height int
|
|
||||||
Title string
|
|
||||||
defaultHTML string
|
|
||||||
HTML string
|
|
||||||
JS string
|
|
||||||
CSS string
|
|
||||||
Colour string
|
|
||||||
Resizable bool
|
|
||||||
DisableInspector bool
|
|
||||||
isHTMLFragment bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AppConfig) merge(in *AppConfig) error {
|
|
||||||
if in.CSS != "" {
|
|
||||||
a.CSS = in.CSS
|
|
||||||
}
|
|
||||||
if 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 != "" {
|
|
||||||
a.Colour = in.Colour
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.JS != "" {
|
|
||||||
a.JS = in.JS
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.Width != 0 {
|
|
||||||
a.Width = in.Width
|
|
||||||
}
|
|
||||||
if in.Height != 0 {
|
|
||||||
a.Height = in.Height
|
|
||||||
}
|
|
||||||
a.Resizable = in.Resizable
|
|
||||||
a.DisableInspector = in.DisableInspector
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates the default configuration
|
|
||||||
func newAppConfig(userConfig *AppConfig) (*AppConfig, error) {
|
|
||||||
result := &AppConfig{
|
|
||||||
Width: 800,
|
|
||||||
Height: 600,
|
|
||||||
Resizable: true,
|
|
||||||
Title: "My Wails App",
|
|
||||||
Colour: "#FFF", // White by default
|
|
||||||
HTML: mewn.String("./wailsruntimeassets/default/default.html"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if userConfig != nil {
|
|
||||||
err := result.merge(userConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
138
azure-pipelines.yaml
Normal file
138
azure-pipelines.yaml
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# avoid double trigger by applying some rules
|
||||||
|
# start a pipeline when push to 'master' branch
|
||||||
|
trigger:
|
||||||
|
- master
|
||||||
|
# or when pull request on 'develop' branch
|
||||||
|
pr:
|
||||||
|
- develop
|
||||||
|
|
||||||
|
# for now there is only one stage 'Build'
|
||||||
|
# in the future we could use multistage strategy for releases
|
||||||
|
stages:
|
||||||
|
- stage: Build
|
||||||
|
|
||||||
|
# there are 3 jobs
|
||||||
|
# one for each os
|
||||||
|
jobs:
|
||||||
|
- deployment: Linux
|
||||||
|
displayName: Lin
|
||||||
|
variables:
|
||||||
|
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
|
||||||
|
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
|
||||||
|
GOBIN: '$(GOPATH)/bin' # Go binaries path
|
||||||
|
GOMODULE: 'on'
|
||||||
|
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
|
||||||
|
pool:
|
||||||
|
vmImage: 'Ubuntu-16.04'
|
||||||
|
environment: 'linux-dev'
|
||||||
|
strategy:
|
||||||
|
runOnce:
|
||||||
|
deploy:
|
||||||
|
steps:
|
||||||
|
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
|
||||||
|
clean: true # whether to fetch clean each time
|
||||||
|
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
|
||||||
|
# go version 1.12.7
|
||||||
|
- script: |
|
||||||
|
wget "https://storage.googleapis.com/golang/go1.12.7.linux-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
|
||||||
|
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
|
||||||
|
displayName: 'Install Go 1.12.7 Linux'
|
||||||
|
- script: |
|
||||||
|
mkdir -p '$(GOBIN)'
|
||||||
|
mkdir -p '$(GOPATH)/pkg'
|
||||||
|
mkdir -p '$(GOROOT)'
|
||||||
|
shopt -s extglob
|
||||||
|
shopt -s dotglob
|
||||||
|
echo '##vso[task.prependpath]$(GOBIN)'
|
||||||
|
echo '##vso[task.prependpath]$(GOROOT)/bin'
|
||||||
|
displayName: 'Set up the Go workspace'
|
||||||
|
- script: |
|
||||||
|
go version
|
||||||
|
go get -v -d ./...
|
||||||
|
cd cmd/wails
|
||||||
|
go install
|
||||||
|
workingDirectory: '$(modulePath)'
|
||||||
|
displayName: 'Get dependencies, then build'
|
||||||
|
- script: |
|
||||||
|
wails version
|
||||||
|
workingDirectory: '$(modulePath)'
|
||||||
|
displayName: 'Check we have output'
|
||||||
|
|
||||||
|
- deployment: Mac
|
||||||
|
displayName: Mac
|
||||||
|
variables:
|
||||||
|
GOPATH: '$(Agent.BuildDirectory)/gopath' # Go workspace path
|
||||||
|
GOROOT: '$(Agent.BuildDirectory)/go' # Go installation path
|
||||||
|
GOBIN: '$(GOPATH)/bin' # Go binaries path
|
||||||
|
GOMODULE: 'on'
|
||||||
|
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
|
||||||
|
pool:
|
||||||
|
vmImage: 'macOS-10.14'
|
||||||
|
environment: 'mac-dev'
|
||||||
|
strategy:
|
||||||
|
runOnce:
|
||||||
|
deploy:
|
||||||
|
steps:
|
||||||
|
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
|
||||||
|
clean: true # whether to fetch clean each time
|
||||||
|
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
|
||||||
|
# go version 1.12.7
|
||||||
|
- script: |
|
||||||
|
wget "https://storage.googleapis.com/golang/go1.12.7.darwin-amd64.tar.gz" --output-document "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
|
||||||
|
tar -C '$(Agent.BuildDirectory)' -xzf "$(Agent.BuildDirectory)/go1.12.7.tar.gz"
|
||||||
|
displayName: 'Install Go 1.12.7 Linux'
|
||||||
|
- script: |
|
||||||
|
mkdir -p '$(GOBIN)'
|
||||||
|
mkdir -p '$(GOPATH)/pkg'
|
||||||
|
mkdir -p '$(GOROOT)'
|
||||||
|
shopt -s extglob
|
||||||
|
shopt -s dotglob
|
||||||
|
echo '##vso[task.prependpath]$(GOBIN)'
|
||||||
|
echo '##vso[task.prependpath]$(GOROOT)/bin'
|
||||||
|
displayName: 'Set up the Go workspace'
|
||||||
|
- script: |
|
||||||
|
go version
|
||||||
|
go get -v -d ./...
|
||||||
|
cd cmd/wails
|
||||||
|
go install
|
||||||
|
workingDirectory: '$(modulePath)'
|
||||||
|
displayName: 'Get dependencies, then build'
|
||||||
|
- script: |
|
||||||
|
wails version
|
||||||
|
workingDirectory: '$(modulePath)'
|
||||||
|
displayName: 'Check we have output'
|
||||||
|
|
||||||
|
- deployment: Win
|
||||||
|
displayName: Win
|
||||||
|
variables:
|
||||||
|
GOMODULE: 'on'
|
||||||
|
modulePath: '$(Agent.BuildDirectory)/wails' # Path to the module's code
|
||||||
|
pool:
|
||||||
|
vmImage: 'windows-2019'
|
||||||
|
environment: 'win-dev'
|
||||||
|
strategy:
|
||||||
|
runOnce:
|
||||||
|
deploy:
|
||||||
|
steps:
|
||||||
|
- checkout: self # self represents the repo where the initial Pipelines YAML file was found
|
||||||
|
clean: true # whether to fetch clean each time
|
||||||
|
path: wails # path to check out source code, relative to the agent's build directory (e.g. \_work\1)
|
||||||
|
# Go tool installer
|
||||||
|
# Find in cache or download a specific version of Go and add it to the PATH
|
||||||
|
- task: GoTool@0
|
||||||
|
inputs:
|
||||||
|
version: '1.12.7'
|
||||||
|
goPath: '$(Agent.BuildDirectory)/go'
|
||||||
|
goBin: '$(Agent.BuildDirectory)/go/bin'
|
||||||
|
displayName: 'Set up the Go workspace'
|
||||||
|
- script: |
|
||||||
|
go version
|
||||||
|
go get -v -d ./...
|
||||||
|
cd cmd/wails
|
||||||
|
go install
|
||||||
|
workingDirectory: '$(modulePath)'
|
||||||
|
displayName: 'Get dependencies, then build'
|
||||||
|
- script: |
|
||||||
|
wails version
|
||||||
|
workingDirectory: '$(Agent.BuildDirectory)/go/bin'
|
||||||
|
displayName: 'Check we have output'
|
@ -11,10 +11,9 @@ func (app *App) setupCli() *cmd.Cli {
|
|||||||
result := cmd.NewCli(app.config.Title, "Debug build")
|
result := cmd.NewCli(app.config.Title, "Debug build")
|
||||||
result.Version(cmd.Version)
|
result.Version(cmd.Version)
|
||||||
|
|
||||||
// Setup cli to handle loglevel and headless flags
|
// Setup cli to handle loglevel
|
||||||
result.
|
result.
|
||||||
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
|
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
|
||||||
// BoolFlag("headless", "Runs the app in headless mode", &app.headless).
|
|
||||||
Action(app.start)
|
Action(app.start)
|
||||||
|
|
||||||
// Banner
|
// Banner
|
File diff suppressed because one or more lines are too long
@ -18,7 +18,7 @@ func NewGitHubHelper() *GitHubHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetVersionTags gets the list of tags on the Wails repo
|
// GetVersionTags gets the list of tags on the Wails repo
|
||||||
// It retuns a list of sorted tags in descending order
|
// It returns a list of sorted tags in descending order
|
||||||
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
|
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
|
||||||
|
|
||||||
result := []*SemanticVersion{}
|
result := []*SemanticVersion{}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mewn "github.com/leaanthony/mewn"
|
"github.com/leaanthony/mewn"
|
||||||
"github.com/leaanthony/slicer"
|
"github.com/leaanthony/slicer"
|
||||||
"github.com/leaanthony/spinner"
|
"github.com/leaanthony/spinner"
|
||||||
)
|
)
|
||||||
@ -249,8 +249,8 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
|
|||||||
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
|
ioutil.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install the bridge library
|
// Install the runtime
|
||||||
err = InstallBridge(caller, projectDir, projectOptions)
|
err = InstallRuntime(caller, projectDir, projectOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -263,22 +263,29 @@ func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forc
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// InstallBridge installs the relevant bridge javascript library
|
// InstallRuntime installs the correct runtime for the type of build
|
||||||
func InstallBridge(caller string, projectDir string, projectOptions *ProjectOptions) error {
|
func InstallRuntime(caller string, projectDir string, projectOptions *ProjectOptions) error {
|
||||||
bridgeFile := "wailsbridge.prod.js"
|
if caller == "build" {
|
||||||
if caller == "serve" {
|
return InstallProdRuntime(projectDir, projectOptions)
|
||||||
bridgeFile = "wailsbridge.js"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy bridge to project
|
return InstallBridge(projectDir, projectOptions)
|
||||||
bridgeAssets := mewn.Group("../wailsruntimeassets/bridge/")
|
}
|
||||||
bridgeFileData := bridgeAssets.Bytes(bridgeFile)
|
|
||||||
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, projectOptions.FrontEnd.Bridge, "wailsbridge.js")
|
// InstallBridge installs the relevant bridge javascript library
|
||||||
err := fs.CreateFile(bridgeFileTarget, bridgeFileData)
|
func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
|
||||||
if err != nil {
|
bridgeFileData := mewn.String("../runtime/assets/bridge.js")
|
||||||
|
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
|
||||||
|
err := fs.CreateFile(bridgeFileTarget, []byte(bridgeFileData))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
// InstallProdRuntime installs the production runtime
|
||||||
|
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
|
||||||
|
prodInit := mewn.String("../runtime/js/runtime/init.js")
|
||||||
|
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
|
||||||
|
err := fs.CreateFile(bridgeFileTarget, []byte(prodInit))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeProject attempts to serve up the current project so that it may be connected to
|
// ServeProject attempts to serve up the current project so that it may be connected to
|
||||||
|
184
cmd/linux.go
184
cmd/linux.go
@ -17,74 +17,72 @@ type LinuxDistribution int
|
|||||||
const (
|
const (
|
||||||
// Unknown is the catch-all distro
|
// Unknown is the catch-all distro
|
||||||
Unknown LinuxDistribution = iota
|
Unknown LinuxDistribution = iota
|
||||||
|
// Debian distribution
|
||||||
|
Debian
|
||||||
// Ubuntu distribution
|
// Ubuntu distribution
|
||||||
Ubuntu
|
Ubuntu
|
||||||
// Arch linux distribution
|
// Arch linux distribution
|
||||||
Arch
|
Arch
|
||||||
// RedHat linux distribution
|
// CentOS linux distribution
|
||||||
RedHat
|
CentOS
|
||||||
// Debian distribution
|
// Fedora linux distribution
|
||||||
Debian
|
Fedora
|
||||||
|
// Gentoo distribution
|
||||||
|
Gentoo
|
||||||
|
// Zorin distribution
|
||||||
|
Zorin
|
||||||
|
// Parrot distribution
|
||||||
|
Parrot
|
||||||
|
// Linuxmint distribution
|
||||||
|
Linuxmint
|
||||||
|
// VoidLinux distribution
|
||||||
|
VoidLinux
|
||||||
|
// Elementary distribution
|
||||||
|
Elementary
|
||||||
|
// Kali distribution
|
||||||
|
Kali
|
||||||
|
// Neon distribution
|
||||||
|
Neon
|
||||||
|
// Manjaro distribution
|
||||||
|
Manjaro
|
||||||
)
|
)
|
||||||
|
|
||||||
// DistroInfo contains all the information relating to a linux distribution
|
// DistroInfo contains all the information relating to a linux distribution
|
||||||
type DistroInfo struct {
|
type DistroInfo struct {
|
||||||
Distribution LinuxDistribution
|
Distribution LinuxDistribution
|
||||||
|
Name string
|
||||||
|
ID string
|
||||||
Description string
|
Description string
|
||||||
Release string
|
Release string
|
||||||
Codename string
|
|
||||||
DistributorID string
|
|
||||||
DiscoveredBy string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLinuxDistroInfo returns information about the running linux distribution
|
// GetLinuxDistroInfo returns information about the running linux distribution
|
||||||
func GetLinuxDistroInfo() *DistroInfo {
|
func GetLinuxDistroInfo() *DistroInfo {
|
||||||
result := &DistroInfo{Distribution: Unknown}
|
result := &DistroInfo{
|
||||||
program := NewProgramHelper()
|
Distribution: Unknown,
|
||||||
// Does lsb_release exist?
|
ID: "unknown",
|
||||||
|
Name: "Unknown",
|
||||||
lsbRelease := program.FindProgram("lsb_release")
|
}
|
||||||
if lsbRelease != nil {
|
_, err := os.Stat("/etc/os-release")
|
||||||
stdout, _, _, err := lsbRelease.Run("-a")
|
if !os.IsNotExist(err) {
|
||||||
if err != nil {
|
osRelease, _ := ioutil.ReadFile("/etc/os-release")
|
||||||
|
result = parseOsRelease(string(osRelease))
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
result.DiscoveredBy = "lsb"
|
|
||||||
for _, line := range strings.Split(stdout, "\n") {
|
// parseOsRelease parses the given os-release data and returns
|
||||||
if strings.Contains(line, ":") {
|
// a DistroInfo struct with the details
|
||||||
// Iterate lines a
|
func parseOsRelease(osRelease string) *DistroInfo {
|
||||||
details := strings.Split(line, ":")
|
result := &DistroInfo{Distribution: Unknown}
|
||||||
key := strings.TrimSpace(details[0])
|
|
||||||
value := strings.TrimSpace(details[1])
|
|
||||||
switch key {
|
|
||||||
case "Distributor ID":
|
|
||||||
result.DistributorID = value
|
|
||||||
switch value {
|
|
||||||
case "Ubuntu":
|
|
||||||
result.Distribution = Ubuntu
|
|
||||||
case "Arch", "ManjaroLinux":
|
|
||||||
result.Distribution = Arch
|
|
||||||
case "Debian":
|
|
||||||
result.Distribution = Debian
|
|
||||||
}
|
|
||||||
case "Description":
|
|
||||||
result.Description = value
|
|
||||||
case "Release":
|
|
||||||
result.Release = value
|
|
||||||
case "Codename":
|
|
||||||
result.Codename = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check if /etc/os-release exists
|
|
||||||
} else if _, err := os.Stat("/etc/os-release"); !os.IsNotExist(err) {
|
|
||||||
// Default value
|
// Default value
|
||||||
osName := "Unknown"
|
osID := "unknown"
|
||||||
|
osNAME := "Unknown"
|
||||||
version := ""
|
version := ""
|
||||||
// read /etc/os-release
|
|
||||||
osRelease, _ := ioutil.ReadFile("/etc/os-release")
|
|
||||||
// Split into lines
|
// Split into lines
|
||||||
lines := strings.Split(string(osRelease), "\n")
|
lines := strings.Split(osRelease, "\n")
|
||||||
// Iterate lines
|
// Iterate lines
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
// Split each line by the equals char
|
// Split each line by the equals char
|
||||||
@ -94,33 +92,69 @@ func GetLinuxDistroInfo() *DistroInfo {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
switch splitLine[0] {
|
switch splitLine[0] {
|
||||||
|
case "ID":
|
||||||
|
osID = strings.Trim(splitLine[1], "\"")
|
||||||
case "NAME":
|
case "NAME":
|
||||||
osName = strings.Trim(splitLine[1], "\"")
|
osNAME = strings.Trim(splitLine[1], "\"")
|
||||||
case "VERSION_ID":
|
case "VERSION_ID":
|
||||||
version = strings.Trim(splitLine[1], "\"")
|
version = strings.Trim(splitLine[1], "\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// Check distro name against list of distros
|
// Check distro name against list of distros
|
||||||
result.Release = version
|
switch osID {
|
||||||
result.DiscoveredBy = "os-release"
|
case "fedora":
|
||||||
switch osName {
|
result.Distribution = Fedora
|
||||||
case "Fedora":
|
case "centos":
|
||||||
result.Distribution = RedHat
|
result.Distribution = CentOS
|
||||||
case "CentOS":
|
case "arch":
|
||||||
result.Distribution = RedHat
|
|
||||||
case "Arch Linux":
|
|
||||||
result.Distribution = Arch
|
result.Distribution = Arch
|
||||||
case "Debian GNU/Linux":
|
case "debian":
|
||||||
result.Distribution = Debian
|
result.Distribution = Debian
|
||||||
|
case "ubuntu":
|
||||||
|
result.Distribution = Ubuntu
|
||||||
|
case "gentoo":
|
||||||
|
result.Distribution = Gentoo
|
||||||
|
case "zorin":
|
||||||
|
result.Distribution = Zorin
|
||||||
|
case "parrot":
|
||||||
|
result.Distribution = Parrot
|
||||||
|
case "linuxmint":
|
||||||
|
result.Distribution = Linuxmint
|
||||||
|
case "void":
|
||||||
|
result.Distribution = VoidLinux
|
||||||
|
case "elementary":
|
||||||
|
result.Distribution = Elementary
|
||||||
|
case "kali":
|
||||||
|
result.Distribution = Kali
|
||||||
|
case "neon":
|
||||||
|
result.Distribution = Neon
|
||||||
|
case "manjaro":
|
||||||
|
result.Distribution = Manjaro
|
||||||
default:
|
default:
|
||||||
result.Distribution = Unknown
|
result.Distribution = Unknown
|
||||||
result.DistributorID = osName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.Name = osNAME
|
||||||
|
result.ID = osID
|
||||||
|
result.Release = version
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckPkgInstalled is all functions that use local programs to see if a package is installed
|
||||||
|
type CheckPkgInstalled func(string) (bool, error)
|
||||||
|
|
||||||
|
// 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()
|
||||||
@ -143,6 +177,17 @@ func PacmanInstalled(packageName string) (bool, error) {
|
|||||||
return exitCode == 0, nil
|
return exitCode == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XbpsInstalled uses pacman to see if a package is installed.
|
||||||
|
func XbpsInstalled(packageName string) (bool, error) {
|
||||||
|
program := NewProgramHelper()
|
||||||
|
xbpsQuery := program.FindProgram("xbps-query")
|
||||||
|
if xbpsQuery == nil {
|
||||||
|
return false, fmt.Errorf("cannot check dependencies: xbps-query not found")
|
||||||
|
}
|
||||||
|
_, _, exitCode, _ := xbpsQuery.Run("-S", packageName)
|
||||||
|
return exitCode == 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RpmInstalled uses rpm to see if a package is installed
|
// RpmInstalled uses rpm to see if a package is installed
|
||||||
func RpmInstalled(packageName string) (bool, error) {
|
func RpmInstalled(packageName string) (bool, error) {
|
||||||
program := NewProgramHelper()
|
program := NewProgramHelper()
|
||||||
@ -156,18 +201,18 @@ func RpmInstalled(packageName string) (bool, error) {
|
|||||||
|
|
||||||
// RequestSupportForDistribution promts the user to submit a request to support their
|
// RequestSupportForDistribution promts the user to submit a request to support their
|
||||||
// currently unsupported distribution
|
// currently unsupported distribution
|
||||||
func RequestSupportForDistribution(distroInfo *DistroInfo, libraryName string) error {
|
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
|
||||||
var logger = NewLogger()
|
var logger = NewLogger()
|
||||||
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'. Please ensure that the '%s' equivalent is installed", distroInfo.DistributorID, libraryName)
|
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'", distroInfo.Name)
|
||||||
|
|
||||||
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.DistributorID)
|
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.Name)
|
||||||
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.DistributorID)
|
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.Name)
|
||||||
result := Prompt(q, "yes")
|
result := Prompt(q, "yes")
|
||||||
if strings.ToLower(result) != "yes" {
|
if strings.ToLower(result) != "yes" {
|
||||||
return defaultError
|
return defaultError
|
||||||
}
|
}
|
||||||
|
|
||||||
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.DistributorID)
|
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.Name)
|
||||||
|
|
||||||
var str strings.Builder
|
var str strings.Builder
|
||||||
|
|
||||||
@ -182,16 +227,15 @@ func RequestSupportForDistribution(distroInfo *DistroInfo, libraryName string) e
|
|||||||
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
|
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
|
||||||
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
|
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
|
||||||
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
|
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
|
||||||
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.DistributorID))
|
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.ID))
|
||||||
|
str.WriteString(fmt.Sprintf("| Distribution Name | %s |\n", distroInfo.Name))
|
||||||
str.WriteString(fmt.Sprintf("| Distribution Version | %s |\n", distroInfo.Release))
|
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())
|
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.ID, str.String())
|
||||||
fullURL := "https://github.com/wailsapp/wails/issues/new?"
|
fullURL := "https://github.com/wailsapp/wails/issues/new?"
|
||||||
params := "title=" + title + "&body=" + body
|
params := "title=" + title + "&body=" + body
|
||||||
|
|
||||||
fmt.Println("Opening browser to file request.")
|
fmt.Println("Opening browser to file request.")
|
||||||
browser.OpenURL(fullURL + url.PathEscape(params))
|
browser.OpenURL(fullURL + url.PathEscape(params))
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
26
cmd/linux_test.go
Normal file
26
cmd/linux_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUbuntuDetection(t *testing.T) {
|
||||||
|
osrelease := `
|
||||||
|
NAME="Ubuntu"
|
||||||
|
VERSION="18.04.2 LTS (Bionic Beaver)"
|
||||||
|
ID=ubuntu
|
||||||
|
ID_LIKE=debian
|
||||||
|
PRETTY_NAME="Ubuntu 18.04.2 LTS"
|
||||||
|
VERSION_ID="18.04"
|
||||||
|
HOME_URL="https://www.ubuntu.com/"
|
||||||
|
SUPPORT_URL="https://help.ubuntu.com/"
|
||||||
|
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||||
|
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||||
|
VERSION_CODENAME=bionic
|
||||||
|
UBUNTU_CODENAME=bionic
|
||||||
|
`
|
||||||
|
|
||||||
|
result := parseOsRelease(osrelease)
|
||||||
|
if result.Distribution != Ubuntu {
|
||||||
|
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
cmd/linuxdb.go
Normal file
91
cmd/linuxdb.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/leaanthony/mewn"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinuxDB is the database for linux distribution data.
|
||||||
|
type LinuxDB struct {
|
||||||
|
Distributions map[string]*Distribution `yaml:"distributions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distribution holds the os-release ID and a map of releases.
|
||||||
|
type Distribution struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Releases map[string]*Release `yaml:"releases"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRelease attempts to return the specific Release information
|
||||||
|
// for the given release name. If there is no specific match, the
|
||||||
|
// default release data is returned.
|
||||||
|
func (d *Distribution) GetRelease(version string) *Release {
|
||||||
|
result := d.Releases[version]
|
||||||
|
if result == nil {
|
||||||
|
result = d.Releases["default"]
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release holds the name and version of the release as given by
|
||||||
|
// os-release. Programs is a slice of dependant programs required
|
||||||
|
// to be present on the local installation for Wails to function.
|
||||||
|
// Libraries is a slice of libraries that must be present for Wails
|
||||||
|
// applications to compile.
|
||||||
|
type Release struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
GccVersionCommand string `yaml:"gccversioncommand"`
|
||||||
|
Programs []*Prerequisite `yaml:"programs"`
|
||||||
|
Libraries []*Prerequisite `yaml:"libraries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prerequisite is a simple struct containing a program/library name
|
||||||
|
// plus the distribution specific help text indicating how to install
|
||||||
|
// it.
|
||||||
|
type Prerequisite struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Help string `yaml:"help,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load will load the given filename from disk and attempt to
|
||||||
|
// import the data into the LinuxDB.
|
||||||
|
func (l *LinuxDB) Load(filename string) error {
|
||||||
|
if fs.FileExists(filename) {
|
||||||
|
data, err := fs.LoadAsBytes(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return l.ImportData(data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImportData will unmarshal the given YAML formatted data
|
||||||
|
// into the LinuxDB
|
||||||
|
func (l *LinuxDB) ImportData(data []byte) error {
|
||||||
|
return yaml.Unmarshal(data, l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDistro returns the Distribution information for the
|
||||||
|
// given distribution name. If the distribution is not supported,
|
||||||
|
// nil is returned.
|
||||||
|
func (l *LinuxDB) GetDistro(distro string) *Distribution {
|
||||||
|
return l.Distributions[distro]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLinuxDB creates a new LinuxDB instance from the bundled
|
||||||
|
// linuxdb.yaml file.
|
||||||
|
func NewLinuxDB() *LinuxDB {
|
||||||
|
data := mewn.Bytes("./linuxdb.yaml")
|
||||||
|
result := LinuxDB{
|
||||||
|
Distributions: make(map[string]*Distribution),
|
||||||
|
}
|
||||||
|
err := result.ImportData(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
188
cmd/linuxdb.yaml
Normal file
188
cmd/linuxdb.yaml
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
---
|
||||||
|
distributions:
|
||||||
|
debian:
|
||||||
|
id: debian
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
name: Debian
|
||||||
|
version: default
|
||||||
|
gccversioncommand: &gccdumpversion -dumpversion
|
||||||
|
programs: &debiandefaultprograms
|
||||||
|
- name: gcc
|
||||||
|
help: Please install with `sudo apt-get install build-essential` and try again
|
||||||
|
- name: pkg-config
|
||||||
|
help: Please install with `sudo apt-get install pkg-config` and try again
|
||||||
|
- name: npm
|
||||||
|
help: Please install with `curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - && sudo apt-get install -y nodejs` and try again
|
||||||
|
libraries: &debiandefaultlibraries
|
||||||
|
- name: libgtk-3-dev
|
||||||
|
help: Please install with `sudo apt-get install libgtk-3-dev` and try again
|
||||||
|
- name: libwebkit2gtk-4.0-dev
|
||||||
|
help: Please install with `sudo apt-get install libwebkit2gtk-4.0-dev` and try again
|
||||||
|
ubuntu:
|
||||||
|
id: ubuntu
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Ubuntu
|
||||||
|
gccversioncommand: &gccdumpfullversion -dumpfullversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
kali:
|
||||||
|
id: kali
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Kali GNU/Linux
|
||||||
|
gccversioncommand: *gccdumpfullversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
parrot:
|
||||||
|
id: parrot
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Parrot GNU/Linux
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
zorin:
|
||||||
|
id: zorin
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Zorin
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
linuxmint:
|
||||||
|
id: linuxmint
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Linux Mint
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
elementary:
|
||||||
|
id: elementary
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: elementary OS
|
||||||
|
gccversioncommand: *gccdumpfullversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
neon:
|
||||||
|
id: neon
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: KDE neon
|
||||||
|
gccversioncommand: *gccdumpfullversion
|
||||||
|
programs: *debiandefaultprograms
|
||||||
|
libraries: *debiandefaultlibraries
|
||||||
|
void:
|
||||||
|
id: void
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: VoidLinux
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs:
|
||||||
|
- name: gcc
|
||||||
|
help: Please install with `xbps-install base-devel` and try again
|
||||||
|
- name: pkg-config
|
||||||
|
help: Please install with `xbps-install pkg-config` and try again
|
||||||
|
- name: npm
|
||||||
|
help: Please install with `xbps-install nodejs` and try again
|
||||||
|
libraries:
|
||||||
|
- name: gtk+3-devel
|
||||||
|
help: Please install with `xbps-install gtk+3-devel` and try again
|
||||||
|
- name: webkit2gtk-devel
|
||||||
|
help: Please install with `xbps-install webkit2gtk-devel` and try again
|
||||||
|
centos:
|
||||||
|
id: centos
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: CentOS Linux
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs:
|
||||||
|
- name: gcc
|
||||||
|
help: Please install with `sudo yum install gcc-c++ make` and try again
|
||||||
|
- name: pkg-config
|
||||||
|
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
|
||||||
|
- name: npm
|
||||||
|
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
|
||||||
|
libraries:
|
||||||
|
- name: gtk3-devel
|
||||||
|
help: Please install with `sudo yum install gtk3-devel` and try again
|
||||||
|
- name: webkitgtk3-devel
|
||||||
|
help: Please install with `sudo yum install webkitgtk3-devel` and try again
|
||||||
|
fedora:
|
||||||
|
id: fedora
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Fedora
|
||||||
|
gccversioncommand: *gccdumpfullversion
|
||||||
|
programs:
|
||||||
|
- name: gcc
|
||||||
|
help: Please install with `sudo yum install gcc-c++ make` and try again
|
||||||
|
- name: pkg-config
|
||||||
|
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
|
||||||
|
- name: npm
|
||||||
|
help: Please install `sudo yum install nodejs` and try again
|
||||||
|
libraries:
|
||||||
|
- name: gtk3-devel
|
||||||
|
help: Please install with `sudo yum install gtk3-devel` and try again
|
||||||
|
- name: webkit2gtk3-devel
|
||||||
|
help: Please install with `sudo yum install webkit2gtk3-devel` and try again
|
||||||
|
arch:
|
||||||
|
id: arch
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Arch Linux
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs: &archdefaultprograms
|
||||||
|
- name: gcc
|
||||||
|
help: Please install with `sudo pacman -S gcc` and try again
|
||||||
|
- name: pkgconf
|
||||||
|
help: Please install with `sudo pacman -S pkgconf` and try again
|
||||||
|
- name: npm
|
||||||
|
help: Please install with `sudo pacman -S npm` and try again
|
||||||
|
libraries: &archdefaultlibraries
|
||||||
|
- name: gtk3
|
||||||
|
help: Please install with `sudo pacman -S gtk3` and try again
|
||||||
|
- name: webkit2gtk
|
||||||
|
help: Please install with `sudo pacman -S webkit2gtk` and try again
|
||||||
|
manjaro:
|
||||||
|
id: manjaro
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Manjaro Linux
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs: *archdefaultprograms
|
||||||
|
libraries: *archdefaultlibraries
|
||||||
|
gentoo:
|
||||||
|
id: gentoo
|
||||||
|
releases:
|
||||||
|
default:
|
||||||
|
version: default
|
||||||
|
name: Gentoo
|
||||||
|
gccversioncommand: *gccdumpversion
|
||||||
|
programs:
|
||||||
|
- name: gcc
|
||||||
|
help: Please install using your system's package manager
|
||||||
|
- name: pkg-config
|
||||||
|
help: Please install using your system's package manager
|
||||||
|
- name: npm
|
||||||
|
help: Please install using your system's package manager
|
||||||
|
libraries:
|
||||||
|
- name: gtk+:3
|
||||||
|
help: Please install with `sudo emerge gtk+:3` and try again
|
||||||
|
- name: webkit-gtk
|
||||||
|
help: Please install with `sudo emerge webkit-gtk` and try again
|
81
cmd/linuxdb_test.go
Normal file
81
cmd/linuxdb_test.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestNewLinuxDB(t *testing.T) {
|
||||||
|
_ = NewLinuxDB()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKnownDistro(t *testing.T) {
|
||||||
|
var linuxDB = NewLinuxDB()
|
||||||
|
result := linuxDB.GetDistro("ubuntu")
|
||||||
|
if result == nil {
|
||||||
|
t.Error("Cannot get distro 'ubuntu'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownDistro(t *testing.T) {
|
||||||
|
var linuxDB = NewLinuxDB()
|
||||||
|
result := linuxDB.GetDistro("unknown")
|
||||||
|
if result != nil {
|
||||||
|
t.Error("Should get nil for distribution 'unknown'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultRelease(t *testing.T) {
|
||||||
|
var linuxDB = NewLinuxDB()
|
||||||
|
result := linuxDB.GetDistro("ubuntu")
|
||||||
|
if result == nil {
|
||||||
|
t.Error("Cannot get distro 'ubuntu'")
|
||||||
|
}
|
||||||
|
|
||||||
|
release := result.GetRelease("default")
|
||||||
|
if release == nil {
|
||||||
|
t.Error("Cannot get release 'default' for distro 'ubuntu'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownRelease(t *testing.T) {
|
||||||
|
var linuxDB = NewLinuxDB()
|
||||||
|
result := linuxDB.GetDistro("ubuntu")
|
||||||
|
if result == nil {
|
||||||
|
t.Error("Cannot get distro 'ubuntu'")
|
||||||
|
}
|
||||||
|
|
||||||
|
release := result.GetRelease("16.04")
|
||||||
|
if release == nil {
|
||||||
|
t.Error("Failed to get release 'default' for unknown release version '16.04'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if release.Version != "default" {
|
||||||
|
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPrerequisites(t *testing.T) {
|
||||||
|
var linuxDB = NewLinuxDB()
|
||||||
|
result := linuxDB.GetDistro("debian")
|
||||||
|
if result == nil {
|
||||||
|
t.Error("Cannot get distro 'debian'")
|
||||||
|
}
|
||||||
|
|
||||||
|
release := result.GetRelease("default")
|
||||||
|
if release == nil {
|
||||||
|
t.Error("Failed to get release 'default' for unknown release version '16.04'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if release.Version != "default" {
|
||||||
|
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if release.Name != "Debian" {
|
||||||
|
t.Errorf("Got Release Name '%s' instead of 'debian' for unknown release version '16.04'", release.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(release.Programs) != 3 {
|
||||||
|
t.Errorf("Expected %d programs for unknown release version '16.04'", len(release.Programs))
|
||||||
|
}
|
||||||
|
if len(release.Libraries) != 2 {
|
||||||
|
t.Errorf("Expected %d libraries for unknown release version '16.04'", len(release.Libraries))
|
||||||
|
}
|
||||||
|
}
|
@ -5,13 +5,6 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Prerequisite defines a Prerequisite!
|
|
||||||
type Prerequisite struct {
|
|
||||||
Name string
|
|
||||||
Help string
|
|
||||||
Path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPrerequisite(name, help string) *Prerequisite {
|
func newPrerequisite(name, help string) *Prerequisite {
|
||||||
return &Prerequisite{Name: name, Help: help}
|
return &Prerequisite{Name: name, Help: help}
|
||||||
}
|
}
|
||||||
@ -48,16 +41,13 @@ func getRequiredProgramsOSX() *Prerequisites {
|
|||||||
func getRequiredProgramsLinux() *Prerequisites {
|
func getRequiredProgramsLinux() *Prerequisites {
|
||||||
result := &Prerequisites{}
|
result := &Prerequisites{}
|
||||||
distroInfo := GetLinuxDistroInfo()
|
distroInfo := GetLinuxDistroInfo()
|
||||||
switch distroInfo.Distribution {
|
if distroInfo.Distribution != Unknown {
|
||||||
case Ubuntu, Debian:
|
var linuxDB = NewLinuxDB()
|
||||||
result.Add(newPrerequisite("gcc", "Please install with `sudo apt install build-essentials` and try again"))
|
distro := linuxDB.GetDistro(distroInfo.ID)
|
||||||
result.Add(newPrerequisite("pkg-config", "Please install with `sudo apt install pkg-config` and try again"))
|
release := distro.GetRelease(distroInfo.Release)
|
||||||
result.Add(newPrerequisite("npm", "Please install with `sudo snap install node --channel=12/stable --classic` and try again"))
|
for _, program := range release.Programs {
|
||||||
default:
|
result.Add(program)
|
||||||
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("npm", "Please install from https://nodejs.org/en/download/ and try again"))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -91,20 +81,15 @@ func getRequiredLibrariesOSX() (*Prerequisites, error) {
|
|||||||
|
|
||||||
func getRequiredLibrariesLinux() (*Prerequisites, error) {
|
func getRequiredLibrariesLinux() (*Prerequisites, error) {
|
||||||
result := &Prerequisites{}
|
result := &Prerequisites{}
|
||||||
|
// The Linux Distribution DB
|
||||||
distroInfo := GetLinuxDistroInfo()
|
distroInfo := GetLinuxDistroInfo()
|
||||||
switch distroInfo.Distribution {
|
if distroInfo.Distribution != Unknown {
|
||||||
case Ubuntu:
|
var linuxDB = NewLinuxDB()
|
||||||
result.Add(newPrerequisite("libgtk-3-dev", "Please install with `sudo apt install libgtk-3-dev` and try again"))
|
distro := linuxDB.GetDistro(distroInfo.ID)
|
||||||
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with `sudo apt install libwebkit2gtk-4.0-dev` and try again"))
|
release := distro.GetRelease(distroInfo.Release)
|
||||||
case Arch:
|
for _, library := range release.Libraries {
|
||||||
result.Add(newPrerequisite("gtk3", "Please install with `sudo pacman -S gtk3` and try again"))
|
result.Add(library)
|
||||||
result.Add(newPrerequisite("webkit2gtk", "Please install with `sudo pacman -S webkit2gtk` and try again"))
|
}
|
||||||
case RedHat:
|
|
||||||
result.Add(newPrerequisite("gtk3-devel", "Please install with `sudo yum install gtk3-devel` and try again"))
|
|
||||||
result.Add(newPrerequisite("webkit2gtk3-devel", "Please install with `sudo yum install webkit2gtk3-devel` and try again"))
|
|
||||||
default:
|
|
||||||
result.Add(newPrerequisite("libgtk-3-dev", "Please install with your system package manager and try again"))
|
|
||||||
result.Add(newPrerequisite("libwebkit2gtk-4.0-dev", "Please install with your system package manager and try again"))
|
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -117,14 +117,15 @@ func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
|
|||||||
}
|
}
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
var stderr string
|
var stderr string
|
||||||
// fmt.Printf("RunCommandArray = %s %+v\n", program, args)
|
var stdout string
|
||||||
if len(dir) > 0 {
|
if len(dir) > 0 {
|
||||||
_, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
|
stdout, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
|
||||||
} else {
|
} else {
|
||||||
_, stderr, err = p.shell.Run(program, args...)
|
stdout, stderr, err = p.shell.Run(program, args...)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(stderr)
|
fmt.Println(stderr)
|
||||||
|
fmt.Println(stdout)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -112,9 +112,9 @@ func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
|
|||||||
Description: "Enter your project description",
|
Description: "Enter your project description",
|
||||||
Version: "0.1.0",
|
Version: "0.1.0",
|
||||||
BinaryName: "",
|
BinaryName: "",
|
||||||
system: NewSystemHelper(),
|
system: ph.system,
|
||||||
log: NewLogger(),
|
log: ph.log,
|
||||||
templates: NewTemplateHelper(),
|
templates: ph.templates,
|
||||||
Author: &author{},
|
Author: &author{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -20,11 +19,7 @@ func Prompt(question string, defaultValue ...string) string {
|
|||||||
fmt.Printf(question + ": ")
|
fmt.Printf(question + ": ")
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
input, _ := reader.ReadString('\n')
|
input, _ := reader.ReadString('\n')
|
||||||
EOL := "\n"
|
input = strings.TrimSpace(input)
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
EOL = "\r\n"
|
|
||||||
}
|
|
||||||
input = strings.Replace(input, EOL, "", -1)
|
|
||||||
|
|
||||||
if input != "" {
|
if input != "" {
|
||||||
answer = input
|
answer = input
|
||||||
|
@ -269,44 +269,35 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var libraryChecker CheckPkgInstalled
|
||||||
distroInfo := GetLinuxDistroInfo()
|
distroInfo := GetLinuxDistroInfo()
|
||||||
for _, library := range *requiredLibraries {
|
|
||||||
switch distroInfo.Distribution {
|
switch distroInfo.Distribution {
|
||||||
case Ubuntu, Debian:
|
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon:
|
||||||
installed, err := DpkgInstalled(library.Name)
|
libraryChecker = DpkgInstalled
|
||||||
if err != nil {
|
case Arch, Manjaro:
|
||||||
return false, err
|
libraryChecker = PacmanInstalled
|
||||||
}
|
case CentOS, Fedora:
|
||||||
if !installed {
|
libraryChecker = RpmInstalled
|
||||||
errors = true
|
case Gentoo:
|
||||||
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
|
libraryChecker = EqueryInstalled
|
||||||
} else {
|
case VoidLinux:
|
||||||
logger.Green("Library '%s' installed.", library.Name)
|
libraryChecker = XbpsInstalled
|
||||||
}
|
|
||||||
case Arch:
|
|
||||||
installed, err := PacmanInstalled(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)
|
|
||||||
}
|
|
||||||
case RedHat:
|
|
||||||
installed, err := RpmInstalled(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, RequestSupportForDistribution(distroInfo, library.Name)
|
return false, RequestSupportForDistribution(distroInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, library := range *requiredLibraries {
|
||||||
|
installed, err := libraryChecker(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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ func NewTemplateHelper() *TemplateHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidTemplate returns true if the given tempalte name resides on disk
|
// IsValidTemplate returns true if the given template name resides on disk
|
||||||
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
|
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
|
||||||
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
|
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
|
||||||
return t.fs.DirExists(pathToTemplate)
|
return t.fs.DirExists(pathToTemplate)
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@angular/platform-browser": "~8.0.1",
|
"@angular/platform-browser": "~8.0.1",
|
||||||
"@angular/platform-browser-dynamic": "~8.0.1",
|
"@angular/platform-browser-dynamic": "~8.0.1",
|
||||||
"@angular/router": "~8.0.1",
|
"@angular/router": "~8.0.1",
|
||||||
|
"@wailsapp/runtime": "^1.0.0",
|
||||||
"ngx-build-plus": "^8.0.3",
|
"ngx-build-plus": "^8.0.3",
|
||||||
"rxjs": "~6.4.0",
|
"rxjs": "~6.4.0",
|
||||||
"tslib": "^1.9.0",
|
"tslib": "^1.9.0",
|
||||||
|
@ -6,13 +6,13 @@ import { environment } from './environments/environment';
|
|||||||
|
|
||||||
import 'zone.js'
|
import 'zone.js'
|
||||||
|
|
||||||
import Bridge from './wailsbridge';
|
import Wails from '@wailsapp/runtime';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
Bridge.Start(() => {
|
Wails.Init(() => {
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
.catch(err => console.error(err));
|
.catch(err => console.error(err));
|
||||||
});
|
});
|
||||||
|
@ -4,10 +4,12 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"core-js": "^3.1.4",
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"wails-react-scripts": "3.0.1-2",
|
"wails-react-scripts": "3.0.1-2",
|
||||||
"react-modal": "3.8.1"
|
"react-modal": "3.8.1",
|
||||||
|
"@wailsapp/runtime": "^1.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import 'core-js/stable';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
import Bridge from "./wailsbridge";
|
import Wails from '@wailsapp/runtime';
|
||||||
|
|
||||||
Bridge.Start(() => {
|
Wails.Init(() => {
|
||||||
ReactDOM.render(<App />, document.getElementById('app'));
|
ReactDOM.render(<App />, document.getElementById('app'));
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": "^2.6.4",
|
"core-js": "^2.6.4",
|
||||||
"vue": "^2.5.22"
|
"vue": "^2.5.22",
|
||||||
|
"@wailsapp/runtime": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.4.0",
|
"@vue/cli-plugin-babel": "^3.4.0",
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import Vue from "vue";
|
import Vue from 'vue';
|
||||||
import App from "./App.vue";
|
import App from './App.vue';
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
Vue.config.devtools = true;
|
Vue.config.devtools = true;
|
||||||
|
|
||||||
import Bridge from "./wailsbridge";
|
import Wails from '@wailsapp/runtime';
|
||||||
|
|
||||||
Bridge.Start(() => {
|
Wails.Init(() => {
|
||||||
new Vue({
|
new Vue({
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount("#app");
|
}).$mount('#app');
|
||||||
});
|
});
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
@ -12,7 +12,8 @@
|
|||||||
"core-js": "^2.6.4",
|
"core-js": "^2.6.4",
|
||||||
"material-design-icons-iconfont": "^5.0.1",
|
"material-design-icons-iconfont": "^5.0.1",
|
||||||
"vue": "^2.5.22",
|
"vue": "^2.5.22",
|
||||||
"vuetify": "^1.5.14"
|
"vuetify": "^1.5.14",
|
||||||
|
"@wailsapp/runtime": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^3.4.0",
|
"@vue/cli-plugin-babel": "^3.4.0",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'babel-polyfill';
|
import 'babel-polyfill';
|
||||||
import Vue from "vue";
|
import Vue from 'vue';
|
||||||
|
|
||||||
// Setup Vuetify
|
// Setup Vuetify
|
||||||
import Vuetify from 'vuetify';
|
import Vuetify from 'vuetify';
|
||||||
@ -7,15 +7,15 @@ Vue.use(Vuetify);
|
|||||||
import 'vuetify/dist/vuetify.min.css';
|
import 'vuetify/dist/vuetify.min.css';
|
||||||
import 'material-design-icons-iconfont';
|
import 'material-design-icons-iconfont';
|
||||||
|
|
||||||
import App from "./App.vue";
|
import App from './App.vue';
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
Vue.config.devtools = true;
|
Vue.config.devtools = true;
|
||||||
|
|
||||||
import Bridge from "./wailsbridge";
|
import Wails from '@wailsapp/runtime';
|
||||||
|
|
||||||
Bridge.Start(() => {
|
Wails.Init(() => {
|
||||||
new Vue({
|
new Vue({
|
||||||
render: h => h(App)
|
render: h => h(App)
|
||||||
}).$mount("#app");
|
}).$mount('#app');
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
// Version - Wails version
|
// Version - Wails version
|
||||||
const Version = "v0.17.0"
|
const Version = "v0.18.1"
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/cmd"
|
"github.com/wailsapp/wails/cmd"
|
||||||
@ -24,7 +23,7 @@ func init() {
|
|||||||
|
|
||||||
system := cmd.NewSystemHelper()
|
system := cmd.NewSystemHelper()
|
||||||
err = system.Initialise()
|
err = system.Initialise()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,23 +32,9 @@ Create your first project by running 'wails init'.`
|
|||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
successMessage = "🚀 " + successMessage
|
successMessage = "🚀 " + successMessage
|
||||||
}
|
}
|
||||||
// Platform check
|
|
||||||
err = platformCheck()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we have a cgo capable environment
|
// Chrck for programs and libraries dependencies
|
||||||
logger.Yellow("Checking for prerequisites...")
|
errors, err := cmd.CheckDependencies(logger)
|
||||||
var requiredProgramErrors bool
|
|
||||||
requiredProgramErrors, err = checkRequiredPrograms()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Linux has library deps
|
|
||||||
var libraryErrors bool
|
|
||||||
libraryErrors, err = checkLibraries()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -60,76 +45,14 @@ Create your first project by running 'wails init'.`
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.White("")
|
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
var errors = libraryErrors || requiredProgramErrors
|
// CheckDependencies() returns !errors
|
||||||
if !errors {
|
// so to get the right message in this
|
||||||
|
// check we have to do it in reversed
|
||||||
|
if errors {
|
||||||
logger.Yellow(successMessage)
|
logger.Yellow(successMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func platformCheck() error {
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "darwin":
|
|
||||||
logger.Yellow("Detected Platform: OSX")
|
|
||||||
case "windows":
|
|
||||||
logger.Yellow("Detected Platform: Windows")
|
|
||||||
case "linux":
|
|
||||||
logger.Yellow("Detected Platform: Linux")
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("Platform %s is currently not supported", runtime.GOOS)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkLibraries() (errors bool, err error) {
|
|
||||||
if runtime.GOOS == "linux" {
|
|
||||||
// Check library prerequisites
|
|
||||||
requiredLibraries, err := cmd.GetRequiredLibraries()
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
distroInfo := cmd.GetLinuxDistroInfo()
|
|
||||||
for _, library := range *requiredLibraries {
|
|
||||||
switch distroInfo.Distribution {
|
|
||||||
case cmd.Ubuntu, cmd.Debian:
|
|
||||||
installed, err := cmd.DpkgInstalled(library.Name)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !installed {
|
|
||||||
errors = true
|
|
||||||
logger.Red("Library '%s' not found. %s", library.Name, library.Help)
|
|
||||||
} else {
|
|
||||||
logger.Green("Library '%s' installed.", library.Name)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return false, cmd.RequestSupportForDistribution(distroInfo, library.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRequiredPrograms() (errors bool, err error) {
|
|
||||||
requiredPrograms, err := cmd.GetRequiredPrograms()
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
errors = false
|
|
||||||
programHelper := cmd.NewProgramHelper()
|
|
||||||
for _, program := range *requiredPrograms {
|
|
||||||
bin := programHelper.FindProgram(program.Name)
|
|
||||||
if bin == nil {
|
|
||||||
errors = true
|
|
||||||
logger.Red("Program '%s' not found. %s", program.Name, program.Help)
|
|
||||||
} else {
|
|
||||||
logger.Green("Program '%s' found: %s", program.Name, bin.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -72,7 +72,16 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that runtime init.js is the production version
|
||||||
|
err = cmd.InstallProdRuntime(projectDir, projectOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Move to project directory
|
// Move to project directory
|
||||||
err = os.Chdir(projectDir)
|
err = os.Chdir(projectDir)
|
||||||
|
@ -45,7 +45,7 @@ func init() {
|
|||||||
projectDir := fs.Cwd()
|
projectDir := fs.Cwd()
|
||||||
|
|
||||||
// Install the bridge library
|
// Install the bridge library
|
||||||
err = cmd.InstallBridge("serve", projectDir, projectOptions)
|
err = cmd.InstallBridge(projectDir, projectOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -42,12 +42,64 @@ To help you in this process, we will ask for some information, add Go/Wails deta
|
|||||||
gomodule = "(Not Set)"
|
gomodule = "(Not Set)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get version numbers for GCC, node & npm
|
||||||
|
program := cmd.NewProgramHelper()
|
||||||
|
// string helpers
|
||||||
|
var gccVersion, nodeVersion, npmVersion string
|
||||||
|
|
||||||
|
// choose between OS (mac,linux,win)
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
gcc := program.FindProgram("gcc")
|
||||||
|
if gcc != nil {
|
||||||
|
stdout, _, _, _ := gcc.Run("-dumpversion")
|
||||||
|
gccVersion = strings.TrimSpace(stdout)
|
||||||
|
}
|
||||||
|
case "linux":
|
||||||
|
// for linux we have to collect
|
||||||
|
// the distribution name
|
||||||
|
distroInfo := cmd.GetLinuxDistroInfo()
|
||||||
|
linuxDB := cmd.NewLinuxDB()
|
||||||
|
distro := linuxDB.GetDistro(distroInfo.ID)
|
||||||
|
release := distro.GetRelease(distroInfo.Release)
|
||||||
|
gccVersionCommand := release.GccVersionCommand
|
||||||
|
|
||||||
|
gcc := program.FindProgram("gcc")
|
||||||
|
if gcc != nil {
|
||||||
|
stdout, _, _, _ := gcc.Run(gccVersionCommand)
|
||||||
|
gccVersion = strings.TrimSpace(stdout)
|
||||||
|
}
|
||||||
|
case "windows":
|
||||||
|
gcc := program.FindProgram("gcc")
|
||||||
|
if gcc != nil {
|
||||||
|
stdout, _, _, _ := gcc.Run("-dumpversion")
|
||||||
|
gccVersion = strings.TrimSpace(stdout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
npm := program.FindProgram("npm")
|
||||||
|
if npm != nil {
|
||||||
|
stdout, _, _, _ := npm.Run("--version")
|
||||||
|
nodeVersion = stdout
|
||||||
|
nodeVersion = nodeVersion[:len(nodeVersion)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
node := program.FindProgram("node")
|
||||||
|
if node != nil {
|
||||||
|
stdout, _, _, _ := node.Run("--version")
|
||||||
|
npmVersion = stdout
|
||||||
|
npmVersion = npmVersion[:len(npmVersion)-1]
|
||||||
|
}
|
||||||
|
|
||||||
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
|
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
|
||||||
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", cmd.Version))
|
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", cmd.Version))
|
||||||
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
|
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
|
||||||
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
|
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
|
||||||
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
|
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
|
||||||
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
|
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
|
||||||
|
str.WriteString(fmt.Sprintf("| GCC | %s |\n", gccVersion))
|
||||||
|
str.WriteString(fmt.Sprintf("| Npm | %s |\n", npmVersion))
|
||||||
|
str.WriteString(fmt.Sprintf("| Node | %s |\n", nodeVersion))
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Processing template and preparing for upload.")
|
fmt.Println("Processing template and preparing for upload.")
|
||||||
|
110
config.go
Normal file
110
config.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package wails
|
||||||
|
|
||||||
|
import "github.com/leaanthony/mewn"
|
||||||
|
|
||||||
|
// AppConfig is the configuration structure used when creating a Wails App object
|
||||||
|
type AppConfig struct {
|
||||||
|
Width, Height int
|
||||||
|
Title string
|
||||||
|
defaultHTML string
|
||||||
|
HTML string
|
||||||
|
JS string
|
||||||
|
CSS string
|
||||||
|
Colour string
|
||||||
|
Resizable bool
|
||||||
|
DisableInspector 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 default HTML
|
||||||
|
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 {
|
||||||
|
if in.CSS != "" {
|
||||||
|
a.CSS = in.CSS
|
||||||
|
}
|
||||||
|
if in.Title != "" {
|
||||||
|
a.Title = in.Title
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Colour != "" {
|
||||||
|
a.Colour = in.Colour
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.JS != "" {
|
||||||
|
a.JS = in.JS
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.Width != 0 {
|
||||||
|
a.Width = in.Width
|
||||||
|
}
|
||||||
|
if in.Height != 0 {
|
||||||
|
a.Height = in.Height
|
||||||
|
}
|
||||||
|
a.Resizable = in.Resizable
|
||||||
|
a.DisableInspector = in.DisableInspector
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the default configuration
|
||||||
|
func newConfig(userConfig *AppConfig) (*AppConfig, error) {
|
||||||
|
result := &AppConfig{
|
||||||
|
Width: 800,
|
||||||
|
Height: 600,
|
||||||
|
Resizable: true,
|
||||||
|
Title: "My Wails App",
|
||||||
|
Colour: "#FFF", // White by default
|
||||||
|
HTML: mewn.String("./runtime/assets/default.html"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if userConfig != nil {
|
||||||
|
err := result.merge(userConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
2
go.mod
2
go.mod
@ -22,9 +22,9 @@ require (
|
|||||||
github.com/pkg/errors v0.8.1 // indirect
|
github.com/pkg/errors v0.8.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.4.1
|
github.com/sirupsen/logrus v1.4.1
|
||||||
github.com/stretchr/testify v1.3.0 // indirect
|
github.com/stretchr/testify v1.3.0 // indirect
|
||||||
github.com/wailsapp/webview v0.2.7
|
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect
|
||||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect
|
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862
|
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862
|
||||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||||
)
|
)
|
||||||
|
13
go.sum
13
go.sum
@ -25,7 +25,6 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU
|
|||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
|
github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o=
|
||||||
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
|
github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@ -41,12 +40,10 @@ github.com/leaanthony/synx v0.1.0 h1:R0lmg2w6VMb8XcotOwAe5DLyzwjLrskNkwU7LLWsyL8
|
|||||||
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
|
github.com/leaanthony/synx v0.1.0/go.mod h1:Iz7eybeeG8bdq640iR+CwYb8p+9EOsgMWghkSRyZcqs=
|
||||||
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
|
github.com/leaanthony/wincursor v0.1.0 h1:Dsyp68QcF5cCs65AMBmxoYNEm0n8K7mMchG6a8fYxf8=
|
||||||
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
|
github.com/leaanthony/wincursor v0.1.0/go.mod h1:7TVwwrzSH/2Y9gLOGH+VhA+bZhoWXBRgbGNTMk+yimE=
|
||||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
|
||||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||||
@ -71,11 +68,7 @@ github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/wailsapp/webview v0.2.7 h1:fN5L5H9Oivg9IJPk7uaXQnjqB68Fny11ZWkIaTIZHmk=
|
|
||||||
github.com/wailsapp/webview v0.2.7/go.mod h1:XO9HJbKWokDxUYTWQEBCYg95n/To1v7PxvanDNVf8hY=
|
|
||||||
github.com/zserge/webview v0.0.0-20190123072648-16c93bcaeaeb/go.mod h1:a1CV8KR4Dd1eP2g+mEijGOp+HKczwdKHWyx0aPHKvo4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@ -84,10 +77,8 @@ golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR17
|
|||||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180606202747-9527bec2660b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb h1:pf3XwC90UUdNPYWZdFjhGBE7DUFuK3Ct1zWmZ65QN30=
|
|
||||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
|
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
|
||||||
@ -95,3 +86,7 @@ golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
|
gopkg.in/AlecAivazis/survey.v1 v1.8.4/go.mod h1:iBNOmqKz/NUbZx3bA+4hAGLRC7fSK7tgtVDT4tB22XA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -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,
|
@ -1,21 +1,27 @@
|
|||||||
package wails
|
package binding
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
import "fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/lib/messages"
|
||||||
|
"github.com/wailsapp/wails/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
type internalMethods struct {
|
type internalMethods struct {
|
||||||
log *CustomLogger
|
log *logger.CustomLogger
|
||||||
browser *RuntimeBrowser
|
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)
|
||||||
}
|
}
|
@ -1,47 +1,46 @@
|
|||||||
package wails
|
package binding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/lib/messages"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
// 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
|
||||||
@ -196,10 +195,14 @@ func (b *bindingManager) processFunctionCall(callData *callData) (interface{}, e
|
|||||||
return nil, errorResult.Interface().(error)
|
return nil, errorResult.Interface().(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// fmt.Printf("result = '%+v'\n", result)
|
||||||
|
if len(result) > 0 {
|
||||||
return result[0].Interface(), nil
|
return result[0].Interface(), nil
|
||||||
}
|
}
|
||||||
|
return nil, 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 +236,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 +275,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}
|
@ -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 {
|
@ -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
|
||||||
}
|
}
|
14
lib/interfaces/appconfig.go
Normal file
14
lib/interfaces/appconfig.go
Normal 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
|
||||||
|
}
|
10
lib/interfaces/bindingmanager.go
Normal file
10
lib/interfaces/bindingmanager.go
Normal 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)
|
||||||
|
}
|
11
lib/interfaces/eventmanager.go
Normal file
11
lib/interfaces/eventmanager.go
Normal 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)
|
||||||
|
}
|
8
lib/interfaces/ipcmanager.go
Normal file
8
lib/interfaces/ipcmanager.go
Normal 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)
|
||||||
|
}
|
@ -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
|
4
lib/interfaces/runtime.go
Normal file
4
lib/interfaces/runtime.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package interfaces
|
||||||
|
|
||||||
|
// Runtime interface
|
||||||
|
type Runtime interface {}
|
@ -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{})
|
@ -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{})
|
@ -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{})
|
@ -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)
|
logger.GlobalLogger.Info(logdata.Message)
|
||||||
case "debug":
|
case "debug":
|
||||||
logger.Debug(logdata.Message)
|
logger.GlobalLogger.Debug(logdata.Message)
|
||||||
case "warning":
|
case "warning":
|
||||||
logger.Warning(logdata.Message)
|
logger.GlobalLogger.Warn(logdata.Message)
|
||||||
case "error":
|
case "error":
|
||||||
logger.Error(logdata.Message)
|
logger.GlobalLogger.Error(logdata.Message)
|
||||||
case "fatal":
|
case "fatal":
|
||||||
logger.Fatal(logdata.Message)
|
logger.GlobalLogger.Fatal(logdata.Message)
|
||||||
default:
|
default:
|
||||||
i.log.ErrorFields("Invalid log level sent", Fields{
|
logger.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()
|
@ -1,4 +1,4 @@
|
|||||||
package wails
|
package ipc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
@ -1,4 +1,4 @@
|
|||||||
package wails
|
package ipc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
@ -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 + "] ",
|
||||||
}
|
}
|
||||||
@ -14,90 +15,90 @@ func newCustomLogger(prefix string) *CustomLogger {
|
|||||||
|
|
||||||
// Info level message
|
// Info level message
|
||||||
func (c *CustomLogger) Info(message string) {
|
func (c *CustomLogger) Info(message string) {
|
||||||
logger.Info(c.prefix + message)
|
GlobalLogger.Info(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infof - formatted message
|
// Infof - formatted message
|
||||||
func (c *CustomLogger) Infof(message string, args ...interface{}) {
|
func (c *CustomLogger) Infof(message string, args ...interface{}) {
|
||||||
logger.Infof(c.prefix+message, args...)
|
GlobalLogger.Infof(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InfoFields - message with fields
|
// InfoFields - message with fields
|
||||||
func (c *CustomLogger) InfoFields(message string, fields Fields) {
|
func (c *CustomLogger) InfoFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Info(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Info(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug level message
|
// Debug level message
|
||||||
func (c *CustomLogger) Debug(message string) {
|
func (c *CustomLogger) Debug(message string) {
|
||||||
logger.Debug(c.prefix + message)
|
GlobalLogger.Debug(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf - formatted message
|
// Debugf - formatted message
|
||||||
func (c *CustomLogger) Debugf(message string, args ...interface{}) {
|
func (c *CustomLogger) Debugf(message string, args ...interface{}) {
|
||||||
logger.Debugf(c.prefix+message, args...)
|
GlobalLogger.Debugf(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DebugFields - message with fields
|
// DebugFields - message with fields
|
||||||
func (c *CustomLogger) DebugFields(message string, fields Fields) {
|
func (c *CustomLogger) DebugFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Debug(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Debug(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn level message
|
// Warn level message
|
||||||
func (c *CustomLogger) Warn(message string) {
|
func (c *CustomLogger) Warn(message string) {
|
||||||
logger.Warn(c.prefix + message)
|
GlobalLogger.Warn(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnf - formatted message
|
// Warnf - formatted message
|
||||||
func (c *CustomLogger) Warnf(message string, args ...interface{}) {
|
func (c *CustomLogger) Warnf(message string, args ...interface{}) {
|
||||||
logger.Warnf(c.prefix+message, args...)
|
GlobalLogger.Warnf(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WarnFields - message with fields
|
// WarnFields - message with fields
|
||||||
func (c *CustomLogger) WarnFields(message string, fields Fields) {
|
func (c *CustomLogger) WarnFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Warn(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Warn(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error level message
|
// Error level message
|
||||||
func (c *CustomLogger) Error(message string) {
|
func (c *CustomLogger) Error(message string) {
|
||||||
logger.Error(c.prefix + message)
|
GlobalLogger.Error(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf - formatted message
|
// Errorf - formatted message
|
||||||
func (c *CustomLogger) Errorf(message string, args ...interface{}) {
|
func (c *CustomLogger) Errorf(message string, args ...interface{}) {
|
||||||
logger.Errorf(c.prefix+message, args...)
|
GlobalLogger.Errorf(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorFields - message with fields
|
// ErrorFields - message with fields
|
||||||
func (c *CustomLogger) ErrorFields(message string, fields Fields) {
|
func (c *CustomLogger) ErrorFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Error(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Error(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal level message
|
// Fatal level message
|
||||||
func (c *CustomLogger) Fatal(message string) {
|
func (c *CustomLogger) Fatal(message string) {
|
||||||
logger.Fatal(c.prefix + message)
|
GlobalLogger.Fatal(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatalf - formatted message
|
// Fatalf - formatted message
|
||||||
func (c *CustomLogger) Fatalf(message string, args ...interface{}) {
|
func (c *CustomLogger) Fatalf(message string, args ...interface{}) {
|
||||||
logger.Fatalf(c.prefix+message, args...)
|
GlobalLogger.Fatalf(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FatalFields - message with fields
|
// FatalFields - message with fields
|
||||||
func (c *CustomLogger) FatalFields(message string, fields Fields) {
|
func (c *CustomLogger) FatalFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Fatal(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Fatal(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panic level message
|
// Panic level message
|
||||||
func (c *CustomLogger) Panic(message string) {
|
func (c *CustomLogger) Panic(message string) {
|
||||||
logger.Panic(c.prefix + message)
|
GlobalLogger.Panic(c.prefix + message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panicf - formatted message
|
// Panicf - formatted message
|
||||||
func (c *CustomLogger) Panicf(message string, args ...interface{}) {
|
func (c *CustomLogger) Panicf(message string, args ...interface{}) {
|
||||||
logger.Panicf(c.prefix+message, args...)
|
GlobalLogger.Panicf(c.prefix+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PanicFields - message with fields
|
// PanicFields - message with fields
|
||||||
func (c *CustomLogger) PanicFields(message string, fields Fields) {
|
func (c *CustomLogger) PanicFields(message string, fields Fields) {
|
||||||
logger.WithFields(map[string]interface{}(fields)).Panic(c.prefix + message)
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Panic(c.prefix + message)
|
||||||
}
|
}
|
47
lib/logger/log.go
Normal file
47
lib/logger/log.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GlobalLogger is the global logger
|
||||||
|
var GlobalLogger = logrus.New()
|
||||||
|
|
||||||
|
// Fields is used by the customLogger object to output
|
||||||
|
// fields along with a message
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Default options for the global logger
|
||||||
|
func init() {
|
||||||
|
GlobalLogger.SetOutput(os.Stdout)
|
||||||
|
GlobalLogger.SetLevel(logrus.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorFields is a helper for logging fields to the global logger
|
||||||
|
func ErrorFields(message string, fields Fields) {
|
||||||
|
GlobalLogger.WithFields(map[string]interface{}(fields)).Error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogLevel sets the log level to the given level
|
||||||
|
func SetLogLevel(level string) {
|
||||||
|
switch strings.ToLower(level) {
|
||||||
|
case "info":
|
||||||
|
GlobalLogger.SetLevel(logrus.InfoLevel)
|
||||||
|
case "debug":
|
||||||
|
GlobalLogger.SetLevel(logrus.DebugLevel)
|
||||||
|
case "warn":
|
||||||
|
GlobalLogger.SetLevel(logrus.WarnLevel)
|
||||||
|
case "error":
|
||||||
|
GlobalLogger.SetLevel(logrus.ErrorLevel)
|
||||||
|
case "fatal":
|
||||||
|
GlobalLogger.SetLevel(logrus.FatalLevel)
|
||||||
|
case "panic":
|
||||||
|
GlobalLogger.SetLevel(logrus.PanicLevel)
|
||||||
|
default:
|
||||||
|
GlobalLogger.SetLevel(logrus.DebugLevel)
|
||||||
|
GlobalLogger.Warnf("Log level '%s' not recognised. Setting to Debug.", level)
|
||||||
|
}
|
||||||
|
}
|
7
lib/messages/calldata.go
Normal file
7
lib/messages/calldata.go
Normal 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"`
|
||||||
|
}
|
7
lib/messages/eventdata.go
Normal file
7
lib/messages/eventdata.go
Normal 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
7
lib/messages/logdata.go
Normal 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"`
|
||||||
|
}
|
@ -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
|
||||||
@ -28,17 +31,17 @@ func (m messageType) toString() string {
|
|||||||
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
|
return [...]string{"j", "s", "h", "n", "b", "c", "w"}[m]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Headless is a backend that opens a local web server
|
// Bridge is a backend that opens a local web server
|
||||||
// and renders the files over a websocket
|
// and renders the files over a websocket
|
||||||
type Headless struct {
|
type Bridge 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
|
// Bridge specific
|
||||||
initialisationJS []string
|
initialisationJS []string
|
||||||
server *http.Server
|
server *http.Server
|
||||||
theConnection *websocket.Conn
|
theConnection *websocket.Conn
|
||||||
@ -47,17 +50,17 @@ type Headless struct {
|
|||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the Headless Renderer
|
// Initialise the Bridge Renderer
|
||||||
func (h *Headless) Initialise(appConfig *AppConfig, ipcManager *ipcManager, eventManager *eventManager) error {
|
func (h *Bridge) 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
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headless) evalJS(js string, mtype messageType) error {
|
func (h *Bridge) evalJS(js string, mtype messageType) error {
|
||||||
|
|
||||||
message := mtype.toString() + js
|
message := mtype.toString() + js
|
||||||
|
|
||||||
@ -71,7 +74,7 @@ func (h *Headless) evalJS(js string, mtype messageType) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headless) injectCSS(css string) {
|
func (h *Bridge) injectCSS(css string) {
|
||||||
// Minify css to overcome issues in the browser with carriage returns
|
// Minify css to overcome issues in the browser with carriage returns
|
||||||
minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{
|
minified, err := htmlmin.Minify([]byte(css), &htmlmin.Options{
|
||||||
MinifyStyles: true,
|
MinifyStyles: true,
|
||||||
@ -83,11 +86,11 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
func (h *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
conn, err := websocket.Upgrade(w, r, w.Header(), 1024, 1024)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
||||||
@ -102,7 +105,7 @@ func (h *Headless) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
go h.start(conn)
|
go h.start(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
|
func (h *Bridge) sendMessage(conn *websocket.Conn, msg string) {
|
||||||
|
|
||||||
h.lock.Lock()
|
h.lock.Lock()
|
||||||
defer h.lock.Unlock()
|
defer h.lock.Unlock()
|
||||||
@ -112,12 +115,12 @@ func (h *Headless) sendMessage(conn *websocket.Conn, msg string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Headless) start(conn *websocket.Conn) {
|
func (h *Bridge) 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/assets/wails.js")
|
||||||
h.evalJS(wailsRuntime, wailsRuntimeMessage)
|
h.evalJS(wailsRuntime, wailsRuntimeMessage)
|
||||||
|
|
||||||
// Inject bindings
|
// Inject bindings
|
||||||
@ -144,8 +147,8 @@ func (h *Headless) start(conn *websocket.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the app in headless mode!
|
// Run the app in Bridge mode!
|
||||||
func (h *Headless) Run() error {
|
func (h *Bridge) Run() error {
|
||||||
h.server = &http.Server{Addr: ":34115"}
|
h.server = &http.Server{Addr: ":34115"}
|
||||||
http.HandleFunc("/bridge", h.wsBridgeHandler)
|
http.HandleFunc("/bridge", h.wsBridgeHandler)
|
||||||
|
|
||||||
@ -160,45 +163,45 @@ func (h *Headless) Run() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBinding creates a new binding with the frontend
|
// NewBinding creates a new binding with the frontend
|
||||||
func (h *Headless) NewBinding(methodName string) error {
|
func (h *Bridge) NewBinding(methodName string) error {
|
||||||
h.bindingCache = append(h.bindingCache, methodName)
|
h.bindingCache = append(h.bindingCache, methodName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectFile is unsupported for Headless but required
|
// SelectFile is unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) SelectFile() string {
|
func (h *Bridge) SelectFile() string {
|
||||||
h.log.Warn("SelectFile() unsupported in bridge mode")
|
h.log.Warn("SelectFile() unsupported in bridge mode")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectDirectory is unsupported for Headless but required
|
// SelectDirectory is unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) SelectDirectory() string {
|
func (h *Bridge) SelectDirectory() string {
|
||||||
h.log.Warn("SelectDirectory() unsupported in bridge mode")
|
h.log.Warn("SelectDirectory() unsupported in bridge mode")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectSaveFile is unsupported for Headless but required
|
// SelectSaveFile is unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) SelectSaveFile() string {
|
func (h *Bridge) SelectSaveFile() string {
|
||||||
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
|
h.log.Warn("SelectSaveFile() unsupported in bridge mode")
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback sends a callback to the frontend
|
// Callback sends a callback to the frontend
|
||||||
func (h *Headless) Callback(data string) error {
|
func (h *Bridge) Callback(data string) error {
|
||||||
return h.evalJS(data, callbackMessage)
|
return h.evalJS(data, callbackMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotifyEvent notifies the frontend of an event
|
// NotifyEvent notifies the frontend of an event
|
||||||
func (h *Headless) NotifyEvent(event *eventData) error {
|
func (h *Bridge) 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,37 +218,37 @@ 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 Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) SetColour(colour string) error {
|
func (h *Bridge) SetColour(colour string) error {
|
||||||
h.log.WarnFields("SetColour ignored for headless more", Fields{"col": colour})
|
h.log.WarnFields("SetColour ignored for Bridge more", logger.Fields{"col": colour})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fullscreen is unsupported for Headless but required
|
// Fullscreen is unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) Fullscreen() {
|
func (h *Bridge) Fullscreen() {
|
||||||
h.log.Warn("Fullscreen() unsupported in bridge mode")
|
h.log.Warn("Fullscreen() unsupported in bridge mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnFullscreen is unsupported for Headless but required
|
// UnFullscreen is unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) UnFullscreen() {
|
func (h *Bridge) UnFullscreen() {
|
||||||
h.log.Warn("UnFullscreen() unsupported in bridge mode")
|
h.log.Warn("UnFullscreen() unsupported in bridge mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTitle is currently unsupported for Headless but required
|
// SetTitle is currently unsupported for Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) SetTitle(title string) {
|
func (h *Bridge) 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 Bridge but required
|
||||||
// for the Renderer interface
|
// for the Renderer interface
|
||||||
func (h *Headless) Close() {
|
func (h *Bridge) Close() {
|
||||||
h.log.Warn("Close() unsupported in bridge mode")
|
h.log.Warn("Close() unsupported in bridge mode")
|
||||||
}
|
}
|
10
lib/renderer/renderer-mewn.go
Normal file
10
lib/renderer/renderer-mewn.go
Normal file
File diff suppressed because one or more lines are too long
@ -1,53 +1,62 @@
|
|||||||
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/webview"
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/lib/messages"
|
||||||
|
wv "github.com/wailsapp/wails/lib/renderer/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 wv.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 = wv.NewWebview(wv.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(_ wv.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("Running...")
|
||||||
|
|
||||||
// Runtime assets
|
// Runtime assets
|
||||||
wailsRuntime := mewn.String("./wailsruntimeassets/default/wails.min.js")
|
wailsRuntime := mewn.String("../../runtime/assets/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
|
||||||
@ -230,7 +242,7 @@ func (w *webViewRenderer) SelectFile() string {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
w.window.Dispatch(func() {
|
w.window.Dispatch(func() {
|
||||||
result = w.window.Dialog(webview.DialogTypeOpen, 0, "Select File", "")
|
result = w.window.Dialog(wv.DialogTypeOpen, 0, "Select File", "")
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
@ -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
|
||||||
@ -247,7 +260,7 @@ func (w *webViewRenderer) SelectDirectory() string {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
w.window.Dispatch(func() {
|
w.window.Dispatch(func() {
|
||||||
result = w.window.Dialog(webview.DialogTypeOpen, webview.DialogFlagDirectory, "Select Directory", "")
|
result = w.window.Dialog(wv.DialogTypeOpen, wv.DialogFlagDirectory, "Select Directory", "")
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
@ -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
|
||||||
@ -264,7 +278,7 @@ func (w *webViewRenderer) SelectSaveFile() string {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
w.window.Dispatch(func() {
|
w.window.Dispatch(func() {
|
||||||
result = w.window.Dialog(webview.DialogTypeSave, 0, "Save file", "")
|
result = w.window.Dialog(wv.DialogTypeSave, 0, "Save file", "")
|
||||||
wg.Done()
|
wg.Done()
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
@ -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()
|
||||||
})
|
})
|
21
lib/renderer/webview/LICENSE
Normal file
21
lib/renderer/webview/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Serge Zaitsev
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
372
lib/renderer/webview/webview.go
Executable file
372
lib/renderer/webview/webview.go
Executable file
@ -0,0 +1,372 @@
|
|||||||
|
// Package wails implements Go bindings to https://github.com/zserge/webview C library.
|
||||||
|
// It is a modified version of webview.go from that repository
|
||||||
|
|
||||||
|
// Bindings closely repeat the C APIs and include both, a simplified
|
||||||
|
// single-function API to just open a full-screen webview window, and a more
|
||||||
|
// advanced and featureful set of APIs, including Go-to-JavaScript bindings.
|
||||||
|
//
|
||||||
|
// The library uses gtk-webkit, Cocoa/Webkit and MSHTML (IE8..11) as a browser
|
||||||
|
// engine and supports Linux, MacOS and Windows 7..10 respectively.
|
||||||
|
//
|
||||||
|
package webview
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux openbsd freebsd CFLAGS: -DWEBVIEW_GTK=1
|
||||||
|
#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
|
||||||
|
#cgo windows CFLAGS: -DWEBVIEW_WINAPI=1
|
||||||
|
#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
|
||||||
|
|
||||||
|
#cgo darwin CFLAGS: -DWEBVIEW_COCOA=1 -x objective-c
|
||||||
|
#cgo darwin LDFLAGS: -framework Cocoa -framework WebKit
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#define WEBVIEW_STATIC
|
||||||
|
#define WEBVIEW_IMPLEMENTATION
|
||||||
|
#include "webview.h"
|
||||||
|
|
||||||
|
extern void _webviewExternalInvokeCallback(void *, void *);
|
||||||
|
|
||||||
|
static inline void CgoWebViewFree(void *w) {
|
||||||
|
free((void *)((struct webview *)w)->title);
|
||||||
|
free((void *)((struct webview *)w)->url);
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *CgoWebViewCreate(int width, int height, char *title, char *url, int resizable, int debug) {
|
||||||
|
struct webview *w = (struct webview *) calloc(1, sizeof(*w));
|
||||||
|
w->width = width;
|
||||||
|
w->height = height;
|
||||||
|
w->title = title;
|
||||||
|
w->url = url;
|
||||||
|
w->resizable = resizable;
|
||||||
|
w->debug = debug;
|
||||||
|
w->external_invoke_cb = (webview_external_invoke_cb_t) _webviewExternalInvokeCallback;
|
||||||
|
if (webview_init(w) != 0) {
|
||||||
|
CgoWebViewFree(w);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewLoop(void *w, int blocking) {
|
||||||
|
return webview_loop((struct webview *)w, blocking);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewTerminate(void *w) {
|
||||||
|
webview_terminate((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewExit(void *w) {
|
||||||
|
webview_exit((struct webview *)w);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetTitle(void *w, char *title) {
|
||||||
|
webview_set_title((struct webview *)w, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetFullscreen(void *w, int fullscreen) {
|
||||||
|
webview_set_fullscreen((struct webview *)w, fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewSetColor(void *w, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||||
|
webview_set_color((struct webview *)w, r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoDialog(void *w, int dlgtype, int flags,
|
||||||
|
char *title, char *arg, char *res, size_t ressz) {
|
||||||
|
webview_dialog(w, dlgtype, flags,
|
||||||
|
(const char*)title, (const char*) arg, res, ressz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int CgoWebViewEval(void *w, char *js) {
|
||||||
|
return webview_eval((struct webview *)w, js);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CgoWebViewInjectCSS(void *w, char *css) {
|
||||||
|
webview_inject_css((struct webview *)w, css);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void _webviewDispatchGoCallback(void *);
|
||||||
|
static inline void _webview_dispatch_cb(struct webview *w, void *arg) {
|
||||||
|
_webviewDispatchGoCallback(arg);
|
||||||
|
}
|
||||||
|
static inline void CgoWebViewDispatch(void *w, uintptr_t arg) {
|
||||||
|
webview_dispatch((struct webview *)w, _webview_dispatch_cb, (void *)arg);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Ensure that main.main is called from the main thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open is a simplified API to open a single native window with a full-size webview in
|
||||||
|
// it. It can be helpful if you want to communicate with the core app using XHR
|
||||||
|
// or WebSockets (as opposed to using JavaScript bindings).
|
||||||
|
//
|
||||||
|
// Window appearance can be customized using title, width, height and resizable parameters.
|
||||||
|
// URL must be provided and can user either a http or https protocol, or be a
|
||||||
|
// local file:// URL. On some platforms "data:" URLs are also supported
|
||||||
|
// (Linux/MacOS).
|
||||||
|
func Open(title, url string, w, h int, resizable bool) error {
|
||||||
|
titleStr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titleStr))
|
||||||
|
urlStr := C.CString(url)
|
||||||
|
defer C.free(unsafe.Pointer(urlStr))
|
||||||
|
resize := C.int(0)
|
||||||
|
if resizable {
|
||||||
|
resize = C.int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := C.webview(titleStr, urlStr, C.int(w), C.int(h), resize)
|
||||||
|
if r != 0 {
|
||||||
|
return errors.New("failed to create webview")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalInvokeCallbackFunc is a function type that is called every time
|
||||||
|
// "window.external.invoke()" is called from JavaScript. Data is the only
|
||||||
|
// obligatory string parameter passed into the "invoke(data)" function from
|
||||||
|
// JavaScript. To pass more complex data serialized JSON or base64 encoded
|
||||||
|
// string can be used.
|
||||||
|
type ExternalInvokeCallbackFunc func(w WebView, data string)
|
||||||
|
|
||||||
|
// Settings is a set of parameters to customize the initial WebView appearance
|
||||||
|
// and behavior. It is passed into the webview.New() constructor.
|
||||||
|
type Settings struct {
|
||||||
|
// WebView main window title
|
||||||
|
Title string
|
||||||
|
// URL to open in a webview
|
||||||
|
URL string
|
||||||
|
// Window width in pixels
|
||||||
|
Width int
|
||||||
|
// Window height in pixels
|
||||||
|
Height int
|
||||||
|
// Allows/disallows window resizing
|
||||||
|
Resizable bool
|
||||||
|
// Enable debugging tools (Linux/BSD/MacOS, on Windows use Firebug)
|
||||||
|
Debug bool
|
||||||
|
// A callback that is executed when JavaScript calls "window.external.invoke()"
|
||||||
|
ExternalInvokeCallback ExternalInvokeCallbackFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebView is an interface that wraps the basic methods for controlling the UI
|
||||||
|
// loop, handling multithreading and providing JavaScript bindings.
|
||||||
|
type WebView interface {
|
||||||
|
// Run() starts the main UI loop until the user closes the webview window or
|
||||||
|
// Terminate() is called.
|
||||||
|
Run()
|
||||||
|
// Loop() runs a single iteration of the main UI.
|
||||||
|
Loop(blocking bool) bool
|
||||||
|
// SetTitle() changes window title. This method must be called from the main
|
||||||
|
// thread only. See Dispatch() for more details.
|
||||||
|
SetTitle(title string)
|
||||||
|
// SetFullscreen() controls window full-screen mode. This method must be
|
||||||
|
// called from the main thread only. See Dispatch() for more details.
|
||||||
|
SetFullscreen(fullscreen bool)
|
||||||
|
// SetColor() changes window background color. This method must be called from
|
||||||
|
// the main thread only. See Dispatch() for more details.
|
||||||
|
SetColor(r, g, b, a uint8)
|
||||||
|
// Eval() evaluates an arbitrary JS code inside the webview. This method must
|
||||||
|
// be called from the main thread only. See Dispatch() for more details.
|
||||||
|
Eval(js string) error
|
||||||
|
// InjectJS() injects an arbitrary block of CSS code using the JS API. This
|
||||||
|
// method must be called from the main thread only. See Dispatch() for more
|
||||||
|
// details.
|
||||||
|
InjectCSS(css string)
|
||||||
|
// Dialog() opens a system dialog of the given type and title. String
|
||||||
|
// argument can be provided for certain dialogs, such as alert boxes. For
|
||||||
|
// alert boxes argument is a message inside the dialog box.
|
||||||
|
Dialog(dlgType DialogType, flags int, title string, arg string) string
|
||||||
|
// Terminate() breaks the main UI loop. This method must be called from the main thread
|
||||||
|
// only. See Dispatch() for more details.
|
||||||
|
Terminate()
|
||||||
|
// Dispatch() schedules some arbitrary function to be executed on the main UI
|
||||||
|
// thread. This may be helpful if you want to run some JavaScript from
|
||||||
|
// background threads/goroutines, or to terminate the app.
|
||||||
|
Dispatch(func())
|
||||||
|
// Exit() closes the window and cleans up the resources. Use Terminate() to
|
||||||
|
// forcefully break out of the main UI loop.
|
||||||
|
Exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialogType is an enumeration of all supported system dialog types
|
||||||
|
type DialogType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogTypeOpen is a system file open dialog
|
||||||
|
DialogTypeOpen DialogType = iota
|
||||||
|
// DialogTypeSave is a system file save dialog
|
||||||
|
DialogTypeSave
|
||||||
|
// DialogTypeAlert is a system alert dialog (message box)
|
||||||
|
DialogTypeAlert
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DialogFlagFile is a normal file picker dialog
|
||||||
|
DialogFlagFile = C.WEBVIEW_DIALOG_FLAG_FILE
|
||||||
|
// DialogFlagDirectory is an open directory dialog
|
||||||
|
DialogFlagDirectory = C.WEBVIEW_DIALOG_FLAG_DIRECTORY
|
||||||
|
// DialogFlagInfo is an info alert dialog
|
||||||
|
DialogFlagInfo = C.WEBVIEW_DIALOG_FLAG_INFO
|
||||||
|
// DialogFlagWarning is a warning alert dialog
|
||||||
|
DialogFlagWarning = C.WEBVIEW_DIALOG_FLAG_WARNING
|
||||||
|
// DialogFlagError is an error dialog
|
||||||
|
DialogFlagError = C.WEBVIEW_DIALOG_FLAG_ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
m sync.Mutex
|
||||||
|
index uintptr
|
||||||
|
fns = map[uintptr]func(){}
|
||||||
|
cbs = map[WebView]ExternalInvokeCallbackFunc{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type webview struct {
|
||||||
|
w unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ WebView = &webview{}
|
||||||
|
|
||||||
|
func boolToInt(b bool) int {
|
||||||
|
if b {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWebview creates and opens a new webview window using the given settings. The
|
||||||
|
// returned object implements the WebView interface. This function returns nil
|
||||||
|
// if a window can not be created.
|
||||||
|
func NewWebview(settings Settings) WebView {
|
||||||
|
if settings.Width == 0 {
|
||||||
|
settings.Width = 640
|
||||||
|
}
|
||||||
|
if settings.Height == 0 {
|
||||||
|
settings.Height = 480
|
||||||
|
}
|
||||||
|
if settings.Title == "" {
|
||||||
|
settings.Title = "WebView"
|
||||||
|
}
|
||||||
|
w := &webview{}
|
||||||
|
w.w = C.CgoWebViewCreate(C.int(settings.Width), C.int(settings.Height),
|
||||||
|
C.CString(settings.Title), C.CString(settings.URL),
|
||||||
|
C.int(boolToInt(settings.Resizable)), C.int(boolToInt(settings.Debug)))
|
||||||
|
m.Lock()
|
||||||
|
if settings.ExternalInvokeCallback != nil {
|
||||||
|
cbs[w] = settings.ExternalInvokeCallback
|
||||||
|
} else {
|
||||||
|
cbs[w] = func(w WebView, data string) {}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Loop(blocking bool) bool {
|
||||||
|
block := C.int(0)
|
||||||
|
if blocking {
|
||||||
|
block = 1
|
||||||
|
}
|
||||||
|
return C.CgoWebViewLoop(w.w, block) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Run() {
|
||||||
|
for w.Loop(true) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Exit() {
|
||||||
|
C.CgoWebViewExit(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dispatch(f func()) {
|
||||||
|
m.Lock()
|
||||||
|
for ; fns[index] != nil; index++ {
|
||||||
|
}
|
||||||
|
fns[index] = f
|
||||||
|
m.Unlock()
|
||||||
|
C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetTitle(title string) {
|
||||||
|
p := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewSetTitle(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetColor(r, g, b, a uint8) {
|
||||||
|
C.CgoWebViewSetColor(w.w, C.uint8_t(r), C.uint8_t(g), C.uint8_t(b), C.uint8_t(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) SetFullscreen(fullscreen bool) {
|
||||||
|
C.CgoWebViewSetFullscreen(w.w, C.int(boolToInt(fullscreen)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Dialog(dlgType DialogType, flags int, title string, arg string) string {
|
||||||
|
const maxPath = 4096
|
||||||
|
titlePtr := C.CString(title)
|
||||||
|
defer C.free(unsafe.Pointer(titlePtr))
|
||||||
|
argPtr := C.CString(arg)
|
||||||
|
defer C.free(unsafe.Pointer(argPtr))
|
||||||
|
resultPtr := (*C.char)(C.calloc((C.size_t)(unsafe.Sizeof((*C.char)(nil))), (C.size_t)(maxPath)))
|
||||||
|
defer C.free(unsafe.Pointer(resultPtr))
|
||||||
|
C.CgoDialog(w.w, C.int(dlgType), C.int(flags), titlePtr,
|
||||||
|
argPtr, resultPtr, C.size_t(maxPath))
|
||||||
|
return C.GoString(resultPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Eval(js string) error {
|
||||||
|
p := C.CString(js)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
switch C.CgoWebViewEval(w.w, p) {
|
||||||
|
case -1:
|
||||||
|
return errors.New("evaluation failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) InjectCSS(css string) {
|
||||||
|
p := C.CString(css)
|
||||||
|
defer C.free(unsafe.Pointer(p))
|
||||||
|
C.CgoWebViewInjectCSS(w.w, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *webview) Terminate() {
|
||||||
|
C.CgoWebViewTerminate(w.w)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewDispatchGoCallback
|
||||||
|
func _webviewDispatchGoCallback(index unsafe.Pointer) {
|
||||||
|
var f func()
|
||||||
|
m.Lock()
|
||||||
|
f = fns[uintptr(index)]
|
||||||
|
delete(fns, uintptr(index))
|
||||||
|
m.Unlock()
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
//export _webviewExternalInvokeCallback
|
||||||
|
func _webviewExternalInvokeCallback(w unsafe.Pointer, data unsafe.Pointer) {
|
||||||
|
m.Lock()
|
||||||
|
var (
|
||||||
|
cb ExternalInvokeCallbackFunc
|
||||||
|
wv WebView
|
||||||
|
)
|
||||||
|
for wv, cb = range cbs {
|
||||||
|
if wv.(*webview).w == w {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
cb(wv, C.GoString((*C.char)(data)))
|
||||||
|
}
|
2244
lib/renderer/webview/webview.h
Normal file
2244
lib/renderer/webview/webview.h
Normal file
File diff suppressed because it is too large
Load Diff
42
log.go
42
log.go
@ -1,42 +0,0 @@
|
|||||||
package wails
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Global logger reference
|
|
||||||
var logger = log.New()
|
|
||||||
|
|
||||||
// Fields is used by the customLogger object to output
|
|
||||||
// fields along with a message
|
|
||||||
type Fields map[string]interface{}
|
|
||||||
|
|
||||||
// Default options for the global logger
|
|
||||||
func init() {
|
|
||||||
logger.SetOutput(os.Stdout)
|
|
||||||
logger.SetLevel(log.DebugLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the log level to the given level
|
|
||||||
func setLogLevel(level string) {
|
|
||||||
switch strings.ToLower(level) {
|
|
||||||
case "info":
|
|
||||||
logger.SetLevel(log.InfoLevel)
|
|
||||||
case "debug":
|
|
||||||
logger.SetLevel(log.DebugLevel)
|
|
||||||
case "warn":
|
|
||||||
logger.SetLevel(log.WarnLevel)
|
|
||||||
case "error":
|
|
||||||
logger.SetLevel(log.ErrorLevel)
|
|
||||||
case "fatal":
|
|
||||||
logger.SetLevel(log.FatalLevel)
|
|
||||||
case "panic":
|
|
||||||
logger.SetLevel(log.PanicLevel)
|
|
||||||
default:
|
|
||||||
logger.SetLevel(log.DebugLevel)
|
|
||||||
logger.Warnf("Log level '%s' not recognised. Setting to Debug.", level)
|
|
||||||
}
|
|
||||||
}
|
|
36
runtime.go
36
runtime.go
@ -1,22 +1,32 @@
|
|||||||
package wails
|
package wails
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
"github.com/wailsapp/wails/lib/logger"
|
||||||
|
"github.com/wailsapp/wails/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomLogger type alias
|
||||||
|
type CustomLogger = logger.CustomLogger
|
||||||
|
|
||||||
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
|
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
|
||||||
type Runtime struct {
|
type Runtime struct {
|
||||||
Events *RuntimeEvents
|
Events *runtime.Events
|
||||||
Log *RuntimeLog
|
Log *runtime.Log
|
||||||
Dialog *RuntimeDialog
|
Dialog *runtime.Dialog
|
||||||
Window *RuntimeWindow
|
Window *runtime.Window
|
||||||
Browser *RuntimeBrowser
|
Browser *runtime.Browser
|
||||||
FileSystem *RuntimeFileSystem
|
FileSystem *runtime.FileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuntime(eventManager *eventManager, renderer Renderer) *Runtime {
|
// NewRuntime creates a new Runtime struct
|
||||||
|
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
|
||||||
return &Runtime{
|
return &Runtime{
|
||||||
Events: newRuntimeEvents(eventManager),
|
Events: runtime.NewEvents(eventManager),
|
||||||
Log: newRuntimeLog(),
|
Log: runtime.NewLog(),
|
||||||
Dialog: newRuntimeDialog(renderer),
|
Dialog: runtime.NewDialog(renderer),
|
||||||
Window: newRuntimeWindow(renderer),
|
Window: runtime.NewWindow(renderer),
|
||||||
Browser: newRuntimeBrowser(),
|
Browser: runtime.NewBrowser(),
|
||||||
FileSystem: newRuntimeFileSystem(),
|
FileSystem: runtime.NewFileSystem(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,15 @@
|
|||||||
/*
|
/*
|
||||||
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).
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
Usage:
|
(c) Lea Anthony 2019-present
|
||||||
```
|
|
||||||
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.
|
|
||||||
*/
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
function init() {
|
||||||
// Bridge object
|
// Bridge object
|
||||||
window.wailsbridge = {
|
window.wailsbridge = {
|
||||||
reconnectOverlay: null,
|
reconnectOverlay: null,
|
||||||
@ -38,6 +32,7 @@ window.wailsbridge = {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Adapted from webview - thanks zserge!
|
// Adapted from webview - thanks zserge!
|
||||||
function injectCSS(css) {
|
function injectCSS(css) {
|
||||||
@ -184,12 +179,12 @@ function startBridge() {
|
|||||||
case 'b':
|
case 'b':
|
||||||
var binding = message.data.slice(1);
|
var binding = message.data.slice(1);
|
||||||
//log("Binding: " + binding)
|
//log("Binding: " + binding)
|
||||||
window.wails._.newBinding(binding);
|
window.wails._.NewBinding(binding);
|
||||||
break;
|
break;
|
||||||
// Call back
|
// Call back
|
||||||
case 'c':
|
case 'c':
|
||||||
var callbackData = message.data.slice(1);
|
var callbackData = message.data.slice(1);
|
||||||
window.wails._.callback(callbackData);
|
window.wails._.Callback(callbackData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
|
window.wails.Log.Error('Unknown message type received: ' + message.data[0]);
|
||||||
@ -203,14 +198,20 @@ function startBridge() {
|
|||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
function start(callback) {
|
||||||
// The main function
|
|
||||||
// Passes the main Wails object to the callback if given.
|
// Set up the bridge
|
||||||
Start: function (callback) {
|
init();
|
||||||
|
|
||||||
// Save the callback
|
// Save the callback
|
||||||
window.wailsbridge.callback = callback;
|
window.wailsbridge.callback = callback;
|
||||||
|
|
||||||
// Start Bridge
|
// Start Bridge
|
||||||
startBridge();
|
startBridge();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
function Init(callback) {
|
||||||
|
start(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Init;
|
18
runtime/assets/default.html
Normal file
18
runtime/assets/default.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="text/javascript">function AddScript(js, callbackID) {
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.text = js;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
1
runtime/assets/wails.js
Normal file
1
runtime/assets/wails.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
!function(n){var t={};function e(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return n[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}e.m=n,e.c=t,e.d=function(n,t,r){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:r})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var o in n)e.d(r,o,function(t){return n[t]}.bind(null,o));return r},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="",e(e.s=0)}([function(n,t,e){"use strict";e.r(t);var r={};e.r(r),e.d(r,"Debug",function(){return c}),e.d(r,"Info",function(){return u}),e.d(r,"Warning",function(){return l}),e.d(r,"Error",function(){return f}),e.d(r,"Fatal",function(){return d});var o={};function i(n,t,e){var r={type:n,callbackID:e,payload:t};!function(n){window.external.invoke(n)}(JSON.stringify(r))}function a(n,t){i("log",{level:n,message:t})}function c(n){a("debug",n)}function u(n){a("info",n)}function l(n){a("warning",n)}function f(n){a("error",n)}function d(n){a("fatal",n)}e.r(o),e.d(o,"OpenURL",function(){return y}),e.d(o,"OpenFile",function(){return g});var s,p={};function v(n,t,e){return null!=e&&null!=e||(e=0),new Promise(function(r,o){var a;do{a=n+"-"+s()}while(p[a]);if(e>0)var c=setTimeout(function(){o(Error("Call to "+n+" timed out. Request ID: "+a))},e);p[a]={timeoutHandle:c,reject:o,resolve:r};try{i("call",{bindingName:n,data:JSON.stringify(t)},a)}catch(n){console.error(n)}})}function w(n,t){return v(".wails."+n,t)}function y(n){return w("Browser.OpenURL",n)}function g(n){return w("Browser.OpenFile",n)}s=window.crypto?function(){var n=new Uint32Array(1);return window.crypto.getRandomValues(n)[0]}:function(){return 9007199254740991*Math.random()};var m=function n(t,e){!function(n,t){if(!(n instanceof t))throw new TypeError("Cannot call a class as a function")}(this,n),e=e||-1,this.Callback=function(n){return t.apply(null,n),-1!==e&&0===(e-=1)}},b={};function h(n,t,e){b[n]=b[n]||[];var r=new m(t,e);b[n].push(r)}function O(n){i("event",{name:n,data:JSON.stringify([].slice.apply(arguments).slice(1))})}var S={};function j(n){try{return new Function("var "+n),!0}catch(n){return!1}}function k(){return(k=Object.assign||function(n){for(var t=1;t<arguments.length;t++){var e=arguments[t];for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r])}return n}).apply(this,arguments)}window.backend={},e.d(t,"Init",function(){return N}),window.wails=window.wails||{},window.backend={};var E={NewBinding:function(n){var t=[].concat(n.split(".").splice(1)),e=window.backend;if(t.length>1)for(var r=0;r<t.length-1;r+=1){var o=t[r];if(!j(o))return new Error("".concat(o," is not a valid javascript identifier."));e[o]||(e[o]={}),e=e[o]}var i=t.pop();if(!j(i))return new Error("".concat(i," is not a valid javascript identifier."));e[i]=function(){var t=0;function e(){var e=[].slice.call(arguments);return v(n,e,t)}return e.setTimeout=function(n){t=n},e.getTimeout=function(){return t},e}()},Callback:function(n){var t;n=decodeURIComponent(n.replace(/\s+/g,"").replace(/[0-9a-f]{2}/g,"%$&"));try{t=JSON.parse(n)}catch(t){var e="Invalid JSON passed to callback: ".concat(t.message,". Message: ").concat(n);throw c(e),new Error(e)}var r=t.callbackid,o=p[r];if(!o){var i="Callback '".concat(r,"' not registed!!!");throw console.error(i),new Error(i)}clearTimeout(o.timeoutHandle),delete p[r],t.error?o.reject(t.error):o.resolve(t.data)},Notify:function(n,t){if(b[n]){for(var e=b[n].slice(),r=0;r<b[n].length;r+=1){var o=b[n][r],i=[];if(t)try{i=JSON.parse(t)}catch(t){f("Invalid JSON data sent to notify. Event name = "+n)}o.Callback(i)&&e.splice(r,1)}b[n]=e}},AddScript:function(n,t){var e=document.createElement("script");e.text=n,document.body.appendChild(e),t&&O(t)},InjectCSS:function(n){var t=document.createElement("style");t.setAttribute("type","text/css"),t.styleSheet?t.styleSheet.cssText=n:t.appendChild(document.createTextNode(n)),(document.head||document.getElementsByTagName("head")[0]).appendChild(t)},Init:N},C={Log:r,Browser:o,Events:{On:function(n,t){h(n,t)},OnMultiple:h,Emit:O,Heartbeat:function(n,t,e){var r=null;S[n]=function(){clearInterval(r),e()},r=setInterval(function(){O(n)},t)},Acknowledge:function(n){if(!S[n])throw new f("Cannot acknowledge unknown heartbeat '".concat(n,"'"));S[n]()}},_:E};function N(n){n()}k(window.wails,C),O("wails:loaded")}]);
|
21
runtime/browser.go
Normal file
21
runtime/browser.go
Normal 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)
|
||||||
|
}
|
30
runtime/dialog.go
Normal file
30
runtime/dialog.go
Normal 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()
|
||||||
|
}
|
25
runtime/events.go
Normal file
25
runtime/events.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/lib/interfaces"
|
||||||
|
|
||||||
|
// Events exposes the events interface
|
||||||
|
type Events struct {
|
||||||
|
eventManager interfaces.EventManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEvents creates a new Events struct
|
||||||
|
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...)
|
||||||
|
}
|
16
runtime/filesystem.go
Normal file
16
runtime/filesystem.go
Normal 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 {}
|
||||||
|
|
||||||
|
// NewFileSystem 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()
|
||||||
|
}
|
31
runtime/js/.eslintrc
Normal file
31
runtime/js/.eslintrc
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es6": true,
|
||||||
|
"amd": true,
|
||||||
|
"node": true,
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2016,
|
||||||
|
"sourceType": "module",
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
"tab"
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
22
runtime/js/babel.config.js
Normal file
22
runtime/js/babel.config.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
module.exports = function (api) {
|
||||||
|
api.cache(true);
|
||||||
|
|
||||||
|
const presets = [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"useBuiltIns": "entry",
|
||||||
|
"corejs": {
|
||||||
|
"version": 3,
|
||||||
|
"proposals": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
presets,
|
||||||
|
};
|
||||||
|
}
|
94
runtime/js/core/bindings.js
Normal file
94
runtime/js/core/bindings.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { Call } from './calls';
|
||||||
|
|
||||||
|
window.backend = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the given identifier is valid Javascript
|
||||||
|
*
|
||||||
|
* @param {boolean} name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function isValidIdentifier(name) {
|
||||||
|
// Don't xss yourself :-)
|
||||||
|
try {
|
||||||
|
new Function('var ' + name);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NewBinding creates a new binding from the given binding name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} bindingName
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line max-lines-per-function
|
||||||
|
export function NewBinding(bindingName) {
|
||||||
|
|
||||||
|
// Get all the sections of the binding
|
||||||
|
var bindingSections = [].concat(bindingName.split('.').splice(1));
|
||||||
|
var pathToBinding = window.backend;
|
||||||
|
|
||||||
|
// Check if we have a path (IE Struct)
|
||||||
|
if (bindingSections.length > 1) {
|
||||||
|
// Iterate over binding sections, adding them to the window.backend object
|
||||||
|
for (let index = 0; index < bindingSections.length-1; index += 1) {
|
||||||
|
const name = bindingSections[index];
|
||||||
|
// Is name a valid javascript identifier?
|
||||||
|
if (!isValidIdentifier(name)) {
|
||||||
|
return new Error(`${name} is not a valid javascript identifier.`);
|
||||||
|
}
|
||||||
|
if (!pathToBinding[name]) {
|
||||||
|
pathToBinding[name] = {};
|
||||||
|
}
|
||||||
|
pathToBinding = pathToBinding[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the actual function/method call name
|
||||||
|
const name = bindingSections.pop();
|
||||||
|
|
||||||
|
// Is name a valid javascript identifier?
|
||||||
|
if (!isValidIdentifier(name)) {
|
||||||
|
return new Error(`${name} is not a valid javascript identifier.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add binding call
|
||||||
|
pathToBinding[name] = function () {
|
||||||
|
|
||||||
|
// No timeout by default
|
||||||
|
var timeout = 0;
|
||||||
|
|
||||||
|
// Actual function
|
||||||
|
function dynamic() {
|
||||||
|
var args = [].slice.call(arguments);
|
||||||
|
return Call(bindingName, args, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow setting timeout to function
|
||||||
|
dynamic.setTimeout = function (newTimeout) {
|
||||||
|
timeout = newTimeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow getting timeout to function
|
||||||
|
dynamic.getTimeout = function () {
|
||||||
|
return timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
return dynamic;
|
||||||
|
}();
|
||||||
|
}
|
34
runtime/js/core/browser.js
Normal file
34
runtime/js/core/browser.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { SystemCall } from './calls';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given URL in the system browser
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function OpenURL(url) {
|
||||||
|
return SystemCall('Browser.OpenURL', url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function OpenFile(filename) {
|
||||||
|
return SystemCall('Browser.OpenFile', filename);
|
||||||
|
}
|
158
runtime/js/core/calls.js
Normal file
158
runtime/js/core/calls.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { Debug } from './log';
|
||||||
|
import { SendMessage } from './ipc';
|
||||||
|
|
||||||
|
var callbacks = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a number from the native browser random function
|
||||||
|
*
|
||||||
|
* @returns number
|
||||||
|
*/
|
||||||
|
function cryptoRandom() {
|
||||||
|
var array = new Uint32Array(1);
|
||||||
|
return window.crypto.getRandomValues(array)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a number using da old-skool Math.Random
|
||||||
|
* I likes to call it LOLRandom
|
||||||
|
*
|
||||||
|
* @returns number
|
||||||
|
*/
|
||||||
|
function basicRandom() {
|
||||||
|
return Math.random() * 9007199254740991;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick a random number function based on browser capability
|
||||||
|
var randomFunc;
|
||||||
|
if (window.crypto) {
|
||||||
|
randomFunc = cryptoRandom;
|
||||||
|
} else {
|
||||||
|
randomFunc = basicRandom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call sends a message to the backend to call the binding with the
|
||||||
|
* given data. A promise is returned and will be completed when the
|
||||||
|
* backend responds. This will be resolved when the call was successful
|
||||||
|
* or rejected if an error is passed back.
|
||||||
|
* There is a timeout mechanism. If the call doesn't respond in the given
|
||||||
|
* time (in milliseconds) then the promise is rejected.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} bindingName
|
||||||
|
* @param {string} data
|
||||||
|
* @param {number=} timeout
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function Call(bindingName, data, timeout) {
|
||||||
|
|
||||||
|
// Timeout infinite by default
|
||||||
|
if (timeout == null || timeout == undefined) {
|
||||||
|
timeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a promise
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
||||||
|
// Create a unique callbackID
|
||||||
|
var callbackID;
|
||||||
|
do {
|
||||||
|
callbackID = bindingName + '-' + randomFunc();
|
||||||
|
} while (callbacks[callbackID]);
|
||||||
|
|
||||||
|
// Set timeout
|
||||||
|
if (timeout > 0) {
|
||||||
|
var timeoutHandle = setTimeout(function () {
|
||||||
|
reject(Error('Call to ' + bindingName + ' timed out. Request ID: ' + callbackID));
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store callback
|
||||||
|
callbacks[callbackID] = {
|
||||||
|
timeoutHandle: timeoutHandle,
|
||||||
|
reject: reject,
|
||||||
|
resolve: resolve
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
bindingName: bindingName,
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make the call
|
||||||
|
SendMessage('call', payload, callbackID);
|
||||||
|
} catch (e) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the backend to return data to a previously called
|
||||||
|
* binding invocation
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} incomingMessage
|
||||||
|
*/
|
||||||
|
export function Callback(incomingMessage) {
|
||||||
|
|
||||||
|
// Decode the message - Credit: https://stackoverflow.com/a/13865680
|
||||||
|
incomingMessage = decodeURIComponent(incomingMessage.replace(/\s+/g, '').replace(/[0-9a-f]{2}/g, '%$&'));
|
||||||
|
|
||||||
|
// Parse the message
|
||||||
|
var message;
|
||||||
|
try {
|
||||||
|
message = JSON.parse(incomingMessage);
|
||||||
|
} catch (e) {
|
||||||
|
const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`;
|
||||||
|
Debug(error);
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
var callbackID = message.callbackid;
|
||||||
|
var callbackData = callbacks[callbackID];
|
||||||
|
if (!callbackData) {
|
||||||
|
const error = `Callback '${callbackID}' not registed!!!`;
|
||||||
|
console.error(error); // eslint-disable-line
|
||||||
|
throw new Error(error);
|
||||||
|
}
|
||||||
|
clearTimeout(callbackData.timeoutHandle);
|
||||||
|
|
||||||
|
delete callbacks[callbackID];
|
||||||
|
|
||||||
|
if (message.error) {
|
||||||
|
callbackData.reject(message.error);
|
||||||
|
} else {
|
||||||
|
callbackData.resolve(message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SystemCall is used to call wails methods from the frontend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} method
|
||||||
|
* @param {any[]=} data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function SystemCall(method, data) {
|
||||||
|
return Call('.wails.' + method, data);
|
||||||
|
}
|
194
runtime/js/core/events.js
Normal file
194
runtime/js/core/events.js
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { Error } from './log';
|
||||||
|
import { SendMessage } from './ipc';
|
||||||
|
|
||||||
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
/**
|
||||||
|
* The Listener class defines a listener! :-)
|
||||||
|
*
|
||||||
|
* @class Listener
|
||||||
|
*/
|
||||||
|
class Listener {
|
||||||
|
/**
|
||||||
|
* Creates an instance of Listener.
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {number} maxCallbacks
|
||||||
|
* @memberof Listener
|
||||||
|
*/
|
||||||
|
constructor(callback, maxCallbacks) {
|
||||||
|
// Default of -1 means infinite
|
||||||
|
maxCallbacks = maxCallbacks || -1;
|
||||||
|
// Callback invokes the callback with the given data
|
||||||
|
// Returns true if this listener should be destroyed
|
||||||
|
this.Callback = (data) => {
|
||||||
|
callback.apply(null, data);
|
||||||
|
// If maxCallbacks is infinite, return false (do not destroy)
|
||||||
|
if (maxCallbacks === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Decrement maxCallbacks. Return true if now 0, otherwise false
|
||||||
|
maxCallbacks -= 1;
|
||||||
|
return maxCallbacks === 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventListeners = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {number} maxCallbacks
|
||||||
|
*/
|
||||||
|
export function OnMultiple(eventName, callback, maxCallbacks) {
|
||||||
|
eventListeners[eventName] = eventListeners[eventName] || [];
|
||||||
|
const thisListener = new Listener(callback, maxCallbacks);
|
||||||
|
eventListeners[eventName].push(thisListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked every time the event is emitted
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function On(eventName, callback) {
|
||||||
|
OnMultiple(eventName, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked once then destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function Once(eventName, callback) {
|
||||||
|
OnMultiple(eventName, callback, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify informs frontend listeners that an event was emitted with the given data
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {string} data
|
||||||
|
*/
|
||||||
|
export function Notify(eventName, data) {
|
||||||
|
|
||||||
|
// Check if we have any listeners for this event
|
||||||
|
if (eventListeners[eventName]) {
|
||||||
|
|
||||||
|
// Keep a list of listener indexes to destroy
|
||||||
|
const newEventListenerList = eventListeners[eventName].slice();
|
||||||
|
|
||||||
|
// Iterate listeners
|
||||||
|
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
||||||
|
|
||||||
|
// Get next listener
|
||||||
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
|
// Parse data if we have it
|
||||||
|
var parsedData = [];
|
||||||
|
if (data) {
|
||||||
|
try {
|
||||||
|
parsedData = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
Error('Invalid JSON data sent to notify. Event name = ' + eventName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do the callback
|
||||||
|
const destroy = listener.Callback(parsedData);
|
||||||
|
if (destroy) {
|
||||||
|
// if the listener indicated to destroy itself, add it to the destroy list
|
||||||
|
newEventListenerList.splice(count, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update callbacks with new list of listners
|
||||||
|
eventListeners[eventName] = newEventListenerList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with the given name and data
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
export function Emit(eventName) {
|
||||||
|
|
||||||
|
// Calculate the data
|
||||||
|
var data = JSON.stringify([].slice.apply(arguments).slice(1));
|
||||||
|
|
||||||
|
// Notify backend
|
||||||
|
const payload = {
|
||||||
|
name: eventName,
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
SendMessage('event', payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks for the heartbeat calls
|
||||||
|
const heartbeatCallbacks = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
|
|
||||||
|
// Declare interval variable
|
||||||
|
let interval = null;
|
||||||
|
|
||||||
|
// Setup callback
|
||||||
|
function dynamicCallback() {
|
||||||
|
// Kill interval
|
||||||
|
clearInterval(interval);
|
||||||
|
// Callback
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register callback
|
||||||
|
heartbeatCallbacks[eventName] = dynamicCallback;
|
||||||
|
|
||||||
|
// Start emitting the event
|
||||||
|
interval = setInterval(function () {
|
||||||
|
Emit(eventName);
|
||||||
|
}, timeInMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
export function Acknowledge(eventName) {
|
||||||
|
// If we are waiting for acknowledgement for this event type
|
||||||
|
if (heartbeatCallbacks[eventName]) {
|
||||||
|
// Acknowledge!
|
||||||
|
heartbeatCallbacks[eventName]();
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot acknowledge unknown heartbeat '${eventName}'`);
|
||||||
|
}
|
||||||
|
}
|
37
runtime/js/core/ipc.js
Normal file
37
runtime/js/core/ipc.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke sends the given message to the backend
|
||||||
|
*
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
function Invoke(message) {
|
||||||
|
window.external.invoke(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the backend based on the given type, payload and callbackID
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} type
|
||||||
|
* @param {string} payload
|
||||||
|
* @param {string=} callbackID
|
||||||
|
*/
|
||||||
|
export function SendMessage(type, payload, callbackID) {
|
||||||
|
const message = {
|
||||||
|
type,
|
||||||
|
callbackID,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
|
||||||
|
Invoke(JSON.stringify(message));
|
||||||
|
}
|
80
runtime/js/core/log.js
Normal file
80
runtime/js/core/log.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { SendMessage } from './ipc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a log message to the backend with the given level + message
|
||||||
|
*
|
||||||
|
* @param {string} level
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
function sendLogMessage(level, message) {
|
||||||
|
|
||||||
|
// Log Message
|
||||||
|
const payload = {
|
||||||
|
level: level,
|
||||||
|
message: message,
|
||||||
|
};
|
||||||
|
SendMessage('log', payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given debug message with the backend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
export function Debug(message) {
|
||||||
|
sendLogMessage('debug', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given info message with the backend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
export function Info(message) {
|
||||||
|
sendLogMessage('info', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given warning message with the backend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
export function Warning(message) {
|
||||||
|
sendLogMessage('warning', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given error message with the backend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
export function Error(message) {
|
||||||
|
sendLogMessage('error', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the given fatal message with the backend
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} message
|
||||||
|
*/
|
||||||
|
export function Fatal(message) {
|
||||||
|
sendLogMessage('fatal', message);
|
||||||
|
}
|
||||||
|
|
55
runtime/js/core/main.js
Normal file
55
runtime/js/core/main.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
import * as Log from './log';
|
||||||
|
import * as Browser from './browser';
|
||||||
|
import { On, OnMultiple, Emit, Notify, Heartbeat, Acknowledge } from './events';
|
||||||
|
import { NewBinding } from './bindings';
|
||||||
|
import { Callback } from './calls';
|
||||||
|
import { AddScript, InjectCSS } from './utils';
|
||||||
|
|
||||||
|
// Initialise global if not already
|
||||||
|
window.wails = window.wails || {};
|
||||||
|
window.backend = {};
|
||||||
|
|
||||||
|
// Setup internal calls
|
||||||
|
var internal = {
|
||||||
|
NewBinding,
|
||||||
|
Callback,
|
||||||
|
Notify,
|
||||||
|
AddScript,
|
||||||
|
InjectCSS,
|
||||||
|
Init,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Setup runtime structure
|
||||||
|
var runtime = {
|
||||||
|
Log,
|
||||||
|
Browser,
|
||||||
|
Events: {
|
||||||
|
On,
|
||||||
|
OnMultiple,
|
||||||
|
Emit,
|
||||||
|
Heartbeat,
|
||||||
|
Acknowledge,
|
||||||
|
},
|
||||||
|
_: internal,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Augment global
|
||||||
|
Object.assign(window.wails, runtime);
|
||||||
|
|
||||||
|
// Emit loaded event
|
||||||
|
Emit('wails:loaded');
|
||||||
|
|
||||||
|
// Nothing to init in production
|
||||||
|
export function Init(callback) {
|
||||||
|
callback();
|
||||||
|
}
|
34
runtime/js/core/utils.js
Normal file
34
runtime/js/core/utils.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import { Emit } from './events';
|
||||||
|
|
||||||
|
export function AddScript(js, callbackID) {
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.text = js;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
if (callbackID) {
|
||||||
|
Emit(callbackID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from webview - thanks zserge!
|
||||||
|
export 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);
|
||||||
|
}
|
7686
runtime/js/package-lock.json
generated
Normal file
7686
runtime/js/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
runtime/js/package.json
Normal file
43
runtime/js/package.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"name": "wails-runtime",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "The Javascript Wails Runtime",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "eslint core/ && npm run build:prod",
|
||||||
|
"build:prod": "webpack --env prod --colors",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/wailsapp/runtime.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"Wails",
|
||||||
|
"Go",
|
||||||
|
"Javascript",
|
||||||
|
"Runtime"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"> 5%",
|
||||||
|
"IE 9"
|
||||||
|
],
|
||||||
|
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/wailsapp/runtime/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/wailsapp/runtime#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.5.0",
|
||||||
|
"@babel/core": "^7.5.4",
|
||||||
|
"@babel/plugin-transform-object-assign": "^7.2.0",
|
||||||
|
"@babel/preset-env": "^7.5.4",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"babel-preset-minify": "^0.5.0",
|
||||||
|
"core-js": "^3.1.4",
|
||||||
|
"eslint": "^6.2.2",
|
||||||
|
"webpack": "^4.35.3",
|
||||||
|
"webpack-cli": "^3.3.5"
|
||||||
|
}
|
||||||
|
}
|
1
runtime/js/runtime/.npmignore
Normal file
1
runtime/js/runtime/.npmignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
bridge.js
|
3
runtime/js/runtime/README.md
Normal file
3
runtime/js/runtime/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Wails Runtime
|
||||||
|
|
||||||
|
This module is the Javascript runtime library for the [Wails](https://wails.app) framework. It is intended to be installed as part of a [Wails](https://wails.app) project, not a standalone module.
|
37
runtime/js/runtime/browser.js
Normal file
37
runtime/js/runtime/browser.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given URL in the system browser
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function OpenURL(url) {
|
||||||
|
return window.wails.Browser.OpenURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the given filename using the system's default file handler
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {sting} filename
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function OpenFile(filename) {
|
||||||
|
return window.wails.Browser.OpenFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OpenURL,
|
||||||
|
OpenFile
|
||||||
|
};
|
89
runtime/js/runtime/events.js
Normal file
89
runtime/js/runtime/events.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {number} maxCallbacks
|
||||||
|
*/
|
||||||
|
function OnMultiple(eventName, callback, maxCallbacks) {
|
||||||
|
window.wails.Events.OnMultiple(eventName, callback, maxCallbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked every time the event is emitted
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
function On(eventName, callback) {
|
||||||
|
OnMultiple(eventName, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked once then destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
function Once(eventName, callback) {
|
||||||
|
OnMultiple(eventName, callback, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event with the given name and data
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
function Emit(eventName) {
|
||||||
|
return window.wails.Events.Emit(eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat emits the event `eventName`, every `timeInMilliseconds` milliseconds until
|
||||||
|
* the event is acknowledged via `Event.Acknowledge`. Once this happens, `callback` is invoked ONCE
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {number} timeInMilliseconds
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
function Heartbeat(eventName, timeInMilliseconds, callback) {
|
||||||
|
window.wails.Events.Heartbeat(eventName, timeInMilliseconds, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledges a heartbeat event by name
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
*/
|
||||||
|
function Acknowledge(eventName) {
|
||||||
|
return window.wails.Events.Acknowledge(eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
OnMultiple,
|
||||||
|
On,
|
||||||
|
Once,
|
||||||
|
Emit,
|
||||||
|
Heartbeat,
|
||||||
|
Acknowledge
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user