mirror of
https://github.com/taigrr/wails.git
synced 2026-04-16 11:44:49 -07:00
Compare commits
3 Commits
bugfix/non
...
firebug
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d6fe609c | ||
|
|
93e7432ba9 | ||
|
|
cb933cc987 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -17,15 +17,8 @@ cmd/wails/wails
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
tmp
|
tmp
|
||||||
node_modules/
|
node_modules/
|
||||||
package.json.md5
|
|
||||||
v2/test/**/frontend/dist
|
|
||||||
v2/test/**/build/
|
|
||||||
v2/test/frameless/icon.png
|
|
||||||
v2/test/hidden/icon.png
|
|
||||||
v2/test/kitchensink/frontend/public/bundle.*
|
|
||||||
v2/pkg/parser/testproject/frontend/wails
|
|
||||||
v2/test/kitchensink/frontend/public
|
v2/test/kitchensink/frontend/public
|
||||||
|
v2/internal/ffenestri/runtime.c
|
||||||
|
v2/internal/runtime/assets/desktop.js
|
||||||
v2/test/kitchensink/build/darwin/desktop/kitchensink
|
v2/test/kitchensink/build/darwin/desktop/kitchensink
|
||||||
v2/test/kitchensink/frontend/package.json.md5
|
v2/test/kitchensink/frontend/package.json.md5
|
||||||
/v2/internal/ffenestri/windows/test/cmake-build-debug/
|
|
||||||
.idea/
|
|
||||||
|
|||||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,8 +1,4 @@
|
|||||||
{
|
{
|
||||||
"go.formatTool": "goimports",
|
"go.formatTool": "goimports",
|
||||||
"eslint.alwaysShowStatus": true,
|
"eslint.alwaysShowStatus": true
|
||||||
"files.associations": {
|
|
||||||
"__locale": "c",
|
|
||||||
"ios": "c"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,3 @@ Wails is what it is because of the time and effort given by these great people.
|
|||||||
* [Kyle](https://github.com/kmuchmore)
|
* [Kyle](https://github.com/kmuchmore)
|
||||||
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
* [Balakrishna Prasad Ganne](https://github.com/aayush420)
|
||||||
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
* [Charaf Rezrazi](https://github.com/Rezrazi)
|
||||||
* [misitebao](https://github.com/misitebao)
|
|
||||||
* [Elie Grenon](https://github.com/DrunkenPoney)
|
|
||||||
* [Amaury Tobias Quiroz](https://github.com/amaury-tobias)
|
|
||||||
@@ -147,12 +147,7 @@ This project was mainly coded to the following albums:
|
|||||||
|
|
||||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
|
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thank You
|
||||||
|
|
||||||
<p align="center" style="text-align: center">
|
|
||||||
A *huge* thanks to <a href="https://pace.dev"><img src="pace.jpeg"/> Pace</a> for sponsoring the project and helping the efforts to get Wails ported to Apple Silicon!<br/><br/>
|
|
||||||
If you are looking for a Project Management tool that's powerful but quick and easy to use, check them out!<br/><br/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center" style="text-align: center">
|
<p align="center" style="text-align: center">
|
||||||
A special thank you to JetBrains for donating licenses to us!<br/><br/>
|
A special thank you to JetBrains for donating licenses to us!<br/><br/>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -65,8 +65,6 @@ const (
|
|||||||
Solus
|
Solus
|
||||||
// Ctlos Linux distribution
|
// Ctlos Linux distribution
|
||||||
Ctlos
|
Ctlos
|
||||||
// EndeavourOS linux distribution
|
|
||||||
EndeavourOS
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DistroInfo contains all the information relating to a linux distribution
|
// DistroInfo contains all the information relating to a linux distribution
|
||||||
@@ -134,7 +132,7 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
|||||||
case "archlabs":
|
case "archlabs":
|
||||||
result.Distribution = ArchLabs
|
result.Distribution = ArchLabs
|
||||||
case "ctlos":
|
case "ctlos":
|
||||||
result.Distribution = Ctlos
|
result.Distribution = Ctlos
|
||||||
case "debian":
|
case "debian":
|
||||||
result.Distribution = Debian
|
result.Distribution = Debian
|
||||||
case "ubuntu":
|
case "ubuntu":
|
||||||
@@ -173,8 +171,6 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
|||||||
result.Distribution = PopOS
|
result.Distribution = PopOS
|
||||||
case "solus":
|
case "solus":
|
||||||
result.Distribution = Solus
|
result.Distribution = Solus
|
||||||
case "endeavouros":
|
|
||||||
result.Distribution = EndeavourOS
|
|
||||||
default:
|
default:
|
||||||
result.Distribution = Unknown
|
result.Distribution = Unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,16 +202,7 @@ distributions:
|
|||||||
name: Ctlos Linux
|
name: Ctlos Linux
|
||||||
gccversioncommand: *gccdumpversion
|
gccversioncommand: *gccdumpversion
|
||||||
programs: *archdefaultprograms
|
programs: *archdefaultprograms
|
||||||
libraries: *archdefaultlibraries
|
libraries: *archdefaultlibraries
|
||||||
endeavouros:
|
|
||||||
id: endeavouros
|
|
||||||
releases:
|
|
||||||
default:
|
|
||||||
version: default
|
|
||||||
name: EndeavourOS
|
|
||||||
gccversioncommand: *gccdumpversion
|
|
||||||
programs: *archdefaultprograms
|
|
||||||
libraries: *archdefaultlibraries
|
|
||||||
manjaro:
|
manjaro:
|
||||||
id: manjaro
|
id: manjaro
|
||||||
releases:
|
releases:
|
||||||
|
|||||||
@@ -24,19 +24,11 @@ func NewSemanticVersion(version string) (*SemanticVersion, error) {
|
|||||||
|
|
||||||
// IsRelease returns true if it's a release version
|
// IsRelease returns true if it's a release version
|
||||||
func (s *SemanticVersion) IsRelease() bool {
|
func (s *SemanticVersion) IsRelease() bool {
|
||||||
// Limit to v1
|
|
||||||
if s.Version.Major() != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPreRelease returns true if it's a prerelease version
|
// IsPreRelease returns true if it's a prerelease version
|
||||||
func (s *SemanticVersion) IsPreRelease() bool {
|
func (s *SemanticVersion) IsPreRelease() bool {
|
||||||
// Limit to v1
|
|
||||||
if s.Version.Major() != 1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(s.Version.Prerelease()) > 0
|
return len(s.Version.Prerelease()) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSemanticVersion_IsPreRelease(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"v1.6.7-pre0", "v1.6.7-pre0", true},
|
|
||||||
{"v2.6.7+pre0", "v2.6.7+pre0", false},
|
|
||||||
{"v2.6.7", "v2.6.7", false},
|
|
||||||
{"v2.0.0+alpha.1", "v2.0.0+alpha.1", false},
|
|
||||||
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
|
|
||||||
{"v1.6.7", "v1.6.7", false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
semanticversion, err := NewSemanticVersion(tt.version)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Invalid semantic version: %s", semanticversion)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := &SemanticVersion{
|
|
||||||
Version: semanticversion.Version,
|
|
||||||
}
|
|
||||||
if got := s.IsPreRelease(); got != tt.want {
|
|
||||||
t.Errorf("IsPreRelease() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSemanticVersion_IsRelease(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"v1.6.7", "v1.6.7", true},
|
|
||||||
{"v2.6.7-pre0", "v2.6.7-pre0", false},
|
|
||||||
{"v2.6.7", "v2.6.7", false},
|
|
||||||
{"v2.6.7+release", "v2.6.7+release", false},
|
|
||||||
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
|
|
||||||
{"v1.6.7-pre0", "v1.6.7-pre0", false},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
semanticversion, err := NewSemanticVersion(tt.version)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Invalid semantic version: %s", semanticversion)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := &SemanticVersion{
|
|
||||||
Version: semanticversion.Version,
|
|
||||||
}
|
|
||||||
if got := s.IsRelease(); got != tt.want {
|
|
||||||
t.Errorf("IsRelease() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -276,7 +276,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
|||||||
switch distroInfo.Distribution {
|
switch distroInfo.Distribution {
|
||||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||||
libraryChecker = DpkgInstalled
|
libraryChecker = DpkgInstalled
|
||||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS:
|
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM:
|
||||||
libraryChecker = PacmanInstalled
|
libraryChecker = PacmanInstalled
|
||||||
case CentOS, Fedora, Tumbleweed, Leap:
|
case CentOS, Fedora, Tumbleweed, Leap:
|
||||||
libraryChecker = RpmInstalled
|
libraryChecker = RpmInstalled
|
||||||
|
|||||||
@@ -19,16 +19,16 @@
|
|||||||
"@types/mocha": "^8.0.3",
|
"@types/mocha": "^8.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.3.0",
|
"@typescript-eslint/eslint-plugin": "^4.3.0",
|
||||||
"@typescript-eslint/parser": "^4.3.0",
|
"@typescript-eslint/parser": "^4.3.0",
|
||||||
"@vue/cli-plugin-eslint": "~4.5.9",
|
"@vue/cli-plugin-eslint": "~4.5.6",
|
||||||
"@vue/cli-plugin-router": "~4.5.9",
|
"@vue/cli-plugin-router": "~4.5.6",
|
||||||
"@vue/cli-plugin-typescript": "~4.5.9",
|
"@vue/cli-plugin-typescript": "~4.5.6",
|
||||||
"@vue/cli-plugin-unit-mocha": "~4.5.9",
|
"@vue/cli-plugin-unit-mocha": "~4.5.6",
|
||||||
"@vue/cli-service": "~4.5.9",
|
"@vue/cli-service": "~4.5.6",
|
||||||
"@vue/compiler-sfc": "^3.0.0",
|
"@vue/compiler-sfc": "^3.0.0",
|
||||||
"@vue/eslint-config-typescript": "^7.0.0",
|
"@vue/eslint-config-typescript": "^5.1.0",
|
||||||
"@vue/test-utils": "^2.0.0-0",
|
"@vue/test-utils": "^2.0.0-0",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "<7.0.0",
|
"eslint": "^7.10.0",
|
||||||
"eslint-plugin-vue": "^7.0.0",
|
"eslint-plugin-vue": "^7.0.0",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"sass-loader": "^10.0.2",
|
"sass-loader": "^10.0.2",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
// Version - Wails version
|
// Version - Wails version
|
||||||
const Version = "v1.11.0"
|
const Version = "v1.8.1-pre6"
|
||||||
|
|||||||
@@ -83,9 +83,6 @@ func init() {
|
|||||||
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
|
return fmt.Errorf("Unable to find 'project.json'. Please check you are in a Wails project directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set firebug flag
|
|
||||||
projectOptions.UseFirebug = usefirebug
|
|
||||||
|
|
||||||
// Check that this platform is supported
|
// Check that this platform is supported
|
||||||
if !projectOptions.PlatformSupported() {
|
if !projectOptions.PlatformSupported() {
|
||||||
logger.Yellow("WARNING: This project is unsupported on %s - it probably won't work!\n Valid platforms: %s\n", runtime.GOOS, strings.Join(projectOptions.Platforms, ", "))
|
logger.Yellow("WARNING: This project is unsupported on %s - it probably won't work!\n Valid platforms: %s\n", runtime.GOOS, strings.Join(projectOptions.Platforms, ", "))
|
||||||
|
|||||||
70
config.go
70
config.go
@@ -1,37 +1,20 @@
|
|||||||
package wails
|
package wails
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
"github.com/leaanthony/mewn"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/runtime"
|
"github.com/wailsapp/wails/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppConfig is the configuration structure used when creating a Wails App object
|
// AppConfig is the configuration structure used when creating a Wails App object
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
// The width and height of your application in pixels
|
Width, Height int
|
||||||
Width, Height int
|
Title string
|
||||||
|
defaultHTML string
|
||||||
// The title to put in the title bar
|
HTML string
|
||||||
Title string
|
JS string
|
||||||
|
CSS string
|
||||||
// The HTML your app should use. If you leave it blank, a default will be used:
|
Colour string
|
||||||
// <!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="IE=edge" content="IE=edge"></head><body><div id="app"></div><script type="text/javascript"></script></body></html>
|
Resizable bool
|
||||||
HTML string
|
|
||||||
|
|
||||||
// The Javascript your app should use. Normally this should be generated by a bundler.
|
|
||||||
JS string
|
|
||||||
|
|
||||||
// The CSS your app should use. Normally this should be generated by a bundler.
|
|
||||||
CSS string
|
|
||||||
|
|
||||||
// The colour of your window. Can take "#fff", "rgb(255,255,255)", "rgba(255,255,255,1)" formats
|
|
||||||
Colour string
|
|
||||||
|
|
||||||
// Indicates whether your app should be resizable
|
|
||||||
Resizable bool
|
|
||||||
|
|
||||||
// Indicated if the devtools should be disabled
|
|
||||||
DisableInspector bool
|
DisableInspector bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,14 +33,9 @@ func (a *AppConfig) GetTitle() string {
|
|||||||
return a.Title
|
return a.Title
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHTML returns the default HTML
|
// GetDefaultHTML returns the default HTML
|
||||||
func (a *AppConfig) GetHTML() string {
|
func (a *AppConfig) GetDefaultHTML() string {
|
||||||
if len(a.HTML) > 0 {
|
return a.defaultHTML
|
||||||
a.HTML = url.QueryEscape(a.HTML)
|
|
||||||
a.HTML = "data:text/html," + strings.ReplaceAll(a.HTML, "+", "%20")
|
|
||||||
a.HTML = strings.ReplaceAll(a.HTML, "%3D", "=")
|
|
||||||
}
|
|
||||||
return a.HTML
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetResizable returns true if the window should be resizable
|
// GetResizable returns true if the window should be resizable
|
||||||
@@ -97,18 +75,10 @@ func (a *AppConfig) merge(in *AppConfig) error {
|
|||||||
a.Colour = in.Colour
|
a.Colour = in.Colour
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.HTML != "" {
|
|
||||||
a.HTML = in.HTML
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.JS != "" {
|
if in.JS != "" {
|
||||||
a.JS = in.JS
|
a.JS = in.JS
|
||||||
}
|
}
|
||||||
|
|
||||||
if in.HTML != "" {
|
|
||||||
a.HTML = in.HTML
|
|
||||||
}
|
|
||||||
|
|
||||||
if in.Width != 0 {
|
if in.Width != 0 {
|
||||||
a.Width = in.Width
|
a.Width = in.Width
|
||||||
}
|
}
|
||||||
@@ -129,7 +99,7 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
|
|||||||
Resizable: true,
|
Resizable: true,
|
||||||
Title: "My Wails App",
|
Title: "My Wails App",
|
||||||
Colour: "#FFF", // White by default
|
Colour: "#FFF", // White by default
|
||||||
HTML: defaultHTML,
|
HTML: mewn.String("./runtime/assets/default.html"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if userConfig != nil {
|
if userConfig != nil {
|
||||||
@@ -141,17 +111,3 @@ func newConfig(userConfig *AppConfig) (*AppConfig, error) {
|
|||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultHTML = `<!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>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>`
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -22,7 +22,7 @@ require (
|
|||||||
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
|
||||||
golang.org/x/text v0.3.0
|
golang.org/x/text v0.3.0
|
||||||
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
|
gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -81,8 +81,6 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
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-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
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=
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ type AppConfig interface {
|
|||||||
GetHeight() int
|
GetHeight() int
|
||||||
GetTitle() string
|
GetTitle() string
|
||||||
GetResizable() bool
|
GetResizable() bool
|
||||||
GetHTML() string
|
GetDefaultHTML() string
|
||||||
GetDisableInspector() bool
|
GetDisableInspector() bool
|
||||||
GetColour() string
|
GetColour() string
|
||||||
GetCSS() string
|
GetCSS() string
|
||||||
GetJS() string
|
GetJS() string
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -58,7 +58,7 @@ func (w *WebView) Initialise(config interfaces.AppConfig, ipc interfaces.IPCMana
|
|||||||
Height: config.GetHeight(),
|
Height: config.GetHeight(),
|
||||||
Title: config.GetTitle(),
|
Title: config.GetTitle(),
|
||||||
Resizable: config.GetResizable(),
|
Resizable: config.GetResizable(),
|
||||||
URL: config.GetHTML(),
|
URL: config.GetDefaultHTML(),
|
||||||
Debug: !config.GetDisableInspector(),
|
Debug: !config.GetDisableInspector(),
|
||||||
ExternalInvokeCallback: func(_ wv.WebView, message string) {
|
ExternalInvokeCallback: func(_ wv.WebView, message string) {
|
||||||
w.ipc.Dispatch(message, w.callback)
|
w.ipc.Dispatch(message, w.callback)
|
||||||
|
|||||||
@@ -364,7 +364,6 @@ struct webview_priv
|
|||||||
webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
|
webkit_web_view_get_settings(WEBKIT_WEB_VIEW(w->priv.webview));
|
||||||
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
||||||
webkit_settings_set_enable_developer_extras(settings, true);
|
webkit_settings_set_enable_developer_extras(settings, true);
|
||||||
webkit_settings_set_hardware_acceleration_policy(settings, WEBKIT_HARDWARE_ACCELERATION_POLICY_ALWAYS);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
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>
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"amd": true,
|
"amd": true,
|
||||||
"node": true
|
"node": true,
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2016,
|
"ecmaVersion": 2016,
|
||||||
"sourceType": "module"
|
"sourceType": "module",
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function OpenURL(url) {
|
|||||||
* Opens the given filename using the system's default file handler
|
* Opens the given filename using the system's default file handler
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} filename
|
* @param {sting} filename
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function OpenFile(filename) {
|
export function OpenFile(filename) {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ if (window.crypto) {
|
|||||||
export function Call(bindingName, data, timeout) {
|
export function Call(bindingName, data, timeout) {
|
||||||
|
|
||||||
// Timeout infinite by default
|
// Timeout infinite by default
|
||||||
if (timeout == null) {
|
if (timeout == null || timeout == undefined) {
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function Invoke(message) {
|
|||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} type
|
* @param {string} type
|
||||||
* @param {Object} payload
|
* @param {string} payload
|
||||||
* @param {string=} callbackID
|
* @param {string=} callbackID
|
||||||
*/
|
*/
|
||||||
export function SendMessage(type, payload, callbackID) {
|
export function SendMessage(type, payload, callbackID) {
|
||||||
|
|||||||
6
runtime/js/package-lock.json
generated
6
runtime/js/package-lock.json
generated
@@ -3959,9 +3959,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"interpret": {
|
"interpret": {
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
index.js
|
bridge.js
|
||||||
8
runtime/js/runtime/package-lock.json
generated
8
runtime/js/runtime/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wailsapp/runtime",
|
"name": "@wailsapp/runtime",
|
||||||
"version": "1.1.1",
|
"version": "1.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -130,9 +130,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"invert-kv": {
|
"invert-kv": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
echo "**** Checking if Wails passes unit tests ****"
|
echo "**** Checking if Wails passes unit tests ****"
|
||||||
if ! go test ./lib/... ./runtime/... ./cmd/...
|
if ! go test ./...
|
||||||
then
|
then
|
||||||
echo ""
|
echo ""
|
||||||
echo "ERROR: Unit tests failed!"
|
echo "ERROR: Unit tests failed!"
|
||||||
|
|||||||
13
v2/.vscode/settings.json
vendored
13
v2/.vscode/settings.json
vendored
@@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"ios": "c",
|
|
||||||
"typeinfo": "c",
|
|
||||||
"sstream": "c",
|
|
||||||
"__functional_03": "c",
|
|
||||||
"functional": "c",
|
|
||||||
"__locale": "c",
|
|
||||||
"locale": "c",
|
|
||||||
"chrono": "c",
|
|
||||||
"system_error": "c"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
40
v2/NOTES.md
40
v2/NOTES.md
@@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
# Packing linux
|
|
||||||
|
|
||||||
* create app, app.desktop, app.png (512x512)
|
|
||||||
* chmod +x app!
|
|
||||||
* ./linuxdeploy-x86_64.AppImage --appdir AppDir -i react.png -d react.desktop -e react --output appimage
|
|
||||||
|
|
||||||
|
|
||||||
# Wails Doctor
|
|
||||||
|
|
||||||
Tested on:
|
|
||||||
|
|
||||||
* Debian 8
|
|
||||||
* Ubuntu 20.04
|
|
||||||
* Ubuntu 19.10
|
|
||||||
* Solus 4.1
|
|
||||||
* Centos 8
|
|
||||||
* Gentoo
|
|
||||||
* OpenSUSE/leap
|
|
||||||
* Fedora 31
|
|
||||||
|
|
||||||
### Development
|
|
||||||
|
|
||||||
Add a new package manager processor here: `v2/internal/system/packagemanager/`. IsAvailable should work even if the package is installed.
|
|
||||||
Add your new package manager to the list of package managers in `v2/internal/system/packagemanager/packagemanager.go`:
|
|
||||||
|
|
||||||
```
|
|
||||||
var db = map[string]PackageManager{
|
|
||||||
"eopkg": NewEopkg(),
|
|
||||||
"apt": NewApt(),
|
|
||||||
"yum": NewYum(),
|
|
||||||
"pacman": NewPacman(),
|
|
||||||
"emerge": NewEmerge(),
|
|
||||||
"zypper": NewZypper(),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Gentoo
|
|
||||||
|
|
||||||
* Setup docker image using: emerge-webrsync -x -v
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# Wails v2 ALPHA
|
|
||||||
|
|
||||||
This branch contains WORK IN PROGRESS! There are no guarantees. Use at your peril!
|
|
||||||
|
|
||||||
This document will be updated as progress is made.
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddBuildSubcommand adds the `build` command for the Wails application
|
|
||||||
func AddBuildSubcommand(app *clir.Cli, w io.Writer) {
|
|
||||||
|
|
||||||
outputType := "desktop"
|
|
||||||
|
|
||||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
|
||||||
|
|
||||||
command := app.NewSubCommand("build", "Builds the application")
|
|
||||||
|
|
||||||
// Setup production flag
|
|
||||||
production := false
|
|
||||||
command.BoolFlag("production", "Build in production mode", &production)
|
|
||||||
|
|
||||||
// Setup pack flag
|
|
||||||
pack := false
|
|
||||||
command.BoolFlag("package", "Create a platform specific package", &pack)
|
|
||||||
|
|
||||||
compilerCommand := "go"
|
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
|
||||||
|
|
||||||
compress := false
|
|
||||||
command.BoolFlag("compress", "Compress final binary", &compress)
|
|
||||||
|
|
||||||
// Setup Platform flag
|
|
||||||
platform := runtime.GOOS
|
|
||||||
command.StringFlag("platform", "Platform to target", &platform)
|
|
||||||
|
|
||||||
// Verbosity
|
|
||||||
verbosity := 1
|
|
||||||
command.IntFlag("v", "Verbosity level (0 - silent, 1 - default, 2 - verbose)", &verbosity)
|
|
||||||
|
|
||||||
// ldflags to pass to `go`
|
|
||||||
ldflags := ""
|
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
|
||||||
|
|
||||||
// tags to pass to `go`
|
|
||||||
tags := ""
|
|
||||||
command.StringFlag("tags", "tags to pass to Go compiler (quoted and space separated)", &tags)
|
|
||||||
|
|
||||||
// Retain assets
|
|
||||||
keepAssets := false
|
|
||||||
command.BoolFlag("k", "Keep generated assets", &keepAssets)
|
|
||||||
|
|
||||||
// Retain assets
|
|
||||||
outputFilename := ""
|
|
||||||
command.StringFlag("o", "Output filename", &outputFilename)
|
|
||||||
|
|
||||||
// Clean build directory
|
|
||||||
cleanBuildDirectory := false
|
|
||||||
command.BoolFlag("clean", "Clean the build directory before building", &cleanBuildDirectory)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
quiet := verbosity == 0
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
logger.Mute(quiet)
|
|
||||||
|
|
||||||
// Validate output type
|
|
||||||
if !validTargetTypes.Contains(outputType) {
|
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !quiet {
|
|
||||||
app.PrintBanner()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup mode
|
|
||||||
mode := build.Debug
|
|
||||||
if production {
|
|
||||||
mode = build.Production
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check platform
|
|
||||||
validPlatformArch := slicer.String([]string{
|
|
||||||
"darwin",
|
|
||||||
"darwin/amd64",
|
|
||||||
"darwin/arm64",
|
|
||||||
"darwin/universal",
|
|
||||||
//"linux/amd64",
|
|
||||||
//"linux/arm-7",
|
|
||||||
"windows",
|
|
||||||
"windows/amd64",
|
|
||||||
})
|
|
||||||
if !validPlatformArch.Contains(platform) {
|
|
||||||
return fmt.Errorf("platform %s is not supported", platform)
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress && platform == "darwin/universal" {
|
|
||||||
println("Warning: compress flag unsupported for universal binaries. Ignoring.")
|
|
||||||
compress = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags
|
|
||||||
userTags := []string{}
|
|
||||||
for _, tag := range strings.Split(tags, " ") {
|
|
||||||
thisTag := strings.TrimSpace(tag)
|
|
||||||
if thisTag != "" {
|
|
||||||
userTags = append(userTags, thisTag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create BuildOptions
|
|
||||||
buildOptions := &build.Options{
|
|
||||||
Logger: logger,
|
|
||||||
OutputType: outputType,
|
|
||||||
OutputFile: outputFilename,
|
|
||||||
CleanBuildDirectory: cleanBuildDirectory,
|
|
||||||
Mode: mode,
|
|
||||||
Pack: pack,
|
|
||||||
LDFlags: ldflags,
|
|
||||||
Compiler: compilerCommand,
|
|
||||||
KeepAssets: keepAssets,
|
|
||||||
Verbosity: verbosity,
|
|
||||||
Compress: compress,
|
|
||||||
UserTags: userTags,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate platform and arch
|
|
||||||
platformSplit := strings.Split(platform, "/")
|
|
||||||
buildOptions.Platform = platformSplit[0]
|
|
||||||
buildOptions.Arch = runtime.GOARCH
|
|
||||||
if len(platformSplit) == 2 {
|
|
||||||
buildOptions.Arch = platformSplit[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a new tabwriter
|
|
||||||
w := new(tabwriter.Writer)
|
|
||||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
|
||||||
|
|
||||||
buildModeText := "debug"
|
|
||||||
if production {
|
|
||||||
buildModeText = "production"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out the system information
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
fmt.Fprintf(w, "App Type: \t%s\n", buildOptions.OutputType)
|
|
||||||
fmt.Fprintf(w, "Platform: \t%s\n", buildOptions.Platform)
|
|
||||||
fmt.Fprintf(w, "Arch: \t%s\n", buildOptions.Arch)
|
|
||||||
fmt.Fprintf(w, "Compiler: \t%s\n", buildOptions.Compiler)
|
|
||||||
fmt.Fprintf(w, "Compress: \t%t\n", buildOptions.Compress)
|
|
||||||
fmt.Fprintf(w, "Build Mode: \t%s\n", buildModeText)
|
|
||||||
fmt.Fprintf(w, "Package: \t%t\n", buildOptions.Pack)
|
|
||||||
fmt.Fprintf(w, "Clean Build Dir: \t%t\n", buildOptions.CleanBuildDirectory)
|
|
||||||
fmt.Fprintf(w, "KeepAssets: \t%t\n", buildOptions.KeepAssets)
|
|
||||||
fmt.Fprintf(w, "LDFlags: \t\"%s\"\n", buildOptions.LDFlags)
|
|
||||||
fmt.Fprintf(w, "Tags: \t[%s]\n", strings.Join(buildOptions.UserTags, ","))
|
|
||||||
if len(buildOptions.OutputFile) > 0 {
|
|
||||||
fmt.Fprintf(w, "Output File: \t%s\n", buildOptions.OutputFile)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
w.Flush()
|
|
||||||
|
|
||||||
return doBuild(buildOptions)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// doBuild is our main build command
|
|
||||||
func doBuild(buildOptions *build.Options) error {
|
|
||||||
|
|
||||||
// Start Time
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
outputFilename, err := build.Build(buildOptions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output stats
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
buildOptions.Logger.Println("")
|
|
||||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
|
||||||
buildOptions.Logger.Println("")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
package debug
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSubcommand adds the `debug` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|
||||||
|
|
||||||
outputType := "desktop"
|
|
||||||
|
|
||||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
|
||||||
|
|
||||||
command := app.NewSubCommand("debug", "Builds the application then runs delve on the binary")
|
|
||||||
|
|
||||||
// Setup target type flag
|
|
||||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
|
||||||
command.StringFlag("t", description, &outputType)
|
|
||||||
|
|
||||||
compilerCommand := "go"
|
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
|
||||||
|
|
||||||
quiet := false
|
|
||||||
command.BoolFlag("q", "Suppress output to console", &quiet)
|
|
||||||
|
|
||||||
// ldflags to pass to `go`
|
|
||||||
ldflags := ""
|
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
|
||||||
|
|
||||||
// Log to file
|
|
||||||
logFile := ""
|
|
||||||
command.StringFlag("l", "Log to file", &logFile)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
logger.Mute(quiet)
|
|
||||||
|
|
||||||
// Validate output type
|
|
||||||
if !validTargetTypes.Contains(outputType) {
|
|
||||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !quiet {
|
|
||||||
app.PrintBanner()
|
|
||||||
}
|
|
||||||
|
|
||||||
task := fmt.Sprintf("Building %s Application", strings.Title(outputType))
|
|
||||||
logger.Println(task)
|
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
|
||||||
|
|
||||||
// Setup mode
|
|
||||||
mode := build.Debug
|
|
||||||
|
|
||||||
// Create BuildOptions
|
|
||||||
buildOptions := &build.Options{
|
|
||||||
Logger: logger,
|
|
||||||
OutputType: outputType,
|
|
||||||
Mode: mode,
|
|
||||||
Pack: false,
|
|
||||||
Platform: runtime.GOOS,
|
|
||||||
LDFlags: ldflags,
|
|
||||||
Compiler: compilerCommand,
|
|
||||||
KeepAssets: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
outputFilename, err := doDebugBuild(buildOptions)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check delve exists
|
|
||||||
delveExists := shell.CommandExists("dlv")
|
|
||||||
if !delveExists {
|
|
||||||
return fmt.Errorf("cannot launch delve (Is it installed?)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get cwd
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch delve
|
|
||||||
println("Launching Delve on port 2345...")
|
|
||||||
command := shell.CreateCommand(cwd, "dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", outputFilename)
|
|
||||||
return command.Run()
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// doDebugBuild is our main build command
|
|
||||||
func doDebugBuild(buildOptions *build.Options) (string, error) {
|
|
||||||
|
|
||||||
// Start Time
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
outputFilename, err := build.Build(buildOptions)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output stats
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
buildOptions.Logger.Println("")
|
|
||||||
buildOptions.Logger.Println(fmt.Sprintf("Built '%s' in %s.", outputFilename, elapsed.Round(time.Millisecond).String()))
|
|
||||||
buildOptions.Logger.Println("")
|
|
||||||
|
|
||||||
return outputFilename, nil
|
|
||||||
}
|
|
||||||
@@ -1,285 +0,0 @@
|
|||||||
package dev
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/colour"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/process"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/commands/build"
|
|
||||||
)
|
|
||||||
|
|
||||||
func LogGreen(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.Green(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogRed(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.Red(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
func LogDarkYellow(message string, args ...interface{}) {
|
|
||||||
text := fmt.Sprintf(message, args...)
|
|
||||||
println(colour.DarkYellow(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSubcommand adds the `dev` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|
||||||
|
|
||||||
command := app.NewSubCommand("dev", "Development mode")
|
|
||||||
|
|
||||||
// Passthrough ldflags
|
|
||||||
ldflags := ""
|
|
||||||
command.StringFlag("ldflags", "optional ldflags", &ldflags)
|
|
||||||
|
|
||||||
// compiler command
|
|
||||||
compilerCommand := "go"
|
|
||||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
|
||||||
|
|
||||||
// extensions to trigger rebuilds
|
|
||||||
extensions := "go"
|
|
||||||
command.StringFlag("e", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
|
||||||
|
|
||||||
// extensions to trigger rebuilds
|
|
||||||
showWarnings := false
|
|
||||||
command.BoolFlag("w", "Show warnings", &showWarnings)
|
|
||||||
|
|
||||||
loglevel := ""
|
|
||||||
command.StringFlag("loglevel", "Loglevel to use - Trace, Debug, Info, Warning, Error", &loglevel)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
app.PrintBanner()
|
|
||||||
|
|
||||||
// TODO: Check you are in a project directory
|
|
||||||
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
var debugBinaryProcess *process.Process = nil
|
|
||||||
var extensionsThatTriggerARebuild = strings.Split(extensions, ",")
|
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
quitChannel := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(quitChannel, os.Interrupt, os.Kill, syscall.SIGTERM)
|
|
||||||
|
|
||||||
debounceQuit := make(chan bool, 1)
|
|
||||||
|
|
||||||
// Do initial build
|
|
||||||
logger.Println("Building application for development...")
|
|
||||||
newProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
|
||||||
if newProcess != nil {
|
|
||||||
debugBinaryProcess = newProcess
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
go debounce(100*time.Millisecond, watcher.Events, debounceQuit, func(event fsnotify.Event) {
|
|
||||||
// logger.Println("event: %+v", event)
|
|
||||||
|
|
||||||
// Check for new directories
|
|
||||||
if event.Op&fsnotify.Create == fsnotify.Create {
|
|
||||||
// If this is a folder, add it to our watch list
|
|
||||||
if fs.DirExists(event.Name) {
|
|
||||||
if !strings.Contains(event.Name, "node_modules") {
|
|
||||||
err := watcher.Add(event.Name)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("%s", err.Error())
|
|
||||||
}
|
|
||||||
LogGreen("[New Directory] Watching new directory: %s", event.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for file writes
|
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
||||||
|
|
||||||
var rebuild bool = false
|
|
||||||
|
|
||||||
// Iterate all file patterns
|
|
||||||
for _, pattern := range extensionsThatTriggerARebuild {
|
|
||||||
if strings.HasSuffix(event.Name, pattern) {
|
|
||||||
rebuild = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rebuild {
|
|
||||||
if showWarnings {
|
|
||||||
LogDarkYellow("[File change] %s did not match extension list (%s)", event.Name, extensions)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
LogGreen("[Attempting rebuild] %s updated", event.Name)
|
|
||||||
|
|
||||||
// Do a rebuild
|
|
||||||
|
|
||||||
// Try and build the app
|
|
||||||
newBinaryProcess, err := restartApp(logger, "dev", ldflags, compilerCommand, debugBinaryProcess, loglevel)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error during build: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If we have a new process, save it
|
|
||||||
if newBinaryProcess != nil {
|
|
||||||
debugBinaryProcess = newBinaryProcess
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get project dir
|
|
||||||
projectDir, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all subdirectories
|
|
||||||
dirs, err := fs.GetSubdirectories(projectDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
LogGreen("Watching (sub)/directory: %s", projectDir)
|
|
||||||
|
|
||||||
// Setup a watcher for non-node_modules directories
|
|
||||||
dirs.Each(func(dir string) {
|
|
||||||
if strings.Contains(dir, "node_modules") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Ignore build directory
|
|
||||||
if strings.HasPrefix(dir, filepath.Join(projectDir, "build")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = watcher.Add(dir)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Wait until we get a quit signal
|
|
||||||
quit := false
|
|
||||||
for quit == false {
|
|
||||||
select {
|
|
||||||
case <-quitChannel:
|
|
||||||
LogGreen("\nCaught quit")
|
|
||||||
// Notify debouncer to quit
|
|
||||||
debounceQuit <- true
|
|
||||||
quit = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill the current program if running
|
|
||||||
if debugBinaryProcess != nil {
|
|
||||||
err := debugBinaryProcess.Kill()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogGreen("Development mode exited")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit: https://drailing.net/2018/01/debounce-function-for-golang/
|
|
||||||
func debounce(interval time.Duration, input chan fsnotify.Event, quitChannel chan bool, cb func(arg fsnotify.Event)) {
|
|
||||||
var item fsnotify.Event
|
|
||||||
timer := time.NewTimer(interval)
|
|
||||||
exit:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case item = <-input:
|
|
||||||
timer.Reset(interval)
|
|
||||||
case <-timer.C:
|
|
||||||
if item.Name != "" {
|
|
||||||
cb(item)
|
|
||||||
}
|
|
||||||
case <-quitChannel:
|
|
||||||
break exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func restartApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, debugBinaryProcess *process.Process, loglevel string) (*process.Process, error) {
|
|
||||||
|
|
||||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand)
|
|
||||||
println()
|
|
||||||
if err != nil {
|
|
||||||
LogRed("Build error - continuing to run current version")
|
|
||||||
LogDarkYellow(err.Error())
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill existing binary if need be
|
|
||||||
if debugBinaryProcess != nil {
|
|
||||||
killError := debugBinaryProcess.Kill()
|
|
||||||
|
|
||||||
if killError != nil {
|
|
||||||
logger.Fatal("Unable to kill debug binary (PID: %d)!", debugBinaryProcess.PID())
|
|
||||||
}
|
|
||||||
|
|
||||||
debugBinaryProcess = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Generate `backend.js`
|
|
||||||
|
|
||||||
// Start up new binary
|
|
||||||
newProcess := process.NewProcess(logger, appBinary, "-loglevel", loglevel)
|
|
||||||
err = newProcess.Start()
|
|
||||||
if err != nil {
|
|
||||||
// Remove binary
|
|
||||||
deleteError := fs.DeleteFile(appBinary)
|
|
||||||
if deleteError != nil {
|
|
||||||
logger.Fatal("Unable to delete app binary: " + appBinary)
|
|
||||||
}
|
|
||||||
logger.Fatal("Unable to start application: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return newProcess, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string) (string, error) {
|
|
||||||
|
|
||||||
// Create random output file
|
|
||||||
outputFile := fmt.Sprintf("dev-%d", time.Now().Unix())
|
|
||||||
|
|
||||||
// Create BuildOptions
|
|
||||||
buildOptions := &build.Options{
|
|
||||||
Logger: logger,
|
|
||||||
OutputType: outputType,
|
|
||||||
Mode: build.Debug,
|
|
||||||
Pack: false,
|
|
||||||
Platform: runtime.GOOS,
|
|
||||||
LDFlags: ldflags,
|
|
||||||
Compiler: compilerCommand,
|
|
||||||
OutputFile: outputFile,
|
|
||||||
IgnoreFrontend: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
return build.Build(buildOptions)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
package doctor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/system"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/system/packagemanager"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSubcommand adds the `doctor` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|
||||||
|
|
||||||
command := app.NewSubCommand("doctor", "Diagnose your environment")
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
|
|
||||||
app.PrintBanner()
|
|
||||||
logger.Print("Scanning system - please wait...")
|
|
||||||
|
|
||||||
// Get system info
|
|
||||||
info, err := system.GetInfo()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Println("Done.")
|
|
||||||
|
|
||||||
// Start a new tabwriter
|
|
||||||
w := new(tabwriter.Writer)
|
|
||||||
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
|
|
||||||
|
|
||||||
// Write out the system information
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
fmt.Fprintf(w, "System\n")
|
|
||||||
fmt.Fprintf(w, "------\n")
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "OS:", info.OS.Name)
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Version: ", info.OS.Version)
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "ID:", info.OS.ID)
|
|
||||||
|
|
||||||
// Exit early if PM not found
|
|
||||||
if info.PM == nil {
|
|
||||||
w.Flush()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
|
||||||
|
|
||||||
// Output Go Information
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Go Version:", runtime.Version())
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Platform:", runtime.GOOS)
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", "Architecture:", runtime.GOARCH)
|
|
||||||
|
|
||||||
// Output Dependencies Status
|
|
||||||
var dependenciesMissing = []string{}
|
|
||||||
var externalPackages = []*packagemanager.Dependancy{}
|
|
||||||
var dependenciesAvailableRequired = 0
|
|
||||||
var dependenciesAvailableOptional = 0
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
fmt.Fprintf(w, "Dependency\tPackage Name\tStatus\tVersion\n")
|
|
||||||
fmt.Fprintf(w, "----------\t------------\t------\t-------\n")
|
|
||||||
|
|
||||||
// Loop over dependencies
|
|
||||||
for _, dependency := range info.Dependencies {
|
|
||||||
|
|
||||||
name := dependency.Name
|
|
||||||
if dependency.Optional {
|
|
||||||
name += "*"
|
|
||||||
}
|
|
||||||
packageName := "Unknown"
|
|
||||||
status := "Not Found"
|
|
||||||
|
|
||||||
// If we found the package
|
|
||||||
if dependency.PackageName != "" {
|
|
||||||
|
|
||||||
packageName = dependency.PackageName
|
|
||||||
|
|
||||||
// If it's installed, update the status
|
|
||||||
if dependency.Installed {
|
|
||||||
status = "Installed"
|
|
||||||
} else {
|
|
||||||
// Generate meaningful status text
|
|
||||||
status = "Available"
|
|
||||||
|
|
||||||
if dependency.Optional {
|
|
||||||
dependenciesAvailableOptional++
|
|
||||||
} else {
|
|
||||||
dependenciesAvailableRequired++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !dependency.Optional {
|
|
||||||
dependenciesMissing = append(dependenciesMissing, dependency.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dependency.External {
|
|
||||||
externalPackages = append(externalPackages, dependency)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, packageName, status, dependency.Version)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
fmt.Fprintf(w, "* - Optional Dependency\n")
|
|
||||||
w.Flush()
|
|
||||||
logger.Println("")
|
|
||||||
logger.Println("Diagnosis")
|
|
||||||
logger.Println("---------\n")
|
|
||||||
|
|
||||||
// Generate an appropriate diagnosis
|
|
||||||
|
|
||||||
if len(dependenciesMissing) == 0 && dependenciesAvailableRequired == 0 {
|
|
||||||
logger.Println("Your system is ready for Wails development!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if dependenciesAvailableRequired != 0 {
|
|
||||||
log.Println("Install required packages using: " + info.Dependencies.InstallAllRequiredCommand())
|
|
||||||
}
|
|
||||||
|
|
||||||
if dependenciesAvailableOptional != 0 {
|
|
||||||
log.Println("Install optional packages using: " + info.Dependencies.InstallAllOptionalCommand())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(externalPackages) > 0 {
|
|
||||||
for _, p := range externalPackages {
|
|
||||||
if p.Optional {
|
|
||||||
print("[Optional] ")
|
|
||||||
}
|
|
||||||
log.Println("Install " + p.Name + ": " + p.InstallCommand)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(dependenciesMissing) != 0 {
|
|
||||||
// TODO: Check if apps are available locally and if so, adjust the diagnosis
|
|
||||||
log.Println("Fatal:")
|
|
||||||
log.Println("Required dependencies missing: " + strings.Join(dependenciesMissing, " "))
|
|
||||||
log.Println("Please read this article on how to resolve this: https://wails.app/guides/resolving-missing-packages")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package generate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/parser"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSubcommand adds the `generate` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|
||||||
|
|
||||||
command := app.NewSubCommand("generate", "Code Generation Tools")
|
|
||||||
|
|
||||||
// Backend API
|
|
||||||
backendAPI := command.NewSubCommand("module", "Generates a JS module for the frontend to interface with the backend")
|
|
||||||
|
|
||||||
// Quiet Init
|
|
||||||
quiet := false
|
|
||||||
backendAPI.BoolFlag("q", "Suppress output to console", &quiet)
|
|
||||||
|
|
||||||
backendAPI.Action(func() error {
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
logger.Mute(quiet)
|
|
||||||
|
|
||||||
app.PrintBanner()
|
|
||||||
|
|
||||||
logger.Print("Generating Javascript module for Go code...")
|
|
||||||
|
|
||||||
// Start Time
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
p, err := parser.GenerateWailsFrontendPackage()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Println("done.")
|
|
||||||
logger.Println("")
|
|
||||||
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
packages := p.Packages
|
|
||||||
|
|
||||||
// Print report
|
|
||||||
for _, pkg := range p.Packages {
|
|
||||||
if pkg.ShouldGenerate() {
|
|
||||||
logPackage(pkg, logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Println("%d packages parsed in %s.", len(packages), elapsed)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func logPackage(pkg *parser.Package, logger *clilogger.CLILogger) {
|
|
||||||
|
|
||||||
logger.Println("Processed Go package '" + pkg.Gopackage.Name + "' as '" + pkg.Name + "'")
|
|
||||||
for _, strct := range pkg.Structs() {
|
|
||||||
logger.Println("")
|
|
||||||
logger.Println(" Processed struct '" + strct.Name + "'")
|
|
||||||
if strct.IsBound {
|
|
||||||
for _, method := range strct.Methods {
|
|
||||||
logger.Println(" Bound method '" + method.Name + "'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strct.IsUsedAsData {
|
|
||||||
for _, field := range strct.Fields {
|
|
||||||
if !field.Ignored {
|
|
||||||
logger.Print(" Processed ")
|
|
||||||
if field.IsOptional {
|
|
||||||
logger.Print("optional ")
|
|
||||||
}
|
|
||||||
logger.Println("field '" + field.Name + "' as '" + field.JSName() + "'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Println("")
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
package initialise
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise/templates"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/git"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSubcommand adds the `init` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
|
||||||
|
|
||||||
// Load the template shortnames
|
|
||||||
validShortNames, err := templates.TemplateShortNames()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
command := app.NewSubCommand("init", "Initialise a new Wails project")
|
|
||||||
|
|
||||||
// Setup template name flag
|
|
||||||
templateName := "vanilla"
|
|
||||||
description := "Name of template to use. Valid tempates: " + validShortNames.Join(" ")
|
|
||||||
command.StringFlag("t", description, &templateName)
|
|
||||||
|
|
||||||
// Setup project name
|
|
||||||
projectName := ""
|
|
||||||
command.StringFlag("n", "Name of project", &projectName)
|
|
||||||
|
|
||||||
// Setup project directory
|
|
||||||
projectDirectory := ""
|
|
||||||
command.StringFlag("d", "Project directory", &projectDirectory)
|
|
||||||
|
|
||||||
// Quiet Init
|
|
||||||
quiet := false
|
|
||||||
command.BoolFlag("q", "Supress output to console", &quiet)
|
|
||||||
|
|
||||||
initGit := false
|
|
||||||
gitInstalled := git.IsInstalled()
|
|
||||||
if gitInstalled {
|
|
||||||
// Git Init
|
|
||||||
command.BoolFlag("g", "Initialise git repository", &initGit)
|
|
||||||
}
|
|
||||||
|
|
||||||
// VSCode project files
|
|
||||||
vscode := false
|
|
||||||
command.BoolFlag("vscode", "Generate VSCode project files", &vscode)
|
|
||||||
|
|
||||||
// List templates
|
|
||||||
list := false
|
|
||||||
command.BoolFlag("l", "List templates", &list)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
logger.Mute(quiet)
|
|
||||||
|
|
||||||
// Are we listing templates?
|
|
||||||
if list {
|
|
||||||
app.PrintBanner()
|
|
||||||
err := templates.OutputList(logger)
|
|
||||||
logger.Println("")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate output type
|
|
||||||
if !validShortNames.Contains(templateName) {
|
|
||||||
logger.Print(fmt.Sprintf("[ERROR] Template '%s' is not valid", templateName))
|
|
||||||
logger.Println("")
|
|
||||||
command.PrintHelp()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate name
|
|
||||||
if len(projectName) == 0 {
|
|
||||||
logger.Println("ERROR: Project name required")
|
|
||||||
logger.Println("")
|
|
||||||
command.PrintHelp()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !quiet {
|
|
||||||
app.PrintBanner()
|
|
||||||
}
|
|
||||||
|
|
||||||
task := fmt.Sprintf("Initialising Project %s", strings.Title(projectName))
|
|
||||||
logger.Println(task)
|
|
||||||
logger.Println(strings.Repeat("-", len(task)))
|
|
||||||
|
|
||||||
// Create Template Options
|
|
||||||
options := &templates.Options{
|
|
||||||
ProjectName: projectName,
|
|
||||||
TargetDir: projectDirectory,
|
|
||||||
TemplateName: templateName,
|
|
||||||
Logger: logger,
|
|
||||||
GenerateVSCode: vscode,
|
|
||||||
InitGit: initGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to discover author details from git config
|
|
||||||
err := findAuthorDetails(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return initProject(options)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// initProject is our main init command
|
|
||||||
func initProject(options *templates.Options) error {
|
|
||||||
|
|
||||||
// Start Time
|
|
||||||
start := time.Now()
|
|
||||||
|
|
||||||
// Install the template
|
|
||||||
err := templates.Install(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.InitGit {
|
|
||||||
err = initGit(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output stats
|
|
||||||
elapsed := time.Since(start)
|
|
||||||
options.Logger.Println("")
|
|
||||||
options.Logger.Println("Project Name: " + options.ProjectName)
|
|
||||||
options.Logger.Println("Project Directory: " + options.TargetDir)
|
|
||||||
options.Logger.Println("Project Template: " + options.TemplateName)
|
|
||||||
if options.GenerateVSCode {
|
|
||||||
options.Logger.Println("VSCode config files generated.")
|
|
||||||
}
|
|
||||||
if options.InitGit {
|
|
||||||
options.Logger.Println("Git repository initialised.")
|
|
||||||
}
|
|
||||||
options.Logger.Println("")
|
|
||||||
options.Logger.Println(fmt.Sprintf("Initialised project '%s' in %s.", options.ProjectName, elapsed.Round(time.Millisecond).String()))
|
|
||||||
options.Logger.Println("")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initGit(options *templates.Options) error {
|
|
||||||
err := git.InitRepo(options.TargetDir)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Unable to initialise git repository:")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findAuthorDetails(options *templates.Options) error {
|
|
||||||
if git.IsInstalled() {
|
|
||||||
name, err := git.Name()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorName = strings.TrimSpace(name)
|
|
||||||
|
|
||||||
email, err := git.Email()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.AuthorEmail = strings.TrimSpace(email)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Wails: Debug {{.ProjectName}} (Desktop)",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "exec",
|
|
||||||
"program": "${workspaceFolder}/{{.PathToDesktopBinary}}",
|
|
||||||
"preLaunchTask": "build_desktop",
|
|
||||||
"cwd": "",
|
|
||||||
"env": {},
|
|
||||||
"args": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Wails: Debug {{.ProjectName}} (Server)",
|
|
||||||
"type": "go",
|
|
||||||
"request": "launch",
|
|
||||||
"mode": "exec",
|
|
||||||
"program": "${workspaceFolder}/{{.PathToServerBinary}}",
|
|
||||||
"preLaunchTask": "build_server",
|
|
||||||
"cwd": "",
|
|
||||||
"env": {},
|
|
||||||
"args": []
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "build_desktop",
|
|
||||||
"type": "shell",
|
|
||||||
"options": {
|
|
||||||
"cwd": "{{.TargetDir}}"
|
|
||||||
},
|
|
||||||
"command": "wails build"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "build_server",
|
|
||||||
"type": "shell",
|
|
||||||
"options": {
|
|
||||||
"cwd": "{{.TargetDir}}"
|
|
||||||
},
|
|
||||||
"command": "wails build -t server"
|
|
||||||
},
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,331 +0,0 @@
|
|||||||
package templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
gofs "io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/leaanthony/debme"
|
|
||||||
"github.com/leaanthony/gosod"
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/olekukonko/tablewriter"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed templates
|
|
||||||
var templates embed.FS
|
|
||||||
|
|
||||||
//go:embed ides/*
|
|
||||||
var ides embed.FS
|
|
||||||
|
|
||||||
// Cahce for the templates
|
|
||||||
// We use this because we need different views of the same data
|
|
||||||
var templateCache []Template = nil
|
|
||||||
|
|
||||||
// Data contains the data we wish to embed during template installation
|
|
||||||
type Data struct {
|
|
||||||
ProjectName string
|
|
||||||
BinaryName string
|
|
||||||
WailsVersion string
|
|
||||||
NPMProjectName string
|
|
||||||
AuthorName string
|
|
||||||
AuthorEmail string
|
|
||||||
AuthorNameAndEmail string
|
|
||||||
WailsDirectory string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options for installing a template
|
|
||||||
type Options struct {
|
|
||||||
ProjectName string
|
|
||||||
TemplateName string
|
|
||||||
BinaryName string
|
|
||||||
TargetDir string
|
|
||||||
Logger *clilogger.CLILogger
|
|
||||||
GenerateVSCode bool
|
|
||||||
PathToDesktopBinary string
|
|
||||||
PathToServerBinary string
|
|
||||||
InitGit bool
|
|
||||||
AuthorName string
|
|
||||||
AuthorEmail string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Template holds data relating to a template
|
|
||||||
// including the metadata stored in template.json
|
|
||||||
type Template struct {
|
|
||||||
|
|
||||||
// Template details
|
|
||||||
Name string `json:"name"`
|
|
||||||
ShortName string `json:"shortname"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
HelpURL string `json:"helpurl"`
|
|
||||||
|
|
||||||
// Other data
|
|
||||||
FS gofs.FS `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTemplate(template gofs.FS) (Template, error) {
|
|
||||||
var result Template
|
|
||||||
data, err := gofs.ReadFile(template, "template.json")
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(data, &result)
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
result.FS = template
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplateShortNames returns a slicer of short template names
|
|
||||||
func TemplateShortNames() (*slicer.StringSlicer, error) {
|
|
||||||
|
|
||||||
var result slicer.StringSlicer
|
|
||||||
|
|
||||||
// If the cache isn't loaded, load it
|
|
||||||
if templateCache == nil {
|
|
||||||
err := loadTemplateCache()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, template := range templateCache {
|
|
||||||
result.Add(template.ShortName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns the list of available templates
|
|
||||||
func List() ([]Template, error) {
|
|
||||||
|
|
||||||
// If the cache isn't loaded, load it
|
|
||||||
if templateCache == nil {
|
|
||||||
err := loadTemplateCache()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return templateCache, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTemplateByShortname returns the template with the given short name
|
|
||||||
func getTemplateByShortname(shortname string) (Template, error) {
|
|
||||||
|
|
||||||
var result Template
|
|
||||||
|
|
||||||
// If the cache isn't loaded, load it
|
|
||||||
if templateCache == nil {
|
|
||||||
err := loadTemplateCache()
|
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, template := range templateCache {
|
|
||||||
if template.ShortName == shortname {
|
|
||||||
return template, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, fmt.Errorf("shortname '%s' is not a valid template shortname", shortname)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads the template cache
|
|
||||||
func loadTemplateCache() error {
|
|
||||||
|
|
||||||
templatesFS, err := debme.FS(templates, "templates")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get directories
|
|
||||||
files, err := templatesFS.ReadDir(".")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset cache
|
|
||||||
templateCache = []Template{}
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
if file.IsDir() {
|
|
||||||
templateFS, err := templatesFS.FS(file.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
template, err := parseTemplate(templateFS)
|
|
||||||
if err != nil {
|
|
||||||
// Cannot parse this template, continue
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
templateCache = append(templateCache, template)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install the given template
|
|
||||||
func Install(options *Options) error {
|
|
||||||
// Get cwd
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Did the user want to install in current directory?
|
|
||||||
if options.TargetDir == "" {
|
|
||||||
|
|
||||||
// If the current directory is empty, use it
|
|
||||||
isEmpty, err := fs.DirIsEmpty(cwd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if isEmpty {
|
|
||||||
// Yes - use cwd
|
|
||||||
options.TargetDir = cwd
|
|
||||||
} else {
|
|
||||||
options.TargetDir = filepath.Join(cwd, options.ProjectName)
|
|
||||||
if fs.DirExists(options.TargetDir) {
|
|
||||||
return fmt.Errorf("cannot create project directory. Dir exists: %s", options.TargetDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Get the absolute path of the given directory
|
|
||||||
targetDir, err := filepath.Abs(filepath.Join(cwd, options.TargetDir))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
options.TargetDir = targetDir
|
|
||||||
if !fs.DirExists(options.TargetDir) {
|
|
||||||
err := fs.Mkdir(options.TargetDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get template
|
|
||||||
template, err := getTemplateByShortname(options.TemplateName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use Gosod to install the template
|
|
||||||
installer := gosod.New(template.FS)
|
|
||||||
|
|
||||||
// Ignore template.json files
|
|
||||||
installer.IgnoreFile("template.json")
|
|
||||||
|
|
||||||
// Setup the data.
|
|
||||||
// We use the directory name for the binary name, like Go
|
|
||||||
BinaryName := filepath.Base(options.TargetDir)
|
|
||||||
NPMProjectName := strings.ToLower(strings.ReplaceAll(BinaryName, " ", ""))
|
|
||||||
localWailsDirectory := fs.RelativePath("../../../../../..")
|
|
||||||
templateData := &Data{
|
|
||||||
ProjectName: options.ProjectName,
|
|
||||||
BinaryName: filepath.Base(options.TargetDir),
|
|
||||||
NPMProjectName: NPMProjectName,
|
|
||||||
WailsDirectory: localWailsDirectory,
|
|
||||||
AuthorEmail: options.AuthorEmail,
|
|
||||||
AuthorName: options.AuthorName,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a formatted name and email combo.
|
|
||||||
if options.AuthorName != "" {
|
|
||||||
templateData.AuthorNameAndEmail = options.AuthorName + " "
|
|
||||||
}
|
|
||||||
if options.AuthorEmail != "" {
|
|
||||||
templateData.AuthorNameAndEmail += "<" + options.AuthorEmail + ">"
|
|
||||||
}
|
|
||||||
templateData.AuthorNameAndEmail = strings.TrimSpace(templateData.AuthorNameAndEmail)
|
|
||||||
|
|
||||||
// Extract the template
|
|
||||||
err = installer.Extract(options.TargetDir, templateData)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = generateIDEFiles(options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputList prints the list of available tempaltes to the given logger
|
|
||||||
func OutputList(logger *clilogger.CLILogger) error {
|
|
||||||
templates, err := List()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
table := tablewriter.NewWriter(logger.Writer)
|
|
||||||
table.SetHeader([]string{"Template", "Short Name", "Description"})
|
|
||||||
table.SetAutoWrapText(false)
|
|
||||||
table.SetAutoFormatHeaders(true)
|
|
||||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
|
||||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
|
||||||
table.SetCenterSeparator("")
|
|
||||||
table.SetColumnSeparator("")
|
|
||||||
table.SetRowSeparator("")
|
|
||||||
table.SetHeaderLine(false)
|
|
||||||
table.SetBorder(false)
|
|
||||||
table.SetTablePadding("\t") // pad with tabs
|
|
||||||
table.SetNoWhiteSpace(true)
|
|
||||||
for _, template := range templates {
|
|
||||||
table.Append([]string{template.Name, template.ShortName, template.Description})
|
|
||||||
}
|
|
||||||
table.Render()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateIDEFiles(options *Options) error {
|
|
||||||
|
|
||||||
if options.GenerateVSCode {
|
|
||||||
return generateVSCodeFiles(options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateVSCodeFiles(options *Options) error {
|
|
||||||
|
|
||||||
targetDir := filepath.Join(options.TargetDir, ".vscode")
|
|
||||||
source, err := debme.FS(ides, "ides/vscode")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use gosod to install the template
|
|
||||||
installer := gosod.New(source)
|
|
||||||
|
|
||||||
binaryName := filepath.Base(options.TargetDir)
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// yay windows
|
|
||||||
binaryName += ".exe"
|
|
||||||
}
|
|
||||||
|
|
||||||
options.PathToDesktopBinary = filepath.Join("build", runtime.GOOS, "desktop", binaryName)
|
|
||||||
options.PathToServerBinary = filepath.Join("build", runtime.GOOS, "server", binaryName)
|
|
||||||
|
|
||||||
err = installer.Extract(targetDir, options)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Basic application struct
|
|
||||||
type Basic struct {
|
|
||||||
runtime *wails.Runtime
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBasic creates a new Basic application struct
|
|
||||||
func NewBasic() *Basic {
|
|
||||||
return &Basic{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// startup is called at application startup
|
|
||||||
func (b *Basic) startup(runtime *wails.Runtime) {
|
|
||||||
// Perform your setup here
|
|
||||||
b.runtime = runtime
|
|
||||||
runtime.Window.SetTitle("{{.ProjectName}}")
|
|
||||||
}
|
|
||||||
|
|
||||||
// shutdown is called at application termination
|
|
||||||
func (b *Basic) shutdown() {
|
|
||||||
// Perform your teardown here
|
|
||||||
}
|
|
||||||
|
|
||||||
// Greet returns a greeting for the given name
|
|
||||||
func (b *Basic) Greet(name string) string {
|
|
||||||
return fmt.Sprintf("Hello %s!", name)
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="/main.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body data-wails-drag>
|
|
||||||
<div id="logo"></div>
|
|
||||||
<div id="input">
|
|
||||||
<input id="name" type="text"></input>
|
|
||||||
<button onclick="greet()">Greet</button>
|
|
||||||
</div>
|
|
||||||
<div id="result"></div>
|
|
||||||
|
|
||||||
<script src="/main.js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,16 +0,0 @@
|
|||||||
// Get input + focus
|
|
||||||
let nameElement = document.getElementById("name");
|
|
||||||
nameElement.focus();
|
|
||||||
|
|
||||||
// Setup the greet function
|
|
||||||
window.greet = function () {
|
|
||||||
|
|
||||||
// Get name
|
|
||||||
let name = nameElement.value;
|
|
||||||
|
|
||||||
// Call Basic.Greet(name)
|
|
||||||
window.backend.main.Basic.Greet(name).then((result) => {
|
|
||||||
// Update result with data back from Basic.Greet()
|
|
||||||
document.getElementById("result").innerText = result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
module test
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/wailsapp/wails/v2 v2.0.0-alpha
|
|
||||||
)
|
|
||||||
|
|
||||||
replace github.com/wailsapp/wails/v2 v2.0.0-alpha => {{.WailsDirectory}}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/mac"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
// Create application with options
|
|
||||||
app := NewBasic()
|
|
||||||
|
|
||||||
err := wails.Run(&options.App{
|
|
||||||
Title: "{{.ProjectName}}",
|
|
||||||
Width: 800,
|
|
||||||
Height: 600,
|
|
||||||
DisableResize: true,
|
|
||||||
Mac: &mac.Options{
|
|
||||||
WebviewIsTransparent: true,
|
|
||||||
WindowBackgroundIsTranslucent: true,
|
|
||||||
TitleBar: mac.TitleBarHiddenInset(),
|
|
||||||
Menu: menu.DefaultMacMenu(),
|
|
||||||
},
|
|
||||||
LogLevel: logger.DEBUG,
|
|
||||||
Startup: app.startup,
|
|
||||||
Shutdown: app.shutdown,
|
|
||||||
Bind: []interface{}{
|
|
||||||
app,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Vanilla HTML/JS/CSS",
|
|
||||||
"shortname": "vanilla",
|
|
||||||
"author": "Lea Anthony<lea.anthony@gmail.com>",
|
|
||||||
"description": "A simple template using only HTML/CSS/JS",
|
|
||||||
"helpurl": "https://wails.app/help/templates/vanilla"
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "{{.ProjectName}}",
|
|
||||||
"outputfilename": "{{.BinaryName}}",
|
|
||||||
"html": "frontend/src/index.html",
|
|
||||||
"author": {
|
|
||||||
"name": "{{.AuthorName}}",
|
|
||||||
"email": "{{.AuthorEmail}}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package templates
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/matryer/is"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
|
||||||
|
|
||||||
is2 := is.New(t)
|
|
||||||
templates, err := List()
|
|
||||||
is2.NoErr(err)
|
|
||||||
|
|
||||||
println("Found these templates:")
|
|
||||||
for _, template := range templates {
|
|
||||||
fmt.Printf("%+v\n", template)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestShortname(t *testing.T) {
|
|
||||||
|
|
||||||
is2 := is.New(t)
|
|
||||||
|
|
||||||
template, err := getTemplateByShortname("vanilla")
|
|
||||||
is2.NoErr(err)
|
|
||||||
|
|
||||||
println("Found this template:")
|
|
||||||
fmt.Printf("%+v\n", template)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInstall(t *testing.T) {
|
|
||||||
|
|
||||||
is2 := is.New(t)
|
|
||||||
|
|
||||||
options := &Options{
|
|
||||||
ProjectName: "test",
|
|
||||||
TemplateName: "vanilla",
|
|
||||||
AuthorName: "Lea Anthony",
|
|
||||||
AuthorEmail: "lea.anthony@gmail.com",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := Install(options)
|
|
||||||
is2.NoErr(err)
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
package update
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/shell"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/github"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddSubcommand adds the `init` command for the Wails application
|
|
||||||
func AddSubcommand(app *clir.Cli, w io.Writer, currentVersion string) error {
|
|
||||||
|
|
||||||
command := app.NewSubCommand("update", "Update the Wails CLI")
|
|
||||||
command.LongDescription(`This command allows you to update your version of the Wails CLI.`)
|
|
||||||
|
|
||||||
// Setup flags
|
|
||||||
var prereleaseRequired bool
|
|
||||||
command.BoolFlag("pre", "Update CLI to latest Prerelease", &prereleaseRequired)
|
|
||||||
|
|
||||||
var specificVersion string
|
|
||||||
command.StringFlag("version", "Install a specific version (Overrides other flags) of the CLI", &specificVersion)
|
|
||||||
|
|
||||||
command.Action(func() error {
|
|
||||||
|
|
||||||
// Create logger
|
|
||||||
logger := clilogger.New(w)
|
|
||||||
|
|
||||||
// Print banner
|
|
||||||
app.PrintBanner()
|
|
||||||
logger.Println("Checking for updates...")
|
|
||||||
|
|
||||||
var desiredVersion *github.SemanticVersion
|
|
||||||
var err error
|
|
||||||
var valid bool
|
|
||||||
|
|
||||||
if len(specificVersion) > 0 {
|
|
||||||
// Check if this is a valid version
|
|
||||||
valid, err = github.IsValidTag(specificVersion)
|
|
||||||
if err == nil {
|
|
||||||
if !valid {
|
|
||||||
err = fmt.Errorf("version '%s' is invalid", specificVersion)
|
|
||||||
} else {
|
|
||||||
desiredVersion, err = github.NewSemanticVersion(specificVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if prereleaseRequired {
|
|
||||||
desiredVersion, err = github.GetLatestPreRelease()
|
|
||||||
} else {
|
|
||||||
desiredVersion, err = github.GetLatestStableRelease()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
|
|
||||||
fmt.Println(" Current Version : " + currentVersion)
|
|
||||||
|
|
||||||
if len(specificVersion) > 0 {
|
|
||||||
fmt.Printf(" Desired Version : v%s\n", desiredVersion)
|
|
||||||
} else {
|
|
||||||
if prereleaseRequired {
|
|
||||||
fmt.Printf(" Latest Prerelease : v%s\n", desiredVersion)
|
|
||||||
} else {
|
|
||||||
fmt.Printf(" Latest Release : v%s\n", desiredVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return updateToVersion(logger, desiredVersion, len(specificVersion) > 0, currentVersion)
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateToVersion(logger *clilogger.CLILogger, targetVersion *github.SemanticVersion, force bool, currentVersion string) error {
|
|
||||||
|
|
||||||
var targetVersionString = "v" + targetVersion.String()
|
|
||||||
|
|
||||||
// Early exit
|
|
||||||
if targetVersionString == currentVersion {
|
|
||||||
logger.Println("Looks like you're up to date!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var desiredVersion string
|
|
||||||
|
|
||||||
if !force {
|
|
||||||
|
|
||||||
compareVersion := currentVersion
|
|
||||||
|
|
||||||
currentVersion, err := github.NewSemanticVersion(compareVersion)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var success bool
|
|
||||||
|
|
||||||
// Release -> Pre-Release = Massage current version to prerelease format
|
|
||||||
if targetVersion.IsPreRelease() && currentVersion.IsRelease() {
|
|
||||||
testVersion, err := github.NewSemanticVersion(compareVersion + "-0")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
success, _ = targetVersion.IsGreaterThan(testVersion)
|
|
||||||
}
|
|
||||||
// Pre-Release -> Release = Massage target version to prerelease format
|
|
||||||
if targetVersion.IsRelease() && currentVersion.IsPreRelease() {
|
|
||||||
// We are ok with greater than or equal
|
|
||||||
mainversion := currentVersion.MainVersion()
|
|
||||||
targetVersion, err = github.NewSemanticVersion(targetVersion.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
success, _ = targetVersion.IsGreaterThanOrEqual(mainversion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release -> Release = Standard check
|
|
||||||
if (targetVersion.IsRelease() && currentVersion.IsRelease()) ||
|
|
||||||
(targetVersion.IsPreRelease() && currentVersion.IsPreRelease()) {
|
|
||||||
|
|
||||||
success, _ = targetVersion.IsGreaterThan(currentVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare
|
|
||||||
if !success {
|
|
||||||
logger.Println("Error: The requested version is lower than the current version.")
|
|
||||||
logger.Println("If this is what you really want to do, use `wails update -version %s`", targetVersionString)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
desiredVersion = "v" + targetVersion.String()
|
|
||||||
|
|
||||||
} else {
|
|
||||||
desiredVersion = "v" + targetVersion.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println()
|
|
||||||
logger.Print("Installing Wails CLI " + desiredVersion + "...")
|
|
||||||
|
|
||||||
// Run command in non module directory
|
|
||||||
homeDir, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Cannot find home directory! Please file a bug report!")
|
|
||||||
}
|
|
||||||
|
|
||||||
sout, serr, err := shell.RunCommand(homeDir, "go", "get", "github.com/wailsapp/wails/v2/cmd/wails@"+desiredVersion)
|
|
||||||
if err != nil {
|
|
||||||
logger.Println("Failed.")
|
|
||||||
logger.Println(sout + `\n` + serr)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
logger.Println("Wails CLI updated to " + desiredVersion)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/colour"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/update"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/debug"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/dev"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/doctor"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/generate"
|
|
||||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/initialise"
|
|
||||||
)
|
|
||||||
|
|
||||||
func fatal(message string) {
|
|
||||||
println(message)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func banner(_ *clir.Cli) string {
|
|
||||||
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
|
||||||
|
|
||||||
app.SetBannerFunction(banner)
|
|
||||||
|
|
||||||
build.AddBuildSubcommand(app, os.Stdout)
|
|
||||||
err = initialise.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = debug.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
err = doctor.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dev.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = generate.AddSubcommand(app, os.Stdout)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = update.AddSubcommand(app, os.Stdout, version)
|
|
||||||
if err != nil {
|
|
||||||
fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = app.Run()
|
|
||||||
if err != nil {
|
|
||||||
println("\n\nERROR: " + err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.65"
|
|
||||||
30
v2/go.mod
30
v2/go.mod
@@ -1,30 +0,0 @@
|
|||||||
module github.com/wailsapp/wails/v2
|
|
||||||
|
|
||||||
go 1.16
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/Masterminds/semver v1.5.0
|
|
||||||
github.com/fatih/structtag v1.2.0
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
|
||||||
github.com/gorilla/websocket v1.4.1
|
|
||||||
github.com/imdario/mergo v0.3.11
|
|
||||||
github.com/jackmordaunt/icns v1.0.0
|
|
||||||
github.com/leaanthony/clir v1.0.4
|
|
||||||
github.com/leaanthony/debme v1.1.2
|
|
||||||
github.com/leaanthony/go-ansi-parser v1.0.1
|
|
||||||
github.com/leaanthony/gosod v1.0.1
|
|
||||||
github.com/leaanthony/slicer v1.5.0
|
|
||||||
github.com/matryer/is v1.4.0
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4
|
|
||||||
github.com/pkg/errors v0.9.1
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible // indirect
|
|
||||||
github.com/tdewolff/test v1.0.6 // indirect
|
|
||||||
github.com/wzshiming/ctc v1.2.3
|
|
||||||
github.com/xyproto/xpm v1.2.1
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82
|
|
||||||
nhooyr.io/websocket v1.8.6
|
|
||||||
)
|
|
||||||
129
v2/go.sum
129
v2/go.sum
@@ -1,129 +0,0 @@
|
|||||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
|
||||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
|
||||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
|
||||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
|
||||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
|
||||||
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
|
||||||
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
|
||||||
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
|
||||||
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
|
||||||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
|
||||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
|
||||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
|
||||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
|
||||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
|
||||||
github.com/jackmordaunt/icns v1.0.0 h1:RYSxplerf/l/DUd09AHtITwckkv/mqjVv4DjYdPmAMQ=
|
|
||||||
github.com/jackmordaunt/icns v1.0.0/go.mod h1:7TTQVEuGzVVfOPPlLNHJIkzA6CoV7aH1Dv9dW351oOo=
|
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
|
|
||||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
|
||||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
|
||||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
|
||||||
github.com/leaanthony/debme v1.1.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
|
||||||
github.com/leaanthony/debme v1.1.2 h1:dGeQuj0+xPIlDQzGIjmAU52+yRg85u5pWaaqrdLBjD0=
|
|
||||||
github.com/leaanthony/debme v1.1.2/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
|
|
||||||
github.com/leaanthony/go-ansi-parser v1.0.1 h1:97v6c5kYppVsbScf4r/VZdXyQ21KQIfeQOk2DgKxGG4=
|
|
||||||
github.com/leaanthony/go-ansi-parser v1.0.1/go.mod h1:7arTzgVI47srICYhvgUV4CGd063sGEeoSlych5yeSPM=
|
|
||||||
github.com/leaanthony/gosod v1.0.1 h1:F+4c3DmEBfigi7oAswCV2RpQ+k4DcNbhuCZUGdBHacQ=
|
|
||||||
github.com/leaanthony/gosod v1.0.1/go.mod h1:W8RyeSFBXu7RpIxPGEJfW4moSyGGEjlJMLV25wEbAdU=
|
|
||||||
github.com/leaanthony/slicer v1.5.0 h1:aHYTN8xbCCLxJmkNKiLB6tgcMARl4eWmH9/F+S/0HtY=
|
|
||||||
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
|
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
|
||||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
|
||||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
|
|
||||||
github.com/tdewolff/minify v2.3.6+incompatible/go.mod h1:9Ov578KJUmAWpS6NeZwRZyT56Uf6o3Mcz9CEsg8USYs=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible h1:x05/cnGwIMf4ceLuDMBOdQ1qGniMoxpP46ghf0Qzh38=
|
|
||||||
github.com/tdewolff/parse v2.3.4+incompatible/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
|
|
||||||
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
|
|
||||||
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
|
||||||
github.com/wzshiming/ctc v1.2.3 h1:q+hW3IQNsjIlOFBTGZZZeIXTElFM4grF4spW/errh/c=
|
|
||||||
github.com/wzshiming/ctc v1.2.3/go.mod h1:2tVAtIY7SUyraSk0JxvwmONNPFL4ARavPuEsg5+KA28=
|
|
||||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae h1:tpXvBXC3hpQBDCc9OojJZCQMVRAbT3TTdUMP8WguXkY=
|
|
||||||
github.com/wzshiming/winseq v0.0.0-20200112104235-db357dc107ae/go.mod h1:VTAq37rkGeV+WOybvZwjXiJOicICdpLCN8ifpISjK20=
|
|
||||||
github.com/xyproto/xpm v1.2.1 h1:trdvGjjWBsOOKzBBUPT6JvaIQM3acJEEYfbxN7M96wg=
|
|
||||||
github.com/xyproto/xpm v1.2.1/go.mod h1:cMnesLsD0PBXLgjDfTDEaKr8XyTFsnP1QycSqRw7BiY=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
|
||||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82 h1:shxDsb9Dz27xzk3A0DxP0JuJnZMpKrdg8+E14eiUAX4=
|
|
||||||
golang.org/x/tools v0.0.0-20200902012652-d1954cc86c82/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
|
||||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package wails
|
|
||||||
|
|
||||||
func Init() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package wails
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Init() error {
|
|
||||||
status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call()
|
|
||||||
if status == 0 {
|
|
||||||
return fmt.Errorf("exit status %d: %v %v", status, r, err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// +build debug
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Init initialises the application for a debug environment
|
|
||||||
func (a *App) Init() error {
|
|
||||||
// Indicate debug mode
|
|
||||||
a.debug = true
|
|
||||||
|
|
||||||
if a.appType == "desktop" {
|
|
||||||
// Enable dev tools
|
|
||||||
a.options.DevTools = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set log levels
|
|
||||||
loglevel := flag.String("loglevel", "debug", "Loglevel to use - Trace, Debug, Info, Warning, Error")
|
|
||||||
flag.Parse()
|
|
||||||
if len(*loglevel) > 0 {
|
|
||||||
switch strings.ToLower(*loglevel) {
|
|
||||||
case "trace":
|
|
||||||
a.logger.SetLogLevel(logger.TRACE)
|
|
||||||
case "info":
|
|
||||||
a.logger.SetLogLevel(logger.INFO)
|
|
||||||
case "warning":
|
|
||||||
a.logger.SetLogLevel(logger.WARNING)
|
|
||||||
case "error":
|
|
||||||
a.logger.SetLogLevel(logger.ERROR)
|
|
||||||
default:
|
|
||||||
a.logger.SetLogLevel(logger.DEBUG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// +build !desktop,!hybrid,!server,!dev
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
// This is the default application that will get run if the user compiles using `go build`.
|
|
||||||
// The reason we want to prevent that is that the `wails build` command does a lot of behind
|
|
||||||
// the scenes work such as asset compilation. If we allow `go build`, the state of these assets
|
|
||||||
// will be unknown and the application will not work as expected.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App defines a Wails application structure
|
|
||||||
type App struct {
|
|
||||||
Title string
|
|
||||||
Width int
|
|
||||||
Height int
|
|
||||||
Resizable bool
|
|
||||||
|
|
||||||
// Indicates if the app is running in debug mode
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
logger *logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateApp returns a null application
|
|
||||||
func CreateApp(_ *options.App) (*App, error) {
|
|
||||||
return &App{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
println(`FATAL: This application was built using "go build". This is unsupported. Please compile using "wails build".`)
|
|
||||||
os.Exit(1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
// +build desktop,!server
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/signal"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App defines a Wails application structure
|
|
||||||
type App struct {
|
|
||||||
appType string
|
|
||||||
|
|
||||||
window *ffenestri.Application
|
|
||||||
servicebus *servicebus.ServiceBus
|
|
||||||
logger *logger.Logger
|
|
||||||
signal *signal.Manager
|
|
||||||
options *options.App
|
|
||||||
|
|
||||||
// Subsystems
|
|
||||||
log *subsystem.Log
|
|
||||||
runtime *subsystem.Runtime
|
|
||||||
event *subsystem.Event
|
|
||||||
//binding *subsystem.Binding
|
|
||||||
call *subsystem.Call
|
|
||||||
menu *subsystem.Menu
|
|
||||||
url *subsystem.URL
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
|
||||||
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Indicates if the app is in debug mode
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// This is our binding DB
|
|
||||||
bindings *binding.Bindings
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
loglevelStore *runtime.Store
|
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create App
|
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
|
||||||
|
|
||||||
// Merge default options
|
|
||||||
options.MergeDefaults(appoptions)
|
|
||||||
|
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(appoptions.Logger)
|
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
// Create the menu manager
|
|
||||||
menuManager := menumanager.NewManager()
|
|
||||||
|
|
||||||
// Process the application menu
|
|
||||||
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
|
||||||
|
|
||||||
// Process context menus
|
|
||||||
contextMenus := options.GetContextMenus(appoptions)
|
|
||||||
for _, contextMenu := range contextMenus {
|
|
||||||
menuManager.AddContextMenu(contextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process tray menus
|
|
||||||
trayMenus := options.GetTrayMenus(appoptions)
|
|
||||||
for _, trayMenu := range trayMenus {
|
|
||||||
menuManager.AddTrayMenu(trayMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(appoptions, myLogger, menuManager)
|
|
||||||
|
|
||||||
// Create binding exemptions - Ugly hack. There must be a better way
|
|
||||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
|
||||||
|
|
||||||
result := &App{
|
|
||||||
appType: "desktop",
|
|
||||||
window: window,
|
|
||||||
servicebus: servicebus.New(myLogger),
|
|
||||||
logger: myLogger,
|
|
||||||
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
|
||||||
menuManager: menuManager,
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
result.options = appoptions
|
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
err := result.Init()
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Setup a context
|
|
||||||
var subsystemWaitGroup sync.WaitGroup
|
|
||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
|
||||||
|
|
||||||
// Start the service bus
|
|
||||||
a.servicebus.Debug()
|
|
||||||
err = a.servicebus.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtimesubsystem
|
|
||||||
err = a.runtime.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
|
||||||
|
|
||||||
// Start the logging subsystem
|
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.log = log
|
|
||||||
err = a.log.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the dispatcher
|
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.dispatcher = dispatcher
|
|
||||||
err = dispatcher.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.options.Mac.URLHandlers != nil {
|
|
||||||
// Start the url handler subsystem
|
|
||||||
url, err := subsystem.NewURL(a.servicebus, a.logger, a.options.Mac.URLHandlers)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.url = url
|
|
||||||
err = a.url.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
|
||||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.event = eventsubsystem
|
|
||||||
err = a.event.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the menu subsystem
|
|
||||||
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.menu = menusubsystem
|
|
||||||
err = a.menu.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the call subsystem
|
|
||||||
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = callSubsystem
|
|
||||||
err = a.call.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump bindings as a debug
|
|
||||||
bindingDump, err := a.bindings.ToJSON()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
err = a.window.Run(dispatcher, bindingDump, a.debug)
|
|
||||||
a.logger.Trace("Ffenestri.Run() exited")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close down all the subsystems
|
|
||||||
a.logger.Trace("Cancelling subsystems")
|
|
||||||
cancel()
|
|
||||||
subsystemWaitGroup.Wait()
|
|
||||||
|
|
||||||
a.logger.Trace("Cancelling dispatcher")
|
|
||||||
dispatcher.Close()
|
|
||||||
|
|
||||||
// Close log
|
|
||||||
a.logger.Trace("Stopping log")
|
|
||||||
log.Close()
|
|
||||||
|
|
||||||
a.logger.Trace("Stopping Service bus")
|
|
||||||
err = a.servicebus.Stop()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown callback
|
|
||||||
if a.shutdownCallback != nil {
|
|
||||||
a.shutdownCallback()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
// +build dev
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/bridge"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/signal"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App defines a Wails application structure
|
|
||||||
type App struct {
|
|
||||||
appType string
|
|
||||||
|
|
||||||
servicebus *servicebus.ServiceBus
|
|
||||||
logger *logger.Logger
|
|
||||||
signal *signal.Manager
|
|
||||||
options *options.App
|
|
||||||
|
|
||||||
// Subsystems
|
|
||||||
log *subsystem.Log
|
|
||||||
runtime *subsystem.Runtime
|
|
||||||
event *subsystem.Event
|
|
||||||
//binding *subsystem.Binding
|
|
||||||
call *subsystem.Call
|
|
||||||
menu *subsystem.Menu
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
|
||||||
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Indicates if the app is in debug mode
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// This is our binding DB
|
|
||||||
bindings *binding.Bindings
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
loglevelStore *runtime.Store
|
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
|
|
||||||
// Bridge
|
|
||||||
bridge *bridge.Bridge
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create App
|
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
|
||||||
|
|
||||||
// Merge default options
|
|
||||||
options.MergeDefaults(appoptions)
|
|
||||||
|
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(appoptions.Logger)
|
|
||||||
|
|
||||||
// Create the menu manager
|
|
||||||
menuManager := menumanager.NewManager()
|
|
||||||
|
|
||||||
// Process the application menu
|
|
||||||
menuManager.SetApplicationMenu(options.GetApplicationMenu(appoptions))
|
|
||||||
|
|
||||||
// Process context menus
|
|
||||||
contextMenus := options.GetContextMenus(appoptions)
|
|
||||||
for _, contextMenu := range contextMenus {
|
|
||||||
menuManager.AddContextMenu(contextMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process tray menus
|
|
||||||
trayMenus := options.GetTrayMenus(appoptions)
|
|
||||||
for _, trayMenu := range trayMenus {
|
|
||||||
menuManager.AddTrayMenu(trayMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create binding exemptions - Ugly hack. There must be a better way
|
|
||||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
|
||||||
|
|
||||||
result := &App{
|
|
||||||
appType: "dev",
|
|
||||||
bindings: binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions),
|
|
||||||
logger: myLogger,
|
|
||||||
servicebus: servicebus.New(myLogger),
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
bridge: bridge.NewBridge(myLogger),
|
|
||||||
menuManager: menuManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
result.options = appoptions
|
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
err := result.Init()
|
|
||||||
|
|
||||||
return result, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Setup a context
|
|
||||||
var subsystemWaitGroup sync.WaitGroup
|
|
||||||
parentContext := context.WithValue(context.Background(), "waitgroup", &subsystemWaitGroup)
|
|
||||||
ctx, cancel := context.WithCancel(parentContext)
|
|
||||||
|
|
||||||
// Start the service bus
|
|
||||||
a.servicebus.Debug()
|
|
||||||
err = a.servicebus.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimesubsystem, err := subsystem.NewRuntime(ctx, a.servicebus, a.logger, a.startupCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtimesubsystem
|
|
||||||
err = a.runtime.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
|
||||||
|
|
||||||
// Start the logging subsystem
|
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.log = log
|
|
||||||
err = a.log.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the dispatcher
|
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.dispatcher = dispatcher
|
|
||||||
err = dispatcher.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
|
||||||
eventsubsystem, err := subsystem.NewEvent(ctx, a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.event = eventsubsystem
|
|
||||||
err = a.event.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the menu subsystem
|
|
||||||
menusubsystem, err := subsystem.NewMenu(ctx, a.servicebus, a.logger, a.menuManager)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.menu = menusubsystem
|
|
||||||
err = a.menu.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the call subsystem
|
|
||||||
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = callSubsystem
|
|
||||||
err = a.call.Start()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump bindings as a debug
|
|
||||||
bindingDump, err := a.bindings.ToJSON()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate backend.js
|
|
||||||
a.bindings.GenerateBackendJS()
|
|
||||||
|
|
||||||
// Setup signal handler
|
|
||||||
signalsubsystem, err := signal.NewManager(ctx, cancel, a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.signal = signalsubsystem
|
|
||||||
a.signal.Start()
|
|
||||||
|
|
||||||
err = a.bridge.Run(dispatcher, a.menuManager, bindingDump, a.debug)
|
|
||||||
a.logger.Trace("Bridge.Run() exited")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close down all the subsystems
|
|
||||||
a.logger.Trace("Cancelling subsystems")
|
|
||||||
cancel()
|
|
||||||
subsystemWaitGroup.Wait()
|
|
||||||
|
|
||||||
a.logger.Trace("Cancelling dispatcher")
|
|
||||||
dispatcher.Close()
|
|
||||||
|
|
||||||
// Close log
|
|
||||||
a.logger.Trace("Stopping log")
|
|
||||||
log.Close()
|
|
||||||
|
|
||||||
a.logger.Trace("Stopping Service bus")
|
|
||||||
err = a.servicebus.Stop()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown callback
|
|
||||||
if a.shutdownCallback != nil {
|
|
||||||
a.shutdownCallback()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
// +build !server,!desktop,hybrid
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/ffenestri"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config defines the Application's configuration
|
|
||||||
type Config struct {
|
|
||||||
Title string // Title is the value to be displayed in the title bar
|
|
||||||
Width int // Width is the desired window width
|
|
||||||
Height int // Height is the desired window height
|
|
||||||
DevTools bool // DevTools enables or disables the browser development tools
|
|
||||||
Resizable bool // Resizable when False prevents window resizing
|
|
||||||
ServerEnabled bool // ServerEnabled when True allows remote connections
|
|
||||||
}
|
|
||||||
|
|
||||||
// App defines a Wails application structure
|
|
||||||
type App struct {
|
|
||||||
config Config
|
|
||||||
window *ffenestri.Application
|
|
||||||
webserver *webserver.WebServer
|
|
||||||
binding *subsystem.Binding
|
|
||||||
call *subsystem.Call
|
|
||||||
event *subsystem.Event
|
|
||||||
log *subsystem.Log
|
|
||||||
runtime *subsystem.Runtime
|
|
||||||
|
|
||||||
bindings *binding.Bindings
|
|
||||||
logger *logger.Logger
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
|
||||||
servicebus *servicebus.ServiceBus
|
|
||||||
|
|
||||||
debug bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create App
|
|
||||||
func CreateApp(options *Options) *App {
|
|
||||||
|
|
||||||
// Merge default options
|
|
||||||
options.mergeDefaults()
|
|
||||||
|
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(os.Stdout)
|
|
||||||
myLogger.SetLogLevel(logger.INFO)
|
|
||||||
|
|
||||||
window := ffenestri.NewApplicationWithConfig(&ffenestri.Config{
|
|
||||||
Title: options.Title,
|
|
||||||
Width: options.Width,
|
|
||||||
Height: options.Height,
|
|
||||||
MinWidth: options.MinWidth,
|
|
||||||
MinHeight: options.MinHeight,
|
|
||||||
MaxWidth: options.MaxWidth,
|
|
||||||
MaxHeight: options.MaxHeight,
|
|
||||||
StartHidden: options.StartHidden,
|
|
||||||
DevTools: options.DevTools,
|
|
||||||
|
|
||||||
Resizable: !options.DisableResize,
|
|
||||||
Fullscreen: options.Fullscreen,
|
|
||||||
}, myLogger)
|
|
||||||
|
|
||||||
app := &App{
|
|
||||||
window: window,
|
|
||||||
webserver: webserver.NewWebServer(myLogger),
|
|
||||||
servicebus: servicebus.New(myLogger),
|
|
||||||
logger: myLogger,
|
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise the app
|
|
||||||
app.Init()
|
|
||||||
|
|
||||||
return app
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
|
|
||||||
// Default app options
|
|
||||||
var port = 8080
|
|
||||||
var ip = "localhost"
|
|
||||||
var suppressLogging = false
|
|
||||||
|
|
||||||
// Create CLI
|
|
||||||
cli := clir.NewCli(filepath.Base(os.Args[0]), "Desktop/Server Build", "")
|
|
||||||
|
|
||||||
// Setup flags
|
|
||||||
cli.IntFlag("p", "Port to serve on", &port)
|
|
||||||
cli.StringFlag("i", "IP to serve on", &ip)
|
|
||||||
cli.BoolFlag("q", "Suppress logging", &suppressLogging)
|
|
||||||
|
|
||||||
// Setup main action
|
|
||||||
cli.Action(func() error {
|
|
||||||
|
|
||||||
// Set IP + Port
|
|
||||||
a.webserver.SetPort(port)
|
|
||||||
a.webserver.SetIP(ip)
|
|
||||||
a.webserver.SetBindings(a.bindings)
|
|
||||||
// Log information (if we aren't suppressing it)
|
|
||||||
if !suppressLogging {
|
|
||||||
cli.PrintBanner()
|
|
||||||
a.logger.Info("Running server at %s", a.webserver.URL())
|
|
||||||
}
|
|
||||||
|
|
||||||
a.servicebus.Start()
|
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.log = log
|
|
||||||
a.log.Start()
|
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.dispatcher = dispatcher
|
|
||||||
a.dispatcher.Start()
|
|
||||||
|
|
||||||
// Start the runtime
|
|
||||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtime
|
|
||||||
a.runtime.Start()
|
|
||||||
|
|
||||||
// Start the binding subsystem
|
|
||||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.binding = binding
|
|
||||||
a.binding.Start()
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
|
||||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.event = event
|
|
||||||
a.event.Start()
|
|
||||||
|
|
||||||
// Start the call subsystem
|
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = call
|
|
||||||
a.call.Start()
|
|
||||||
|
|
||||||
// Required so that the WailsInit functions are fired!
|
|
||||||
runtime.GoRuntime().Events.Emit("wails:loaded")
|
|
||||||
|
|
||||||
// Set IP + Port
|
|
||||||
a.webserver.SetPort(port)
|
|
||||||
a.webserver.SetIP(ip)
|
|
||||||
|
|
||||||
// Log information (if we aren't suppressing it)
|
|
||||||
if !suppressLogging {
|
|
||||||
cli.PrintBanner()
|
|
||||||
println("Running server at " + a.webserver.URL())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump bindings as a debug
|
|
||||||
bindingDump, err := a.bindings.ToJSON()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := a.webserver.Start(dispatcher, event); err != nil {
|
|
||||||
a.logger.Error("Webserver failed to start %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
result := a.window.Run(dispatcher, bindingDump)
|
|
||||||
a.servicebus.Stop()
|
|
||||||
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
return cli.Run()
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// +build !debug
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/pkg/logger"
|
|
||||||
|
|
||||||
// Init initialises the application for a production environment
|
|
||||||
func (a *App) Init() error {
|
|
||||||
a.logger.SetLogLevel(logger.ERROR)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
// +build server,!desktop
|
|
||||||
|
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
|
|
||||||
"github.com/leaanthony/clir"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App defines a Wails application structure
|
|
||||||
type App struct {
|
|
||||||
appType string
|
|
||||||
|
|
||||||
binding *subsystem.Binding
|
|
||||||
call *subsystem.Call
|
|
||||||
event *subsystem.Event
|
|
||||||
log *subsystem.Log
|
|
||||||
runtime *subsystem.Runtime
|
|
||||||
|
|
||||||
options *options.App
|
|
||||||
|
|
||||||
bindings *binding.Bindings
|
|
||||||
logger *logger.Logger
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
|
||||||
servicebus *servicebus.ServiceBus
|
|
||||||
webserver *webserver.WebServer
|
|
||||||
|
|
||||||
debug bool
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
loglevelStore *runtime.Store
|
|
||||||
appconfigStore *runtime.Store
|
|
||||||
|
|
||||||
// Startup/Shutdown
|
|
||||||
startupCallback func(*runtime.Runtime)
|
|
||||||
shutdownCallback func()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create App
|
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
|
||||||
|
|
||||||
// Merge default options
|
|
||||||
options.MergeDefaults(appoptions)
|
|
||||||
|
|
||||||
// Set up logger
|
|
||||||
myLogger := logger.New(appoptions.Logger)
|
|
||||||
myLogger.SetLogLevel(appoptions.LogLevel)
|
|
||||||
|
|
||||||
result := &App{
|
|
||||||
appType: "server",
|
|
||||||
bindings: binding.NewBindings(myLogger, options.Bind),
|
|
||||||
logger: myLogger,
|
|
||||||
servicebus: servicebus.New(myLogger),
|
|
||||||
webserver: webserver.NewWebServer(myLogger),
|
|
||||||
startupCallback: appoptions.Startup,
|
|
||||||
shutdownCallback: appoptions.Shutdown,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise app
|
|
||||||
result.Init()
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *App) Run() error {
|
|
||||||
|
|
||||||
// Default app options
|
|
||||||
var port = 8080
|
|
||||||
var ip = "localhost"
|
|
||||||
var supressLogging = false
|
|
||||||
var debugMode = false
|
|
||||||
|
|
||||||
// Create CLI
|
|
||||||
cli := clir.NewCli(filepath.Base(os.Args[0]), "Server Build", "")
|
|
||||||
|
|
||||||
// Setup flags
|
|
||||||
cli.IntFlag("p", "Port to serve on", &port)
|
|
||||||
cli.StringFlag("i", "IP to serve on", &ip)
|
|
||||||
cli.BoolFlag("d", "Debug mode", &debugMode)
|
|
||||||
cli.BoolFlag("q", "Supress logging", &supressLogging)
|
|
||||||
|
|
||||||
// Setup main action
|
|
||||||
cli.Action(func() error {
|
|
||||||
|
|
||||||
// Set IP + Port
|
|
||||||
a.webserver.SetPort(port)
|
|
||||||
a.webserver.SetIP(ip)
|
|
||||||
a.webserver.SetBindings(a.bindings)
|
|
||||||
// Log information (if we aren't supressing it)
|
|
||||||
if !supressLogging {
|
|
||||||
cli.PrintBanner()
|
|
||||||
a.logger.Info("Running server at %s", a.webserver.URL())
|
|
||||||
}
|
|
||||||
|
|
||||||
if debugMode {
|
|
||||||
a.servicebus.Debug()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the runtime
|
|
||||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.runtime = runtime
|
|
||||||
a.runtime.Start()
|
|
||||||
|
|
||||||
// Application Stores
|
|
||||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
|
|
||||||
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
|
|
||||||
|
|
||||||
a.servicebus.Start()
|
|
||||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.log = log
|
|
||||||
a.log.Start()
|
|
||||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.dispatcher = dispatcher
|
|
||||||
a.dispatcher.Start()
|
|
||||||
|
|
||||||
// Start the binding subsystem
|
|
||||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.binding = binding
|
|
||||||
a.binding.Start()
|
|
||||||
|
|
||||||
// Start the eventing subsystem
|
|
||||||
event, err := subsystem.NewEvent(a.servicebus, a.logger)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.event = event
|
|
||||||
a.event.Start()
|
|
||||||
|
|
||||||
// Start the call subsystem
|
|
||||||
call, err := subsystem.NewCall(a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.call = call
|
|
||||||
a.call.Start()
|
|
||||||
|
|
||||||
// Required so that the WailsInit functions are fired!
|
|
||||||
runtime.GoRuntime().Events.Emit("wails:loaded")
|
|
||||||
|
|
||||||
if err := a.webserver.Start(dispatcher, event); err != nil {
|
|
||||||
a.logger.Error("Webserver failed to start %s", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return cli.Run()
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
package assetdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AssetDB is a database for assets encoded as byte slices
|
|
||||||
type AssetDB struct {
|
|
||||||
db map[string][]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAssetDB creates a new AssetDB and initialises a blank db
|
|
||||||
func NewAssetDB() *AssetDB {
|
|
||||||
return &AssetDB{
|
|
||||||
db: make(map[string][]byte),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddAsset saves the given byte slice under the given name
|
|
||||||
func (a *AssetDB) AddAsset(name string, data []byte) {
|
|
||||||
a.db[name] = data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the named asset
|
|
||||||
func (a *AssetDB) Remove(name string) {
|
|
||||||
delete(a.db, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asset retrieves the byte slice for the given name
|
|
||||||
func (a *AssetDB) Read(name string) ([]byte, error) {
|
|
||||||
result := a.db[name]
|
|
||||||
if result == nil {
|
|
||||||
return nil, fmt.Errorf("asset '%s' not found", name)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AssetAsString returns the asset as a string.
|
|
||||||
// It also returns a boolean indicating whether the asset existed or not.
|
|
||||||
func (a *AssetDB) String(name string) (string, error) {
|
|
||||||
asset, err := a.Read(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return *(*string)(unsafe.Pointer(&asset)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AssetDB) Dump() {
|
|
||||||
fmt.Printf("Assets:\n")
|
|
||||||
for k, _ := range a.db {
|
|
||||||
fmt.Println(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize converts the entire database to a file that when compiled will
|
|
||||||
// reconstruct the AssetDB during init()
|
|
||||||
// name: name of the asset.AssetDB instance
|
|
||||||
// pkg: package name placed at the top of the file
|
|
||||||
func (a *AssetDB) Serialize(name, pkg string) string {
|
|
||||||
var cdata strings.Builder
|
|
||||||
// Set buffer size to 4k
|
|
||||||
cdata.Grow(4096)
|
|
||||||
|
|
||||||
// Write header
|
|
||||||
header := `// DO NOT EDIT - Generated automatically
|
|
||||||
package %s
|
|
||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/internal/assetdb"
|
|
||||||
|
|
||||||
var (
|
|
||||||
%s *assetdb.AssetDB = assetdb.NewAssetDB()
|
|
||||||
)
|
|
||||||
|
|
||||||
// AssetsDB is a clean interface to the assetdb.AssetDB struct
|
|
||||||
type AssetsDB interface {
|
|
||||||
Read(string) ([]byte, error)
|
|
||||||
String(string) (string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assets returns the asset database
|
|
||||||
func Assets() AssetsDB {
|
|
||||||
return %s
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
`
|
|
||||||
cdata.WriteString(fmt.Sprintf(header, pkg, name, name))
|
|
||||||
|
|
||||||
for aname, bytes := range a.db {
|
|
||||||
cdata.WriteString(fmt.Sprintf("\t%s.AddAsset(\"%s\", []byte{",
|
|
||||||
name,
|
|
||||||
aname))
|
|
||||||
|
|
||||||
l := len(bytes)
|
|
||||||
if l == 0 {
|
|
||||||
cdata.WriteString("0x00})\n")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert each byte to hex
|
|
||||||
for _, b := range bytes[:l-1] {
|
|
||||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
|
||||||
}
|
|
||||||
cdata.WriteString(fmt.Sprintf("0x%x})\n", bytes[l-1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
cdata.WriteString(`}`)
|
|
||||||
|
|
||||||
return cdata.String()
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package assetdb
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
import "github.com/matryer/is"
|
|
||||||
|
|
||||||
func TestExistsAsBytes(t *testing.T) {
|
|
||||||
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("hello", helloworld)
|
|
||||||
|
|
||||||
result, err := db.Read("hello")
|
|
||||||
|
|
||||||
is.True(err == nil)
|
|
||||||
is.Equal(result, helloworld)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotExistsAsBytes(t *testing.T) {
|
|
||||||
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("hello4", helloworld)
|
|
||||||
|
|
||||||
result, err := db.Read("hello")
|
|
||||||
|
|
||||||
is.True(err != nil)
|
|
||||||
is.True(result == nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExistsAsString(t *testing.T) {
|
|
||||||
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("hello", helloworld)
|
|
||||||
|
|
||||||
result, err := db.String("hello")
|
|
||||||
|
|
||||||
// Ensure it exists
|
|
||||||
is.True(err == nil)
|
|
||||||
|
|
||||||
// Ensure the string is the same as the byte slice
|
|
||||||
is.Equal(result, "Hello, World!")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotExistsAsString(t *testing.T) {
|
|
||||||
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("hello", helloworld)
|
|
||||||
|
|
||||||
result, err := db.String("help")
|
|
||||||
|
|
||||||
// Ensure it doesn't exist
|
|
||||||
is.True(err != nil)
|
|
||||||
|
|
||||||
// Ensure the string is blank
|
|
||||||
is.Equal(result, "")
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
// +build !desktop
|
|
||||||
package assetdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errWhence = errors.New("Seek: invalid whence")
|
|
||||||
var errOffset = errors.New("Seek: invalid offset")
|
|
||||||
|
|
||||||
// Open implements the http.FileSystem interface for the AssetDB
|
|
||||||
func (a *AssetDB) Open(name string) (http.File, error) {
|
|
||||||
if name == "/" || name == "" {
|
|
||||||
return &Entry{name: "/", dir: true, db: a}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if data, ok := a.db[name]; ok {
|
|
||||||
return &Entry{name: name, data: data, size: len(data)}, nil
|
|
||||||
} else {
|
|
||||||
for n, _ := range a.db {
|
|
||||||
if strings.HasPrefix(n, name+"/") {
|
|
||||||
return &Entry{name: name, db: a, dir: true}, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Entry{}, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
// readdir returns the directory entries for the requested directory
|
|
||||||
func (a *AssetDB) readdir(name string) ([]os.FileInfo, error) {
|
|
||||||
dir := name
|
|
||||||
var ents []string
|
|
||||||
|
|
||||||
fim := make(map[string]os.FileInfo)
|
|
||||||
for fn, data := range a.db {
|
|
||||||
if strings.HasPrefix(fn, dir) {
|
|
||||||
pieces := strings.Split(fn[len(dir)+1:], "/")
|
|
||||||
if len(pieces) == 1 {
|
|
||||||
fim[pieces[0]] = FI{name: pieces[0], dir: false, size: len(data)}
|
|
||||||
ents = append(ents, pieces[0])
|
|
||||||
} else {
|
|
||||||
fim[pieces[0]] = FI{name: pieces[0], dir: true, size: -1}
|
|
||||||
ents = append(ents, pieces[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ents) == 0 {
|
|
||||||
return nil, os.ErrNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(ents)
|
|
||||||
var list []os.FileInfo
|
|
||||||
for _, dir := range ents {
|
|
||||||
list = append(list, fim[dir])
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry implements the http.File interface to allow for
|
|
||||||
// use in the http.FileSystem implementation of AssetDB
|
|
||||||
type Entry struct {
|
|
||||||
name string
|
|
||||||
data []byte
|
|
||||||
dir bool
|
|
||||||
size int
|
|
||||||
db *AssetDB
|
|
||||||
off int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close is a noop
|
|
||||||
func (e Entry) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read fills the slice provided returning how many bytes were written
|
|
||||||
// and any errors encountered
|
|
||||||
func (e *Entry) Read(p []byte) (n int, err error) {
|
|
||||||
if e.off >= e.size {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
numout := len(p)
|
|
||||||
if numout > e.size {
|
|
||||||
numout = e.size
|
|
||||||
}
|
|
||||||
for i := 0; i < numout; i++ {
|
|
||||||
p[i] = e.data[e.off+i]
|
|
||||||
}
|
|
||||||
e.off += numout
|
|
||||||
n = int(numout)
|
|
||||||
err = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seek seeks to the specified offset from the location specified by whence
|
|
||||||
func (e *Entry) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
switch whence {
|
|
||||||
default:
|
|
||||||
return 0, errWhence
|
|
||||||
case io.SeekStart:
|
|
||||||
offset += 0
|
|
||||||
case io.SeekCurrent:
|
|
||||||
offset += int64(e.off)
|
|
||||||
case io.SeekEnd:
|
|
||||||
offset += int64(e.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
if offset < 0 {
|
|
||||||
return 0, errOffset
|
|
||||||
}
|
|
||||||
e.off = int(offset)
|
|
||||||
return offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Readdir returns the directory entries inside this entry if it is a directory
|
|
||||||
func (e Entry) Readdir(count int) ([]os.FileInfo, error) {
|
|
||||||
ents := []os.FileInfo{}
|
|
||||||
if !e.dir {
|
|
||||||
return ents, errors.New("Not a directory")
|
|
||||||
}
|
|
||||||
return e.db.readdir(e.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat returns information about this directory entry
|
|
||||||
func (e Entry) Stat() (os.FileInfo, error) {
|
|
||||||
return FI{e.name, e.size, e.dir}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FI is the AssetDB implementation of os.FileInfo.
|
|
||||||
type FI struct {
|
|
||||||
name string
|
|
||||||
size int
|
|
||||||
dir bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsDir returns true if this is a directory
|
|
||||||
func (fi FI) IsDir() bool {
|
|
||||||
return fi.dir
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModTime always returns now
|
|
||||||
func (fi FI) ModTime() time.Time {
|
|
||||||
return time.Time{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode returns the file as readonly and directories
|
|
||||||
// as world writeable and executable
|
|
||||||
func (fi FI) Mode() os.FileMode {
|
|
||||||
if fi.IsDir() {
|
|
||||||
return 0755 | os.ModeDir
|
|
||||||
}
|
|
||||||
return 0444
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the name of this object without
|
|
||||||
// any leading slashes
|
|
||||||
func (fi FI) Name() string {
|
|
||||||
return path.Base(fi.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the size of this item
|
|
||||||
func (fi FI) Size() int64 {
|
|
||||||
return int64(fi.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sys returns nil
|
|
||||||
func (fi FI) Sys() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package assetdb
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/matryer/is"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestOpenLeadingSlash(t *testing.T) {
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("/hello", helloworld)
|
|
||||||
|
|
||||||
file, err := db.Open("/hello")
|
|
||||||
// Ensure it does exist
|
|
||||||
is.True(err == nil)
|
|
||||||
|
|
||||||
buff := make([]byte, len(helloworld))
|
|
||||||
n, err := file.Read(buff)
|
|
||||||
fmt.Printf("Error %v\n", err)
|
|
||||||
is.True(err == nil)
|
|
||||||
is.Equal(n, len(helloworld))
|
|
||||||
result := string(buff)
|
|
||||||
|
|
||||||
// Ensure the string is blank
|
|
||||||
is.Equal(result, string(helloworld))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOpen(t *testing.T) {
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("/hello", helloworld)
|
|
||||||
|
|
||||||
file, err := db.Open("/hello")
|
|
||||||
|
|
||||||
// Ensure it does exist
|
|
||||||
is.True(err == nil)
|
|
||||||
|
|
||||||
buff := make([]byte, len(helloworld))
|
|
||||||
n, err := file.Read(buff)
|
|
||||||
is.True(err == nil)
|
|
||||||
is.Equal(n, len(helloworld))
|
|
||||||
result := string(buff)
|
|
||||||
|
|
||||||
// Ensure the string is blank
|
|
||||||
is.Equal(result, string(helloworld))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaddir(t *testing.T) {
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("/hello", helloworld)
|
|
||||||
db.AddAsset("/directory/hello", helloworld)
|
|
||||||
db.AddAsset("/directory/subdirectory/hello", helloworld)
|
|
||||||
|
|
||||||
dir, err := db.Open("/doesntexist")
|
|
||||||
is.True(err == os.ErrNotExist)
|
|
||||||
ents, err := dir.Readdir(-1)
|
|
||||||
is.Equal([]os.FileInfo{}, ents)
|
|
||||||
|
|
||||||
dir, err = db.Open("/")
|
|
||||||
is.True(dir != nil)
|
|
||||||
is.True(err == nil)
|
|
||||||
ents, err = dir.Readdir(-1)
|
|
||||||
is.True(err == nil)
|
|
||||||
is.Equal(3, len(ents))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReaddirSubdirectory(t *testing.T) {
|
|
||||||
is := is.New(t)
|
|
||||||
|
|
||||||
var helloworld = []byte{72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33}
|
|
||||||
|
|
||||||
db := NewAssetDB()
|
|
||||||
db.AddAsset("/hello", helloworld)
|
|
||||||
db.AddAsset("/directory/hello", helloworld)
|
|
||||||
db.AddAsset("/directory/subdirectory/hello", helloworld)
|
|
||||||
|
|
||||||
expected := []os.FileInfo{
|
|
||||||
FI{name: "hello", dir: false, size: len(helloworld)},
|
|
||||||
FI{name: "subdirectory", dir: true, size: -1},
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := db.Open("/directory")
|
|
||||||
is.True(dir != nil)
|
|
||||||
is.True(err == nil)
|
|
||||||
ents, err := dir.Readdir(-1)
|
|
||||||
is.Equal(expected, ents)
|
|
||||||
|
|
||||||
// Check sub-subdirectory
|
|
||||||
dir, err = db.Open("/directory/subdirectory")
|
|
||||||
is.True(dir != nil)
|
|
||||||
is.True(err == nil)
|
|
||||||
ents, err = dir.Readdir(-1)
|
|
||||||
is.True(err == nil)
|
|
||||||
is.Equal([]os.FileInfo{FI{name: "hello", size: len(helloworld)}}, ents)
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package bind
|
|
||||||
|
|
||||||
func IsStructPointer(value interface{}) bool {
|
|
||||||
switch t := value.(type) {
|
|
||||||
default:
|
|
||||||
println(t)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "backend",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Package to wrap backend method calls",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC"
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Bindings struct {
|
|
||||||
db *DB
|
|
||||||
logger logger.CustomLogger
|
|
||||||
exemptions slicer.StringSlicer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBindings returns a new Bindings object
|
|
||||||
func NewBindings(logger *logger.Logger, structPointersToBind []interface{}, exemptions []interface{}) *Bindings {
|
|
||||||
result := &Bindings{
|
|
||||||
db: newDB(),
|
|
||||||
logger: logger.CustomLogger("Bindings"),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, exemption := range exemptions {
|
|
||||||
if exemptions == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(exemption).Pointer()).Name()
|
|
||||||
// Yuk yuk yuk! Is there a better way?
|
|
||||||
name = strings.TrimSuffix(name, "-fm")
|
|
||||||
result.exemptions.Add(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the structs to bind
|
|
||||||
for _, ptr := range structPointersToBind {
|
|
||||||
err := result.Add(ptr)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("Error during binding: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the given struct methods to the Bindings
|
|
||||||
func (b *Bindings) Add(structPtr interface{}) error {
|
|
||||||
|
|
||||||
methods, err := b.getMethods(structPtr)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot bind value to app: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, method := range methods {
|
|
||||||
splitName := strings.Split(method.Name, ".")
|
|
||||||
packageName := splitName[0]
|
|
||||||
structName := splitName[1]
|
|
||||||
methodName := splitName[2]
|
|
||||||
|
|
||||||
// Add it as a regular method
|
|
||||||
b.db.AddMethod(packageName, structName, methodName, method)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bindings) DB() *DB {
|
|
||||||
return b.db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bindings) ToJSON() (string, error) {
|
|
||||||
return b.db.ToJSON()
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BoundMethod defines all the data related to a Go method that is
|
|
||||||
// bound to the Wails application
|
|
||||||
type BoundMethod struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Inputs []*Parameter `json:"inputs,omitempty"`
|
|
||||||
Outputs []*Parameter `json:"outputs,omitempty"`
|
|
||||||
Comments string `json:"comments,omitempty"`
|
|
||||||
Method reflect.Value `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputCount returns the number of inputs this bound method has
|
|
||||||
func (b *BoundMethod) InputCount() int {
|
|
||||||
return len(b.Inputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OutputCount returns the number of outputs this bound method has
|
|
||||||
func (b *BoundMethod) OutputCount() int {
|
|
||||||
return len(b.Outputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseArgs method converts the input json into the types expected by the method
|
|
||||||
func (b *BoundMethod) ParseArgs(args []json.RawMessage) ([]interface{}, error) {
|
|
||||||
|
|
||||||
result := make([]interface{}, b.InputCount())
|
|
||||||
if len(args) != b.InputCount() {
|
|
||||||
return nil, fmt.Errorf("received %d arguments to method '%s', expected %d", len(args), b.Name, b.InputCount())
|
|
||||||
}
|
|
||||||
for index, arg := range args {
|
|
||||||
typ := b.Inputs[index].reflectType
|
|
||||||
inputValue := reflect.New(typ).Interface()
|
|
||||||
err := json.Unmarshal(arg, inputValue)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if inputValue == nil {
|
|
||||||
result[index] = reflect.Zero(typ).Interface()
|
|
||||||
} else {
|
|
||||||
result[index] = reflect.ValueOf(inputValue).Elem().Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call will attempt to call this bound method with the given args
|
|
||||||
func (b *BoundMethod) Call(args []interface{}) (interface{}, error) {
|
|
||||||
// Check inputs
|
|
||||||
expectedInputLength := len(b.Inputs)
|
|
||||||
actualInputLength := len(args)
|
|
||||||
if expectedInputLength != actualInputLength {
|
|
||||||
return nil, fmt.Errorf("%s takes %d inputs. Received %d", b.Name, expectedInputLength, actualInputLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convert inputs to reflect values **/
|
|
||||||
|
|
||||||
// Create slice for the input arguments to the method call
|
|
||||||
callArgs := make([]reflect.Value, expectedInputLength)
|
|
||||||
|
|
||||||
// Iterate over given arguments
|
|
||||||
for index, arg := range args {
|
|
||||||
// Save the converted argument
|
|
||||||
callArgs[index] = reflect.ValueOf(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the call
|
|
||||||
callResults := b.Method.Call(callArgs)
|
|
||||||
|
|
||||||
//** Check results **//
|
|
||||||
var returnValue interface{}
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch b.OutputCount() {
|
|
||||||
case 1:
|
|
||||||
// Loop over results and determine if the result
|
|
||||||
// is an error or not
|
|
||||||
for _, result := range callResults {
|
|
||||||
interfac := result.Interface()
|
|
||||||
temp, ok := interfac.(error)
|
|
||||||
if ok {
|
|
||||||
err = temp
|
|
||||||
} else {
|
|
||||||
returnValue = interfac
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
returnValue = callResults[0].Interface()
|
|
||||||
if temp, ok := callResults[1].Interface().(error); ok {
|
|
||||||
err = temp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnValue, err
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DB is our database of method bindings
|
|
||||||
type DB struct {
|
|
||||||
// map[packagename] -> map[structname] -> map[methodname]*method
|
|
||||||
store map[string]map[string]map[string]*BoundMethod
|
|
||||||
|
|
||||||
// This uses fully qualified method names as a shortcut for store traversal.
|
|
||||||
// It used for performance gains at runtime
|
|
||||||
methodMap map[string]*BoundMethod
|
|
||||||
|
|
||||||
// Lock to ensure sync access to the data
|
|
||||||
lock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDB() *DB {
|
|
||||||
return &DB{
|
|
||||||
store: make(map[string]map[string]map[string]*BoundMethod),
|
|
||||||
methodMap: make(map[string]*BoundMethod),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMethodFromStore returns the method for the given package/struct/method names
|
|
||||||
// nil is returned if any one of those does not exist
|
|
||||||
func (d *DB) GetMethodFromStore(packageName string, structName string, methodName string) *BoundMethod {
|
|
||||||
|
|
||||||
// Lock the db whilst processing and unlock on return
|
|
||||||
d.lock.RLock()
|
|
||||||
defer d.lock.RUnlock()
|
|
||||||
|
|
||||||
structMap, exists := d.store[packageName]
|
|
||||||
if !exists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
methodMap, exists := structMap[structName]
|
|
||||||
if !exists {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return methodMap[methodName]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMethod returns the method for the given qualified method name
|
|
||||||
// qualifiedMethodName is "packagename.structname.methodname"
|
|
||||||
func (d *DB) GetMethod(qualifiedMethodName string) *BoundMethod {
|
|
||||||
|
|
||||||
// Lock the db whilst processing and unlock on return
|
|
||||||
d.lock.RLock()
|
|
||||||
defer d.lock.RUnlock()
|
|
||||||
|
|
||||||
return d.methodMap[qualifiedMethodName]
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMethod adds the given method definition to the db using the given qualified path: packageName.structName.methodName
|
|
||||||
func (d *DB) AddMethod(packageName string, structName string, methodName string, methodDefinition *BoundMethod) {
|
|
||||||
|
|
||||||
// TODO: Validate inputs?
|
|
||||||
|
|
||||||
// Lock the db whilst processing and unlock on return
|
|
||||||
d.lock.Lock()
|
|
||||||
defer d.lock.Unlock()
|
|
||||||
|
|
||||||
// Get the map associated with the package name
|
|
||||||
structMap, exists := d.store[packageName]
|
|
||||||
if !exists {
|
|
||||||
// Create a new map for this packagename
|
|
||||||
d.store[packageName] = make(map[string]map[string]*BoundMethod)
|
|
||||||
structMap = d.store[packageName]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the map associated with the struct name
|
|
||||||
methodMap, exists := structMap[structName]
|
|
||||||
if !exists {
|
|
||||||
// Create a new map for this packagename
|
|
||||||
structMap[structName] = make(map[string]*BoundMethod)
|
|
||||||
methodMap = structMap[structName]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the method definition
|
|
||||||
methodMap[methodName] = methodDefinition
|
|
||||||
|
|
||||||
// Store in the methodMap
|
|
||||||
key := packageName + "." + structName + "." + methodName
|
|
||||||
d.methodMap[key] = methodDefinition
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToJSON converts the method map to JSON
|
|
||||||
func (d *DB) ToJSON() (string, error) {
|
|
||||||
|
|
||||||
// Lock the db whilst processing and unlock on return
|
|
||||||
d.lock.RLock()
|
|
||||||
defer d.lock.RUnlock()
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(&d.store)
|
|
||||||
|
|
||||||
// Return zero copy string as this string will be read only
|
|
||||||
return *(*string)(unsafe.Pointer(&bytes)), err
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/fs"
|
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed assets/package.json
|
|
||||||
var packageJSON []byte
|
|
||||||
|
|
||||||
func (b *Bindings) GenerateBackendJS() {
|
|
||||||
|
|
||||||
store := b.db.store
|
|
||||||
var output bytes.Buffer
|
|
||||||
|
|
||||||
output.WriteString(`// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
const backend = {`)
|
|
||||||
output.WriteString("\n")
|
|
||||||
|
|
||||||
var sortedPackageNames slicer.StringSlicer
|
|
||||||
for packageName := range store {
|
|
||||||
sortedPackageNames.Add(packageName)
|
|
||||||
}
|
|
||||||
sortedPackageNames.Sort()
|
|
||||||
sortedPackageNames.Each(func(packageName string) {
|
|
||||||
packages := store[packageName]
|
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", packageName))
|
|
||||||
output.WriteString("\n")
|
|
||||||
var sortedStructNames slicer.StringSlicer
|
|
||||||
for structName := range packages {
|
|
||||||
sortedStructNames.Add(structName)
|
|
||||||
}
|
|
||||||
sortedStructNames.Sort()
|
|
||||||
|
|
||||||
sortedStructNames.Each(func(structName string) {
|
|
||||||
structs := packages[structName]
|
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": {", structName))
|
|
||||||
output.WriteString("\n")
|
|
||||||
|
|
||||||
var sortedMethodNames slicer.StringSlicer
|
|
||||||
for methodName := range structs {
|
|
||||||
sortedMethodNames.Add(methodName)
|
|
||||||
}
|
|
||||||
sortedMethodNames.Sort()
|
|
||||||
|
|
||||||
sortedMethodNames.Each(func(methodName string) {
|
|
||||||
methodDetails := structs[methodName]
|
|
||||||
output.WriteString(" /**\n")
|
|
||||||
output.WriteString(" * " + methodName + "\n")
|
|
||||||
var args slicer.StringSlicer
|
|
||||||
for count, input := range methodDetails.Inputs {
|
|
||||||
arg := fmt.Sprintf("arg%d", count+1)
|
|
||||||
args.Add(arg)
|
|
||||||
output.WriteString(fmt.Sprintf(" * @param {%s} %s - Go Type: %s\n", goTypeToJSDocType(input.TypeName), arg, input.TypeName))
|
|
||||||
}
|
|
||||||
returnType := "Promise"
|
|
||||||
returnTypeDetails := ""
|
|
||||||
if methodDetails.OutputCount() > 0 {
|
|
||||||
firstType := goTypeToJSDocType(methodDetails.Outputs[0].TypeName)
|
|
||||||
returnType += "<" + firstType
|
|
||||||
if methodDetails.OutputCount() == 2 {
|
|
||||||
secondType := goTypeToJSDocType(methodDetails.Outputs[1].TypeName)
|
|
||||||
returnType += "|" + secondType
|
|
||||||
}
|
|
||||||
returnType += ">"
|
|
||||||
returnTypeDetails = " - Go Type: " + methodDetails.Outputs[0].TypeName
|
|
||||||
} else {
|
|
||||||
returnType = "Promise<void>"
|
|
||||||
}
|
|
||||||
output.WriteString(" * @returns {" + returnType + "} " + returnTypeDetails + "\n")
|
|
||||||
output.WriteString(" */\n")
|
|
||||||
argsString := args.Join(", ")
|
|
||||||
output.WriteString(fmt.Sprintf(" \"%s\": (%s) => {", methodName, argsString))
|
|
||||||
output.WriteString("\n")
|
|
||||||
output.WriteString(fmt.Sprintf(" return window.backend.%s.%s.%s(%s);", packageName, structName, methodName, argsString))
|
|
||||||
output.WriteString("\n")
|
|
||||||
output.WriteString(fmt.Sprintf(" },"))
|
|
||||||
output.WriteString("\n")
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }"))
|
|
||||||
output.WriteString("\n")
|
|
||||||
})
|
|
||||||
|
|
||||||
output.WriteString(fmt.Sprintf(" }\n"))
|
|
||||||
output.WriteString("\n")
|
|
||||||
})
|
|
||||||
|
|
||||||
output.WriteString(`};
|
|
||||||
export default backend;`)
|
|
||||||
output.WriteString("\n")
|
|
||||||
|
|
||||||
// TODO: Make this configurable in wails.json
|
|
||||||
dirname, err := fs.RelativeToCwd("frontend/src/backend")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !fs.DirExists(dirname) {
|
|
||||||
err := fs.MkDirs(dirname)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packageJsonFile := filepath.Join(dirname, "package.json")
|
|
||||||
if !fs.FileExists(packageJsonFile) {
|
|
||||||
err := os.WriteFile(packageJsonFile, packageJSON, 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Join(dirname, "index.js")
|
|
||||||
err = os.WriteFile(filename, output.Bytes(), 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func goTypeToJSDocType(input string) string {
|
|
||||||
switch true {
|
|
||||||
case input == "string":
|
|
||||||
return "string"
|
|
||||||
case input == "error":
|
|
||||||
return "Error"
|
|
||||||
case
|
|
||||||
strings.HasPrefix(input, "int"),
|
|
||||||
strings.HasPrefix(input, "uint"),
|
|
||||||
strings.HasPrefix(input, "float"):
|
|
||||||
return "number"
|
|
||||||
case input == "bool":
|
|
||||||
return "boolean"
|
|
||||||
case input == "[]byte":
|
|
||||||
return "string"
|
|
||||||
case strings.HasPrefix(input, "[]"):
|
|
||||||
arrayType := goTypeToJSDocType(input[2:])
|
|
||||||
return "Array.<" + arrayType + ">"
|
|
||||||
default:
|
|
||||||
return "any"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_goTypeToJSDocType(t *testing.T) {
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "string",
|
|
||||||
input: "string",
|
|
||||||
want: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "error",
|
|
||||||
input: "error",
|
|
||||||
want: "Error",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int",
|
|
||||||
input: "int",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "int32",
|
|
||||||
input: "int32",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint",
|
|
||||||
input: "uint",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "uint32",
|
|
||||||
input: "uint32",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "float32",
|
|
||||||
input: "float32",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "float64",
|
|
||||||
input: "float64",
|
|
||||||
want: "number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bool",
|
|
||||||
input: "bool",
|
|
||||||
want: "boolean",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "[]byte",
|
|
||||||
input: "[]byte",
|
|
||||||
want: "string",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "[]int",
|
|
||||||
input: "[]int",
|
|
||||||
want: "Array.<number>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "[]bool",
|
|
||||||
input: "[]bool",
|
|
||||||
want: "Array.<boolean>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "anything else",
|
|
||||||
input: "foo",
|
|
||||||
want: "any",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if got := goTypeToJSDocType(tt.input); got != tt.want {
|
|
||||||
t.Errorf("goTypeToJSDocType() = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
// Parameter defines a Go method parameter
|
|
||||||
type Parameter struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
TypeName string `json:"type"`
|
|
||||||
reflectType reflect.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
func newParameter(Name string, Type reflect.Type) *Parameter {
|
|
||||||
return &Parameter{
|
|
||||||
Name: Name,
|
|
||||||
TypeName: Type.String(),
|
|
||||||
reflectType: Type,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsType returns true if the given
|
|
||||||
func (p *Parameter) IsType(typename string) bool {
|
|
||||||
return p.TypeName == typename
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsError returns true if the parameter type is an error
|
|
||||||
func (p *Parameter) IsError() bool {
|
|
||||||
return p.IsType("error")
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
package binding
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a
|
|
||||||
// pointer to a struct
|
|
||||||
func isStructPtr(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Ptr &&
|
|
||||||
reflect.ValueOf(value).Elem().Kind() == reflect.Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
// isFunction returns true if the given value is a function
|
|
||||||
func isFunction(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Func
|
|
||||||
}
|
|
||||||
|
|
||||||
// isStructPtr returns true if the value given is a struct
|
|
||||||
func isStruct(value interface{}) bool {
|
|
||||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bindings) getMethods(value interface{}) ([]*BoundMethod, error) {
|
|
||||||
|
|
||||||
// Create result placeholder
|
|
||||||
var result []*BoundMethod
|
|
||||||
|
|
||||||
// Check type
|
|
||||||
if !isStructPtr(value) {
|
|
||||||
|
|
||||||
if isStruct(value) {
|
|
||||||
name := reflect.ValueOf(value).Type().Name()
|
|
||||||
return nil, fmt.Errorf("%s is a struct, not a pointer to a struct", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isFunction(value) {
|
|
||||||
name := runtime.FuncForPC(reflect.ValueOf(value).Pointer()).Name()
|
|
||||||
return nil, fmt.Errorf("%s is a function, not a pointer to a struct. Wails v2 has deprecated the binding of functions. Please wrap your functions up in a struct and bind a pointer to that struct.", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a pointer to a struct.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Struct
|
|
||||||
structType := reflect.TypeOf(value)
|
|
||||||
structValue := reflect.ValueOf(value)
|
|
||||||
baseName := structType.String()[1:]
|
|
||||||
|
|
||||||
// Process Methods
|
|
||||||
for i := 0; i < structType.NumMethod(); i++ {
|
|
||||||
methodDef := structType.Method(i)
|
|
||||||
methodName := methodDef.Name
|
|
||||||
fullMethodName := baseName + "." + methodName
|
|
||||||
method := structValue.MethodByName(methodName)
|
|
||||||
|
|
||||||
methodReflectName := runtime.FuncForPC(methodDef.Func.Pointer()).Name()
|
|
||||||
if b.exemptions.Contains(methodReflectName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new method
|
|
||||||
boundMethod := &BoundMethod{
|
|
||||||
Name: fullMethodName,
|
|
||||||
Inputs: nil,
|
|
||||||
Outputs: nil,
|
|
||||||
Comments: "",
|
|
||||||
Method: method,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate inputs
|
|
||||||
methodType := method.Type()
|
|
||||||
inputParamCount := methodType.NumIn()
|
|
||||||
var inputs []*Parameter
|
|
||||||
for inputIndex := 0; inputIndex < inputParamCount; inputIndex++ {
|
|
||||||
input := methodType.In(inputIndex)
|
|
||||||
thisParam := newParameter("", input)
|
|
||||||
inputs = append(inputs, thisParam)
|
|
||||||
}
|
|
||||||
|
|
||||||
boundMethod.Inputs = inputs
|
|
||||||
|
|
||||||
// Iterate outputs
|
|
||||||
// TODO: Determine what to do about limiting return types
|
|
||||||
// especially around errors.
|
|
||||||
outputParamCount := methodType.NumOut()
|
|
||||||
var outputs []*Parameter
|
|
||||||
for outputIndex := 0; outputIndex < outputParamCount; outputIndex++ {
|
|
||||||
output := methodType.Out(outputIndex)
|
|
||||||
thisParam := newParameter("", output)
|
|
||||||
outputs = append(outputs, thisParam)
|
|
||||||
}
|
|
||||||
boundMethod.Outputs = outputs
|
|
||||||
|
|
||||||
// Save method in result
|
|
||||||
result = append(result, boundMethod)
|
|
||||||
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Bridge struct {
|
|
||||||
upgrader websocket.Upgrader
|
|
||||||
server *http.Server
|
|
||||||
myLogger *logger.Logger
|
|
||||||
|
|
||||||
bindings string
|
|
||||||
dispatcher *messagedispatcher.Dispatcher
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
sessions map[string]*session
|
|
||||||
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
|
|
||||||
// Dialog client
|
|
||||||
dialog *messagedispatcher.DispatchClient
|
|
||||||
|
|
||||||
// Menus
|
|
||||||
menumanager *menumanager.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBridge(myLogger *logger.Logger) *Bridge {
|
|
||||||
result := &Bridge{
|
|
||||||
myLogger: myLogger,
|
|
||||||
upgrader: websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }},
|
|
||||||
sessions: make(map[string]*session),
|
|
||||||
}
|
|
||||||
|
|
||||||
myLogger.SetLogLevel(1)
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
result.ctx = ctx
|
|
||||||
result.cancel = cancel
|
|
||||||
result.server = &http.Server{Addr: ":34115"}
|
|
||||||
http.HandleFunc("/bridge", result.wsBridgeHandler)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bridge) Run(dispatcher *messagedispatcher.Dispatcher, menumanager *menumanager.Manager, bindings string, debug bool) error {
|
|
||||||
|
|
||||||
// Ensure we cancel the context when we shutdown
|
|
||||||
defer b.cancel()
|
|
||||||
|
|
||||||
b.bindings = bindings
|
|
||||||
b.dispatcher = dispatcher
|
|
||||||
b.menumanager = menumanager
|
|
||||||
|
|
||||||
// Setup dialog handler
|
|
||||||
dialogClient := NewDialogClient(b.myLogger)
|
|
||||||
b.dialog = dispatcher.RegisterClient(dialogClient)
|
|
||||||
dialogClient.dispatcher = b.dialog
|
|
||||||
|
|
||||||
b.myLogger.Info("Bridge mode started.")
|
|
||||||
|
|
||||||
err := b.server.ListenAndServe()
|
|
||||||
if err != nil && err != http.ErrServerClosed {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bridge) wsBridgeHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
c, err := b.upgrader.Upgrade(w, r, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Print("upgrade:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
b.myLogger.Info("Connection from frontend accepted [%s].", c.RemoteAddr().String())
|
|
||||||
b.startSession(c)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Bridge) startSession(conn *websocket.Conn) {
|
|
||||||
|
|
||||||
// Create a new session for this connection
|
|
||||||
s := newSession(conn, b.menumanager, b.bindings, b.dispatcher, b.myLogger, b.ctx)
|
|
||||||
|
|
||||||
// Setup the close handler
|
|
||||||
conn.SetCloseHandler(func(int, string) error {
|
|
||||||
b.myLogger.Info("Connection dropped [%s].", s.Identifier())
|
|
||||||
b.dispatcher.RemoveClient(s.client)
|
|
||||||
b.mu.Lock()
|
|
||||||
delete(b.sessions, s.Identifier())
|
|
||||||
b.mu.Unlock()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
b.mu.Lock()
|
|
||||||
go s.start(len(b.sessions) == 0)
|
|
||||||
b.sessions[s.Identifier()] = s
|
|
||||||
b.mu.Unlock()
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BridgeClient struct {
|
|
||||||
session *session
|
|
||||||
|
|
||||||
// Tray menu cache to send to reconnecting clients
|
|
||||||
messageCache chan string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) DeleteTrayMenuByID(id string) {
|
|
||||||
b.session.sendMessage("TD" + id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBridgeClient() *BridgeClient {
|
|
||||||
return &BridgeClient{
|
|
||||||
messageCache: make(chan string, 100),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) Quit() {
|
|
||||||
b.session.log.Info("Quit unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) NotifyEvent(message string) {
|
|
||||||
b.session.sendMessage("n" + message)
|
|
||||||
b.session.log.Info("NotifyEvent: %s", message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) CallResult(message string) {
|
|
||||||
b.session.sendMessage("c" + message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
|
||||||
// Handled by dialog_client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
|
||||||
// Handled by dialog_client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
|
||||||
// Handled by dialog_client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowSetTitle(title string) {
|
|
||||||
b.session.log.Info("WindowSetTitle unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowShow() {
|
|
||||||
b.session.log.Info("WindowShow unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowHide() {
|
|
||||||
b.session.log.Info("WindowHide unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowCenter() {
|
|
||||||
b.session.log.Info("WindowCenter unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowMaximise() {
|
|
||||||
b.session.log.Info("WindowMaximise unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowUnmaximise() {
|
|
||||||
b.session.log.Info("WindowUnmaximise unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowMinimise() {
|
|
||||||
b.session.log.Info("WindowMinimise unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowUnminimise() {
|
|
||||||
b.session.log.Info("WindowUnminimise unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowPosition(x int, y int) {
|
|
||||||
b.session.log.Info("WindowPosition unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowSize(width int, height int) {
|
|
||||||
b.session.log.Info("WindowSize unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowSetMinSize(width int, height int) {
|
|
||||||
b.session.log.Info("WindowSetMinSize unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowSetMaxSize(width int, height int) {
|
|
||||||
b.session.log.Info("WindowSetMaxSize unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowFullscreen() {
|
|
||||||
b.session.log.Info("WindowFullscreen unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowUnFullscreen() {
|
|
||||||
b.session.log.Info("WindowUnFullscreen unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) WindowSetColour(colour int) {
|
|
||||||
b.session.log.Info("WindowSetColour unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) DarkModeEnabled(callbackID string) {
|
|
||||||
b.session.log.Info("DarkModeEnabled unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) SetApplicationMenu(menuJSON string) {
|
|
||||||
b.session.log.Info("SetApplicationMenu unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) SetTrayMenu(trayMenuJSON string) {
|
|
||||||
b.session.sendMessage("TS" + trayMenuJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
|
||||||
b.session.sendMessage("TU" + trayMenuJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BridgeClient) UpdateContextMenu(contextMenuJSON string) {
|
|
||||||
b.session.log.Info("UpdateContextMenu unsupported in Bridge mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBridgeClient(session *session) *BridgeClient {
|
|
||||||
return &BridgeClient{
|
|
||||||
session: session,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,144 +0,0 @@
|
|||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
|
|
||||||
"github.com/leaanthony/slicer"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/dialog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DialogClient struct {
|
|
||||||
dispatcher *messagedispatcher.DispatchClient
|
|
||||||
log *logger.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) DeleteTrayMenuByID(id string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDialogClient(log *logger.Logger) *DialogClient {
|
|
||||||
return &DialogClient{
|
|
||||||
log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) Quit() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) NotifyEvent(message string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) CallResult(message string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) OpenDialog(dialogOptions *dialog.OpenDialog, callbackID string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) SaveDialog(dialogOptions *dialog.SaveDialog, callbackID string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) MessageDialog(dialogOptions *dialog.MessageDialog, callbackID string) {
|
|
||||||
|
|
||||||
osa, err := exec.LookPath("osascript")
|
|
||||||
if err != nil {
|
|
||||||
d.log.Info("MessageDialog unavailable (osascript not found)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var btns slicer.StringSlicer
|
|
||||||
defaultButton := ""
|
|
||||||
cancelButton := ""
|
|
||||||
for index, btn := range dialogOptions.Buttons {
|
|
||||||
btns.Add(strconv.Quote(btn))
|
|
||||||
if btn == dialogOptions.DefaultButton {
|
|
||||||
defaultButton = fmt.Sprintf("default button %d", index+1)
|
|
||||||
}
|
|
||||||
if btn == dialogOptions.CancelButton {
|
|
||||||
cancelButton = fmt.Sprintf("cancel button %d", index+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buttons := "{" + btns.Join(",") + "}"
|
|
||||||
script := fmt.Sprintf("display dialog \"%s\" buttons %s %s %s with title \"%s\"", dialogOptions.Message, buttons, defaultButton, cancelButton, dialogOptions.Title)
|
|
||||||
go func() {
|
|
||||||
out, err := exec.Command(osa, "-e", script).Output()
|
|
||||||
if err != nil {
|
|
||||||
// Assume user has pressed cancel button
|
|
||||||
if dialogOptions.CancelButton != "" {
|
|
||||||
d.dispatcher.DispatchMessage("DM" + callbackID + "|" + dialogOptions.CancelButton)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.log.Error("Dialog had bad exit code. If this was a Cancel button, add 'CancelButton' to the dialog.MessageDialog struct. Error: %s", err.Error())
|
|
||||||
d.dispatcher.DispatchMessage("DM" + callbackID + "|error - check logs")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonPressed := strings.TrimSpace(strings.TrimPrefix(string(out), "button returned:"))
|
|
||||||
d.dispatcher.DispatchMessage("DM" + callbackID + "|" + buttonPressed)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowSetTitle(title string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowShow() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowHide() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowCenter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowMaximise() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowUnmaximise() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowMinimise() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowUnminimise() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowPosition(x int, y int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowSize(width int, height int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowSetMinSize(width int, height int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowSetMaxSize(width int, height int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowFullscreen() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowUnFullscreen() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) WindowSetColour(colour int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) DarkModeEnabled(callbackID string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) SetApplicationMenu(menuJSON string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) SetTrayMenu(trayMenuJSON string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) UpdateTrayMenuLabel(trayMenuJSON string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DialogClient) UpdateContextMenu(contextMenuJSON string) {
|
|
||||||
}
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package bridge
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
_ "embed"
|
|
||||||
"log"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed darwin.js
|
|
||||||
var darwinRuntime string
|
|
||||||
|
|
||||||
// session represents a single websocket session
|
|
||||||
type session struct {
|
|
||||||
bindings string
|
|
||||||
conn *websocket.Conn
|
|
||||||
//eventManager interfaces.EventManager
|
|
||||||
log *logger.Logger
|
|
||||||
//ipc interfaces.IPCManager
|
|
||||||
|
|
||||||
// Mutex for writing to the socket
|
|
||||||
shutdown chan bool
|
|
||||||
writeChan chan []byte
|
|
||||||
|
|
||||||
done bool
|
|
||||||
|
|
||||||
// context
|
|
||||||
ctx context.Context
|
|
||||||
|
|
||||||
// client
|
|
||||||
client *messagedispatcher.DispatchClient
|
|
||||||
|
|
||||||
// Menus
|
|
||||||
menumanager *menumanager.Manager
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSession(conn *websocket.Conn, menumanager *menumanager.Manager, bindings string, dispatcher *messagedispatcher.Dispatcher, logger *logger.Logger, ctx context.Context) *session {
|
|
||||||
result := &session{
|
|
||||||
conn: conn,
|
|
||||||
bindings: bindings,
|
|
||||||
log: logger,
|
|
||||||
shutdown: make(chan bool),
|
|
||||||
writeChan: make(chan []byte, 100),
|
|
||||||
ctx: ctx,
|
|
||||||
menumanager: menumanager,
|
|
||||||
}
|
|
||||||
|
|
||||||
result.client = dispatcher.RegisterClient(newBridgeClient(result))
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identifier returns a string identifier for the remote connection.
|
|
||||||
// Taking the form of the client's <ip address>:<port>.
|
|
||||||
func (s *session) Identifier() string {
|
|
||||||
if s.conn != nil {
|
|
||||||
return s.conn.RemoteAddr().String()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *session) sendMessage(msg string) error {
|
|
||||||
if !s.done {
|
|
||||||
s.writeChan <- []byte(msg)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *session) start(firstSession bool) {
|
|
||||||
s.log.SetLogLevel(1)
|
|
||||||
s.log.Info("Connected to frontend.")
|
|
||||||
go s.writePump()
|
|
||||||
|
|
||||||
var wailsRuntime string
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "darwin":
|
|
||||||
wailsRuntime = darwinRuntime
|
|
||||||
default:
|
|
||||||
log.Fatal("platform not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingsMessage := "window.wailsbindings = `" + s.bindings + "`;"
|
|
||||||
s.log.Info(bindingsMessage)
|
|
||||||
bootstrapMessage := bindingsMessage + wailsRuntime
|
|
||||||
|
|
||||||
s.sendMessage("b" + bootstrapMessage)
|
|
||||||
|
|
||||||
// Send menus
|
|
||||||
traymenus, err := s.menumanager.GetTrayMenus()
|
|
||||||
if err != nil {
|
|
||||||
s.log.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, trayMenu := range traymenus {
|
|
||||||
s.sendMessage("TS" + trayMenu)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
messageType, buffer, err := s.conn.ReadMessage()
|
|
||||||
if messageType == -1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
s.log.Error("Error reading message: %v", err)
|
|
||||||
err = s.conn.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
message := string(buffer)
|
|
||||||
|
|
||||||
s.log.Debug("Got message: %#v\n", message)
|
|
||||||
|
|
||||||
// Dispatch message as normal
|
|
||||||
s.client.DispatchMessage(message)
|
|
||||||
|
|
||||||
if s.done {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown
|
|
||||||
func (s *session) Shutdown() {
|
|
||||||
s.conn.Close()
|
|
||||||
s.done = true
|
|
||||||
s.log.Info("session %v exit", s.Identifier())
|
|
||||||
}
|
|
||||||
|
|
||||||
// writePump pulls messages from the writeChan and sends them to the client
|
|
||||||
// since it uses a channel to read the messages the socket is protected without locks
|
|
||||||
func (s *session) writePump() {
|
|
||||||
s.log.Debug("Session %v - writePump start", s.Identifier())
|
|
||||||
defer s.log.Debug("Session %v - writePump shutdown", s.Identifier())
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-s.ctx.Done():
|
|
||||||
s.Shutdown()
|
|
||||||
return
|
|
||||||
case msg, ok := <-s.writeChan:
|
|
||||||
s.conn.SetWriteDeadline(time.Now().Add(1 * time.Second))
|
|
||||||
if !ok {
|
|
||||||
s.log.Debug("writeChan was closed!")
|
|
||||||
s.conn.WriteMessage(websocket.CloseMessage, []byte{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
|
|
||||||
s.log.Debug(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,89 +0,0 @@
|
|||||||
package colour
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wzshiming/ctc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Col(col ctc.Color, text string) string {
|
|
||||||
return fmt.Sprintf("%s%s%s", col, text, ctc.Reset)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Yellow(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightYellow, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Red(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightRed, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Blue(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightBlue, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Green(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightGreen, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Cyan(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightCyan, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Magenta(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightMagenta, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func White(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightWhite, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Black(text string) string {
|
|
||||||
return Col(ctc.ForegroundBrightBlack, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkYellow(text string) string {
|
|
||||||
return Col(ctc.ForegroundYellow, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkRed(text string) string {
|
|
||||||
return Col(ctc.ForegroundRed, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkBlue(text string) string {
|
|
||||||
return Col(ctc.ForegroundBlue, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkGreen(text string) string {
|
|
||||||
return Col(ctc.ForegroundGreen, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkCyan(text string) string {
|
|
||||||
return Col(ctc.ForegroundCyan, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkMagenta(text string) string {
|
|
||||||
return Col(ctc.ForegroundMagenta, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkWhite(text string) string {
|
|
||||||
return Col(ctc.ForegroundWhite, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
func DarkBlack(text string) string {
|
|
||||||
return Col(ctc.ForegroundBlack, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
var rainbowCols = []func(string) string{Red, Yellow, Green, Cyan, Blue, Magenta}
|
|
||||||
|
|
||||||
func Rainbow(text string) string {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
for i := 0; i < len(text); i++ {
|
|
||||||
fn := rainbowCols[i%len(rainbowCols)]
|
|
||||||
builder.WriteString(fn(text[i : i+1]))
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RandomID returns a random ID as a string
|
|
||||||
func RandomID() string {
|
|
||||||
b := make([]byte, 16)
|
|
||||||
_, err := rand.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%x", b)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Joel
|
|
||||||
|
|
||||||
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.
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
// deepcopy makes deep copies of things. A standard copy will copy the
|
|
||||||
// pointers: deep copy copies the values pointed to. Unexported field
|
|
||||||
// values are not copied.
|
|
||||||
//
|
|
||||||
// Copyright (c)2014-2016, Joel Scoble (github.com/mohae), all rights reserved.
|
|
||||||
// License: MIT, for more details check the included LICENSE file.
|
|
||||||
package deepcopy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Interface for delegating copy process to type
|
|
||||||
type Interface interface {
|
|
||||||
DeepCopy() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iface is an alias to Copy; this exists for backwards compatibility reasons.
|
|
||||||
func Iface(iface interface{}) interface{} {
|
|
||||||
return Copy(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy creates a deep copy of whatever is passed to it and returns the copy
|
|
||||||
// in an interface{}. The returned value will need to be asserted to the
|
|
||||||
// correct type.
|
|
||||||
func Copy(src interface{}) interface{} {
|
|
||||||
if src == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the interface a reflect.Value
|
|
||||||
original := reflect.ValueOf(src)
|
|
||||||
|
|
||||||
// Make a copy of the same type as the original.
|
|
||||||
cpy := reflect.New(original.Type()).Elem()
|
|
||||||
|
|
||||||
// Recursively copy the original.
|
|
||||||
copyRecursive(original, cpy)
|
|
||||||
|
|
||||||
// Return the copy as an interface.
|
|
||||||
return cpy.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// copyRecursive does the actual copying of the interface. It currently has
|
|
||||||
// limited support for what it can handle. Add as needed.
|
|
||||||
func copyRecursive(original, cpy reflect.Value) {
|
|
||||||
// check for implement deepcopy.Interface
|
|
||||||
if original.CanInterface() {
|
|
||||||
if copier, ok := original.Interface().(Interface); ok {
|
|
||||||
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle according to original's Kind
|
|
||||||
switch original.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
// Get the actual value being pointed to.
|
|
||||||
originalValue := original.Elem()
|
|
||||||
|
|
||||||
// if it isn't valid, return.
|
|
||||||
if !originalValue.IsValid() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cpy.Set(reflect.New(originalValue.Type()))
|
|
||||||
copyRecursive(originalValue, cpy.Elem())
|
|
||||||
|
|
||||||
case reflect.Interface:
|
|
||||||
// If this is a nil, don't do anything
|
|
||||||
if original.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Get the value for the interface, not the pointer.
|
|
||||||
originalValue := original.Elem()
|
|
||||||
|
|
||||||
// Get the value by calling Elem().
|
|
||||||
copyValue := reflect.New(originalValue.Type()).Elem()
|
|
||||||
copyRecursive(originalValue, copyValue)
|
|
||||||
cpy.Set(copyValue)
|
|
||||||
|
|
||||||
case reflect.Struct:
|
|
||||||
t, ok := original.Interface().(time.Time)
|
|
||||||
if ok {
|
|
||||||
cpy.Set(reflect.ValueOf(t))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Go through each field of the struct and copy it.
|
|
||||||
for i := 0; i < original.NumField(); i++ {
|
|
||||||
// The Type's StructField for a given field is checked to see if StructField.PkgPath
|
|
||||||
// is set to determine if the field is exported or not because CanSet() returns false
|
|
||||||
// for settable fields. I'm not sure why. -mohae
|
|
||||||
if original.Type().Field(i).PkgPath != "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
copyRecursive(original.Field(i), cpy.Field(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Slice:
|
|
||||||
if original.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Make a new slice and copy each element.
|
|
||||||
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
|
|
||||||
for i := 0; i < original.Len(); i++ {
|
|
||||||
copyRecursive(original.Index(i), cpy.Index(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
case reflect.Map:
|
|
||||||
if original.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cpy.Set(reflect.MakeMap(original.Type()))
|
|
||||||
for _, key := range original.MapKeys() {
|
|
||||||
originalValue := original.MapIndex(key)
|
|
||||||
copyValue := reflect.New(originalValue.Type()).Elem()
|
|
||||||
copyRecursive(originalValue, copyValue)
|
|
||||||
copyKey := Copy(key.Interface())
|
|
||||||
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
cpy.Set(original)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
# 3rd Party Licenses
|
|
||||||
|
|
||||||
## Webview
|
|
||||||
Whilst not using the library directly, there is certainly some code that is inspired by or used from the webview library.
|
|
||||||
|
|
||||||
Homepage: https://github.com/webview/webview
|
|
||||||
License: https://github.com/webview/webview/blob/master/LICENSE
|
|
||||||
|
|
||||||
## vec
|
|
||||||
Homepage: https://github.com/rxi/vec
|
|
||||||
License: https://github.com/rxi/vec/blob/master/LICENSE
|
|
||||||
|
|
||||||
## json
|
|
||||||
Homepage: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
|
||||||
License: http://git.ozlabs.org/?p=ccan;a=blob;f=licenses/BSD-MIT;h=89de354795ec7a7cdab07c091029653d3618540d;hb=HEAD
|
|
||||||
|
|
||||||
## hashmap
|
|
||||||
Homepage: https://github.com/sheredom/hashmap.h
|
|
||||||
License: https://github.com/sheredom/hashmap.h/blob/master/LICENSE
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Created by Lea Anthony on 6/1/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
// Credit: https://stackoverflow.com/a/8465083
|
|
||||||
char* concat(const char *string1, const char *string2)
|
|
||||||
{
|
|
||||||
const size_t len1 = strlen(string1);
|
|
||||||
const size_t len2 = strlen(string2);
|
|
||||||
char *result = malloc(len1 + len2 + 1);
|
|
||||||
strcpy(result, string1);
|
|
||||||
memcpy(result + len1, string2, len2 + 1);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10k is more than enough for a log message
|
|
||||||
#define MAXMESSAGE 1024*10
|
|
||||||
char abortbuffer[MAXMESSAGE];
|
|
||||||
|
|
||||||
void ABORT(const char *message, ...) {
|
|
||||||
const char *temp = concat("FATAL: ", message);
|
|
||||||
va_list args;
|
|
||||||
va_start(args, message);
|
|
||||||
vsnprintf(abortbuffer, MAXMESSAGE, temp, args);
|
|
||||||
printf("%s\n", &abortbuffer[0]);
|
|
||||||
MEMFREE(temp);
|
|
||||||
va_end(args);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int freeHashmapItem(void *const context, struct hashmap_element_s *const e) {
|
|
||||||
free(e->data);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* getJSONString(JsonNode *item, const char* key) {
|
|
||||||
// Get key
|
|
||||||
JsonNode *node = json_find_member(item, key);
|
|
||||||
const char *result = "";
|
|
||||||
if ( node != NULL && node->tag == JSON_STRING) {
|
|
||||||
result = node->string_;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ABORT_JSON(JsonNode *node, const char* key) {
|
|
||||||
ABORT("Unable to read required key '%s' from JSON: %s\n", key, json_encode(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* mustJSONString(JsonNode *node, const char* key) {
|
|
||||||
const char* result = getJSONString(node, key);
|
|
||||||
if ( result == NULL ) {
|
|
||||||
ABORT_JSON(node, key);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
JsonNode* mustJSONObject(JsonNode *node, const char* key) {
|
|
||||||
struct JsonNode* result = getJSONObject(node, key);
|
|
||||||
if ( result == NULL ) {
|
|
||||||
ABORT_JSON(node, key);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode* getJSONObject(JsonNode* node, const char* key) {
|
|
||||||
return json_find_member(node, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getJSONBool(JsonNode *item, const char* key, bool *result) {
|
|
||||||
JsonNode *node = json_find_member(item, key);
|
|
||||||
if ( node != NULL && node->tag == JSON_BOOL) {
|
|
||||||
*result = node->bool_;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getJSONInt(JsonNode *item, const char* key, int *result) {
|
|
||||||
JsonNode *node = json_find_member(item, key);
|
|
||||||
if ( node != NULL && node->tag == JSON_NUMBER) {
|
|
||||||
*result = (int) node->number_;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonNode* mustParseJSON(const char* JSON) {
|
|
||||||
JsonNode* parsedUpdate = json_decode(JSON);
|
|
||||||
if ( parsedUpdate == NULL ) {
|
|
||||||
ABORT("Unable to decode JSON: %s\n", JSON);
|
|
||||||
}
|
|
||||||
return parsedUpdate;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Lea Anthony on 6/1/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef COMMON_H
|
|
||||||
#define COMMON_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include "string.h"
|
|
||||||
#include "hashmap.h"
|
|
||||||
#include "vec.h"
|
|
||||||
#include "json.h"
|
|
||||||
|
|
||||||
#define STREQ(a,b) strcmp(a, b) == 0
|
|
||||||
#define STREMPTY(string) strlen(string) == 0
|
|
||||||
#define STRCOPY(a) concat(a, "")
|
|
||||||
#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0
|
|
||||||
#define MEMFREE(input) free((void*)input); input = NULL;
|
|
||||||
#define FREE_AND_SET(variable, value) if( variable != NULL ) { MEMFREE(variable); } variable = value
|
|
||||||
|
|
||||||
// Credit: https://stackoverflow.com/a/8465083
|
|
||||||
char* concat(const char *string1, const char *string2);
|
|
||||||
void ABORT(const char *message, ...);
|
|
||||||
int freeHashmapItem(void *const context, struct hashmap_element_s *const e);
|
|
||||||
const char* getJSONString(JsonNode *item, const char* key);
|
|
||||||
const char* mustJSONString(JsonNode *node, const char* key);
|
|
||||||
JsonNode* getJSONObject(JsonNode* node, const char* key);
|
|
||||||
JsonNode* mustJSONObject(JsonNode *node, const char* key);
|
|
||||||
|
|
||||||
bool getJSONBool(JsonNode *item, const char* key, bool *result);
|
|
||||||
bool getJSONInt(JsonNode *item, const char* key, int *result);
|
|
||||||
|
|
||||||
JsonNode* mustParseJSON(const char* JSON);
|
|
||||||
|
|
||||||
#endif //ASSETS_C_COMMON_H
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
////
|
|
||||||
//// Created by Lea Anthony on 6/1/21.
|
|
||||||
////
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "ffenestri_darwin.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "contextmenus_darwin.h"
|
|
||||||
#include "menu_darwin.h"
|
|
||||||
|
|
||||||
ContextMenu* NewContextMenu(const char* contextMenuJSON) {
|
|
||||||
ContextMenu* result = malloc(sizeof(ContextMenu));
|
|
||||||
|
|
||||||
JsonNode* processedJSON = json_decode(contextMenuJSON);
|
|
||||||
if( processedJSON == NULL ) {
|
|
||||||
ABORT("[NewTrayMenu] Unable to parse TrayMenu JSON: %s", contextMenuJSON);
|
|
||||||
}
|
|
||||||
// Save reference to this json
|
|
||||||
result->processedJSON = processedJSON;
|
|
||||||
|
|
||||||
result->ID = mustJSONString(processedJSON, "ID");
|
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
|
||||||
|
|
||||||
result->menu = NewMenu(processedMenu);
|
|
||||||
result->nsmenu = NULL;
|
|
||||||
result->menu->menuType = ContextMenuType;
|
|
||||||
result->menu->parentData = result;
|
|
||||||
result->contextMenuData = NULL;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu* GetContextMenuByID(ContextMenuStore* store, const char *contextMenuID) {
|
|
||||||
return (ContextMenu*)hashmap_get(&store->contextMenuMap, (char*)contextMenuID, strlen(contextMenuID));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteContextMenu(ContextMenu* contextMenu) {
|
|
||||||
// Free Menu
|
|
||||||
DeleteMenu(contextMenu->menu);
|
|
||||||
|
|
||||||
// Delete any context menu data we may have stored
|
|
||||||
if( contextMenu->contextMenuData != NULL ) {
|
|
||||||
MEMFREE(contextMenu->contextMenuData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free JSON
|
|
||||||
if (contextMenu->processedJSON != NULL ) {
|
|
||||||
json_delete(contextMenu->processedJSON);
|
|
||||||
contextMenu->processedJSON = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free context menu
|
|
||||||
free(contextMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
int freeContextMenu(void *const context, struct hashmap_element_s *const e) {
|
|
||||||
DeleteContextMenu(e->data);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData) {
|
|
||||||
|
|
||||||
// If no context menu ID was given, abort
|
|
||||||
if( contextMenuID == NULL ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu* contextMenu = GetContextMenuByID(store, contextMenuID);
|
|
||||||
|
|
||||||
// We don't need the ID now
|
|
||||||
MEMFREE(contextMenuID);
|
|
||||||
|
|
||||||
if( contextMenu == NULL ) {
|
|
||||||
// Free context menu data
|
|
||||||
if( contextMenuData != NULL ) {
|
|
||||||
MEMFREE(contextMenuData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to store the context menu data. Free existing data if we have it
|
|
||||||
// and set to the new value.
|
|
||||||
FREE_AND_SET(contextMenu->contextMenuData, contextMenuData);
|
|
||||||
|
|
||||||
// Grab the content view and show the menu
|
|
||||||
id contentView = msg_reg(mainWindow, s("contentView"));
|
|
||||||
|
|
||||||
// Get the triggering event
|
|
||||||
id menuEvent = msg_reg(mainWindow, s("currentEvent"));
|
|
||||||
|
|
||||||
if( contextMenu->nsmenu == NULL ) {
|
|
||||||
// GetMenu creates the NSMenu
|
|
||||||
contextMenu->nsmenu = GetMenu(contextMenu->menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show popup
|
|
||||||
((id(*)(id, SEL, id, id, id))objc_msgSend)(c("NSMenu"), s("popUpContextMenu:withEvent:forView:"), contextMenu->nsmenu, menuEvent, contentView);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
////
|
|
||||||
//// Created by Lea Anthony on 6/1/21.
|
|
||||||
////
|
|
||||||
//
|
|
||||||
#ifndef CONTEXTMENU_DARWIN_H
|
|
||||||
#define CONTEXTMENU_DARWIN_H
|
|
||||||
|
|
||||||
#include "json.h"
|
|
||||||
#include "menu_darwin.h"
|
|
||||||
#include "contextmenustore_darwin.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char* ID;
|
|
||||||
id nsmenu;
|
|
||||||
Menu* menu;
|
|
||||||
|
|
||||||
JsonNode* processedJSON;
|
|
||||||
|
|
||||||
// Context menu data is given by the frontend when clicking a context menu.
|
|
||||||
// We send this to the backend when an item is selected
|
|
||||||
const char* contextMenuData;
|
|
||||||
} ContextMenu;
|
|
||||||
|
|
||||||
|
|
||||||
ContextMenu* NewContextMenu(const char* contextMenuJSON);
|
|
||||||
|
|
||||||
ContextMenu* GetContextMenuByID( ContextMenuStore* store, const char *contextMenuID);
|
|
||||||
void DeleteContextMenu(ContextMenu* contextMenu);
|
|
||||||
int freeContextMenu(void *const context, struct hashmap_element_s *const e);
|
|
||||||
|
|
||||||
void ShowContextMenu(ContextMenuStore* store, id mainWindow, const char *contextMenuID, const char *contextMenuData);
|
|
||||||
|
|
||||||
#endif //CONTEXTMENU_DARWIN_H
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
|
|
||||||
#include "contextmenus_darwin.h"
|
|
||||||
#include "contextmenustore_darwin.h"
|
|
||||||
|
|
||||||
ContextMenuStore* NewContextMenuStore() {
|
|
||||||
|
|
||||||
ContextMenuStore* result = malloc(sizeof(ContextMenuStore));
|
|
||||||
|
|
||||||
// Allocate Context Menu Store
|
|
||||||
if( 0 != hashmap_create((const unsigned)4, &result->contextMenuMap)) {
|
|
||||||
ABORT("[NewContextMenus] Not enough memory to allocate contextMenuStore!");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddContextMenuToStore(ContextMenuStore* store, const char* contextMenuJSON) {
|
|
||||||
ContextMenu* newMenu = NewContextMenu(contextMenuJSON);
|
|
||||||
|
|
||||||
//TODO: check if there is already an entry for this menu
|
|
||||||
hashmap_put(&store->contextMenuMap, newMenu->ID, strlen(newMenu->ID), newMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMenu* GetContextMenuFromStore(ContextMenuStore* store, const char* menuID) {
|
|
||||||
// Get the current menu
|
|
||||||
return hashmap_get(&store->contextMenuMap, menuID, strlen(menuID));
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateContextMenuInStore(ContextMenuStore* store, const char* menuJSON) {
|
|
||||||
ContextMenu* newContextMenu = NewContextMenu(menuJSON);
|
|
||||||
|
|
||||||
// Get the current menu
|
|
||||||
ContextMenu *currentMenu = GetContextMenuFromStore(store, newContextMenu->ID);
|
|
||||||
if ( currentMenu == NULL ) {
|
|
||||||
ABORT("Attempted to update unknown context menu with ID '%s'.", newContextMenu->ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
hashmap_remove(&store->contextMenuMap, newContextMenu->ID, strlen(newContextMenu->ID));
|
|
||||||
|
|
||||||
// Save the status bar reference
|
|
||||||
DeleteContextMenu(currentMenu);
|
|
||||||
|
|
||||||
hashmap_put(&store->contextMenuMap, newContextMenu->ID, strlen(newContextMenu->ID), newContextMenu);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DeleteContextMenuStore(ContextMenuStore* store) {
|
|
||||||
|
|
||||||
// Guard against NULLs
|
|
||||||
if( store == NULL ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete context menus
|
|
||||||
if( hashmap_num_entries(&store->contextMenuMap) > 0 ) {
|
|
||||||
if (0 != hashmap_iterate_pairs(&store->contextMenuMap, freeContextMenu, NULL)) {
|
|
||||||
ABORT("[DeleteContextMenuStore] Failed to release contextMenuStore entries!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free context menu hashmap
|
|
||||||
hashmap_destroy(&store->contextMenuMap);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Lea Anthony on 7/1/21.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef CONTEXTMENUSTORE_DARWIN_H
|
|
||||||
#define CONTEXTMENUSTORE_DARWIN_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
int dummy;
|
|
||||||
|
|
||||||
// This is our context menu store which keeps track
|
|
||||||
// of all instances of ContextMenus
|
|
||||||
struct hashmap_s contextMenuMap;
|
|
||||||
|
|
||||||
} ContextMenuStore;
|
|
||||||
|
|
||||||
ContextMenuStore* NewContextMenuStore();
|
|
||||||
|
|
||||||
void DeleteContextMenuStore(ContextMenuStore* store);
|
|
||||||
void UpdateContextMenuInStore(ContextMenuStore* store, const char* menuJSON);
|
|
||||||
|
|
||||||
void AddContextMenuToStore(ContextMenuStore* store, const char* contextMenuJSON);
|
|
||||||
|
|
||||||
#endif //CONTEXTMENUSTORE_DARWIN_H
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,64 +0,0 @@
|
|||||||
// Credit: https://gist.github.com/ysc3839/b08d2bff1c7dacde529bed1d37e85ccf
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
typedef enum _WINDOWCOMPOSITIONATTRIB
|
|
||||||
{
|
|
||||||
WCA_UNDEFINED = 0,
|
|
||||||
WCA_NCRENDERING_ENABLED = 1,
|
|
||||||
WCA_NCRENDERING_POLICY = 2,
|
|
||||||
WCA_TRANSITIONS_FORCEDISABLED = 3,
|
|
||||||
WCA_ALLOW_NCPAINT = 4,
|
|
||||||
WCA_CAPTION_BUTTON_BOUNDS = 5,
|
|
||||||
WCA_NONCLIENT_RTL_LAYOUT = 6,
|
|
||||||
WCA_FORCE_ICONIC_REPRESENTATION = 7,
|
|
||||||
WCA_EXTENDED_FRAME_BOUNDS = 8,
|
|
||||||
WCA_HAS_ICONIC_BITMAP = 9,
|
|
||||||
WCA_THEME_ATTRIBUTES = 10,
|
|
||||||
WCA_NCRENDERING_EXILED = 11,
|
|
||||||
WCA_NCADORNMENTINFO = 12,
|
|
||||||
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
|
|
||||||
WCA_VIDEO_OVERLAY_ACTIVE = 14,
|
|
||||||
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
|
|
||||||
WCA_DISALLOW_PEEK = 16,
|
|
||||||
WCA_CLOAK = 17,
|
|
||||||
WCA_CLOAKED = 18,
|
|
||||||
WCA_ACCENT_POLICY = 19,
|
|
||||||
WCA_FREEZE_REPRESENTATION = 20,
|
|
||||||
WCA_EVER_UNCLOAKED = 21,
|
|
||||||
WCA_VISUAL_OWNER = 22,
|
|
||||||
WCA_HOLOGRAPHIC = 23,
|
|
||||||
WCA_EXCLUDED_FROM_DDA = 24,
|
|
||||||
WCA_PASSIVEUPDATEMODE = 25,
|
|
||||||
WCA_USEDARKMODECOLORS = 26,
|
|
||||||
WCA_LAST = 27
|
|
||||||
} WINDOWCOMPOSITIONATTRIB;
|
|
||||||
|
|
||||||
typedef struct _WINDOWCOMPOSITIONATTRIBDATA
|
|
||||||
{
|
|
||||||
WINDOWCOMPOSITIONATTRIB Attrib;
|
|
||||||
PVOID pvData;
|
|
||||||
SIZE_T cbData;
|
|
||||||
} WINDOWCOMPOSITIONATTRIBDATA;
|
|
||||||
|
|
||||||
typedef enum _ACCENT_STATE
|
|
||||||
{
|
|
||||||
ACCENT_DISABLED = 0,
|
|
||||||
ACCENT_ENABLE_GRADIENT = 1,
|
|
||||||
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
|
|
||||||
ACCENT_ENABLE_BLURBEHIND = 3,
|
|
||||||
ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803
|
|
||||||
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
|
|
||||||
ACCENT_INVALID_STATE = 6
|
|
||||||
} ACCENT_STATE;
|
|
||||||
|
|
||||||
typedef struct _ACCENT_POLICY
|
|
||||||
{
|
|
||||||
ACCENT_STATE AccentState;
|
|
||||||
DWORD AccentFlags;
|
|
||||||
DWORD GradientColor;
|
|
||||||
DWORD AnimationId;
|
|
||||||
} ACCENT_POLICY;
|
|
||||||
|
|
||||||
typedef BOOL (WINAPI *pfnGetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
|
|
||||||
|
|
||||||
typedef BOOL (WINAPI *pfnSetWindowCompositionAttribute)(HWND, WINDOWCOMPOSITIONATTRIBDATA*);
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
package ffenestri
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
|
||||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
|
||||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
|
||||||
|
|
||||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
|
||||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
|
||||||
|
|
||||||
#cgo windows CXXFLAGS: -std=c++11
|
|
||||||
#cgo windows,amd64 LDFLAGS: -L./windows/x64 -lwebview -lWebView2Loader -lgdi32 -lole32 -lShlwapi -luser32 -loleaut32
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "ffenestri.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
// Application is our main application object
|
|
||||||
type Application struct {
|
|
||||||
config *options.App
|
|
||||||
memory []unsafe.Pointer
|
|
||||||
|
|
||||||
// This is the main app pointer
|
|
||||||
app *C.struct_Application
|
|
||||||
|
|
||||||
// Manages menus
|
|
||||||
menuManager *menumanager.Manager
|
|
||||||
|
|
||||||
// Logger
|
|
||||||
logger logger.CustomLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) saveMemoryReference(mem unsafe.Pointer) {
|
|
||||||
a.memory = append(a.memory, mem)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) string2CString(str string) *C.char {
|
|
||||||
result := C.CString(str)
|
|
||||||
a.saveMemoryReference(unsafe.Pointer(result))
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
runtime.LockOSThread()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApplicationWithConfig creates a new application based on the given config
|
|
||||||
func NewApplicationWithConfig(config *options.App, logger *logger.Logger, menuManager *menumanager.Manager) *Application {
|
|
||||||
return &Application{
|
|
||||||
config: config,
|
|
||||||
logger: logger.CustomLogger("Ffenestri"),
|
|
||||||
menuManager: menuManager,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Application) freeMemory() {
|
|
||||||
for _, mem := range a.memory {
|
|
||||||
// fmt.Printf("Freeing memory: %+v\n", mem)
|
|
||||||
C.free(mem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool2Cint converts a Go boolean to a C integer
|
|
||||||
func (a *Application) bool2Cint(value bool) C.int {
|
|
||||||
if value {
|
|
||||||
return C.int(1)
|
|
||||||
}
|
|
||||||
return C.int(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dispatcher is the interface to send messages to
|
|
||||||
var dispatcher *messagedispatcher.DispatchClient
|
|
||||||
|
|
||||||
// Dispatcher is what we register out client with
|
|
||||||
type Dispatcher interface {
|
|
||||||
RegisterClient(client messagedispatcher.Client) *messagedispatcher.DispatchClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// DispatchClient is the means for passing messages to the backend
|
|
||||||
type DispatchClient interface {
|
|
||||||
SendMessage(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func intToColour(colour int) (C.int, C.int, C.int, C.int) {
|
|
||||||
var alpha = C.int(colour & 0xFF)
|
|
||||||
var blue = C.int((colour >> 8) & 0xFF)
|
|
||||||
var green = C.int((colour >> 16) & 0xFF)
|
|
||||||
var red = C.int((colour >> 24) & 0xFF)
|
|
||||||
return red, green, blue, alpha
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the application
|
|
||||||
func (a *Application) Run(incomingDispatcher Dispatcher, bindings string, debug bool) error {
|
|
||||||
title := a.string2CString(a.config.Title)
|
|
||||||
width := C.int(a.config.Width)
|
|
||||||
height := C.int(a.config.Height)
|
|
||||||
resizable := a.bool2Cint(!a.config.DisableResize)
|
|
||||||
devtools := a.bool2Cint(a.config.DevTools)
|
|
||||||
fullscreen := a.bool2Cint(a.config.Fullscreen)
|
|
||||||
startHidden := a.bool2Cint(a.config.StartHidden)
|
|
||||||
logLevel := C.int(a.config.LogLevel)
|
|
||||||
hideWindowOnClose := a.bool2Cint(a.config.HideWindowOnClose)
|
|
||||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel, hideWindowOnClose)
|
|
||||||
|
|
||||||
// Save app reference
|
|
||||||
a.app = (*C.struct_Application)(app)
|
|
||||||
|
|
||||||
// Set Min Window Size
|
|
||||||
minWidth := C.int(a.config.MinWidth)
|
|
||||||
minHeight := C.int(a.config.MinHeight)
|
|
||||||
C.SetMinWindowSize(a.app, minWidth, minHeight)
|
|
||||||
|
|
||||||
// Set Max Window Size
|
|
||||||
maxWidth := C.int(a.config.MaxWidth)
|
|
||||||
maxHeight := C.int(a.config.MaxHeight)
|
|
||||||
C.SetMaxWindowSize(a.app, maxWidth, maxHeight)
|
|
||||||
|
|
||||||
// Set debug if needed
|
|
||||||
C.SetDebug(app, a.bool2Cint(debug))
|
|
||||||
|
|
||||||
// TODO: Move frameless to Linux options
|
|
||||||
if a.config.Frameless {
|
|
||||||
C.DisableFrame(a.app)
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.config.RGBA != 0 {
|
|
||||||
r, g, b, alpha := intToColour(a.config.RGBA)
|
|
||||||
C.SetColour(a.app, r, g, b, alpha)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escape bindings so C doesn't freak out
|
|
||||||
bindings = strings.ReplaceAll(bindings, `"`, `\"`)
|
|
||||||
|
|
||||||
// Set bindings
|
|
||||||
C.SetBindings(app, a.string2CString(bindings))
|
|
||||||
|
|
||||||
// save the dispatcher in a package variable so that the C callbacks
|
|
||||||
// can access it
|
|
||||||
dispatcher = incomingDispatcher.RegisterClient(newClient(a))
|
|
||||||
|
|
||||||
// Process platform settings
|
|
||||||
err := a.processPlatformSettings()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check we could initialise the application
|
|
||||||
if app != nil {
|
|
||||||
// Yes - Save memory reference and run app, cleaning up afterwards
|
|
||||||
a.saveMemoryReference(unsafe.Pointer(app))
|
|
||||||
C.Run(app, 0, nil)
|
|
||||||
} else {
|
|
||||||
// Oh no! We couldn't initialise the application
|
|
||||||
a.logger.Fatal("Cannot initialise Application.")
|
|
||||||
}
|
|
||||||
//println("\n\n\n\n\n\nhererererer\n\n\n\n")
|
|
||||||
a.freeMemory()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// messageFromWindowCallback is called by any messages sent in
|
|
||||||
// webkit to window.external.invoke. It relays the message on to
|
|
||||||
// the dispatcher.
|
|
||||||
//export messageFromWindowCallback
|
|
||||||
func messageFromWindowCallback(data *C.char) {
|
|
||||||
dispatcher.DispatchMessage(C.GoString(data))
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#ifndef __FFENESTRI_H__
|
|
||||||
#define __FFENESTRI_H__
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
struct Application;
|
|
||||||
|
|
||||||
extern struct Application *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel, int hideWindowOnClose);
|
|
||||||
extern void SetMinWindowSize(struct Application*, int minWidth, int minHeight);
|
|
||||||
extern void SetMaxWindowSize(struct Application*, int maxWidth, int maxHeight);
|
|
||||||
extern void Run(struct Application*, int argc, char **argv);
|
|
||||||
extern void DestroyApplication(struct Application*);
|
|
||||||
extern void SetDebug(struct Application*, int flag);
|
|
||||||
extern void SetBindings(struct Application*, const char *bindings);
|
|
||||||
extern void ExecJS(struct Application*, const char *script);
|
|
||||||
extern void Hide(struct Application*);
|
|
||||||
extern void Show(struct Application*);
|
|
||||||
extern void Center(struct Application*);
|
|
||||||
extern void Maximise(struct Application*);
|
|
||||||
extern void Unmaximise(struct Application*);
|
|
||||||
extern void ToggleMaximise(struct Application*);
|
|
||||||
extern void Minimise(struct Application*);
|
|
||||||
extern void Unminimise(struct Application*);
|
|
||||||
extern void ToggleMinimise(struct Application*);
|
|
||||||
extern void SetColour(struct Application*, int red, int green, int blue, int alpha);
|
|
||||||
extern void SetSize(struct Application*, int width, int height);
|
|
||||||
extern void SetPosition(struct Application*, int x, int y);
|
|
||||||
extern void Quit(struct Application*);
|
|
||||||
extern void SetTitle(struct Application*, const char *title);
|
|
||||||
extern void Fullscreen(struct Application*);
|
|
||||||
extern void UnFullscreen(struct Application*);
|
|
||||||
extern void ToggleFullscreen(struct Application*);
|
|
||||||
extern void DisableFrame(struct Application*);
|
|
||||||
extern void OpenDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories);
|
|
||||||
extern void SaveDialog(struct Application*, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
|
||||||
extern void MessageDialog(struct Application*, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton);
|
|
||||||
extern void DarkModeEnabled(struct Application*, char *callbackID);
|
|
||||||
extern void SetApplicationMenu(struct Application*, const char *);
|
|
||||||
extern void AddTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void SetTrayMenu(struct Application*, const char *menuTrayJSON);
|
|
||||||
extern void DeleteTrayMenuByID(struct Application*, const char *id);
|
|
||||||
extern void UpdateTrayMenuLabel(struct Application*, const char* JSON);
|
|
||||||
extern void AddContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
extern void UpdateContextMenu(struct Application*, char *contextMenuJSON);
|
|
||||||
extern void WebviewIsTransparent(struct Application*);
|
|
||||||
extern void WindowBackgroundIsTranslucent(struct Application*);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user