mirror of
https://github.com/taigrr/wails.git
synced 2026-04-02 21:29:17 -07:00
Compare commits
59 Commits
v2-linux
...
feature/go
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02bd44f772 | ||
|
|
a84f43d959 | ||
|
|
b48da620b3 | ||
|
|
ed8884a581 | ||
|
|
05d27dda64 | ||
|
|
bd0c9dbc43 | ||
|
|
1c813622c0 | ||
|
|
76ba7d56ec | ||
|
|
fe2c5e8611 | ||
|
|
422ee22d0c | ||
|
|
22f94cfdb6 | ||
|
|
5d754f40de | ||
|
|
c9b26c6352 | ||
|
|
6f0696631f | ||
|
|
fefb54de12 | ||
|
|
b4224066f7 | ||
|
|
3d9ef75488 | ||
|
|
79ecb0704e | ||
|
|
8154887824 | ||
|
|
39ae91a250 | ||
|
|
861f5f2a1c | ||
|
|
89ed00d6ed | ||
|
|
1e1834158b | ||
|
|
214fcf03b9 | ||
|
|
82ac4f1358 | ||
|
|
8b7cd03428 | ||
|
|
4d2b4fec45 | ||
|
|
bc0478b2b2 | ||
|
|
d59849b952 | ||
|
|
806821ad49 | ||
|
|
a4cc95351f | ||
|
|
f851b89350 | ||
|
|
7f72189231 | ||
|
|
ef79dd95cf | ||
|
|
d49b146eaa | ||
|
|
8c051e004d | ||
|
|
c02b9ac032 | ||
|
|
35f839ae65 | ||
|
|
0474f15c05 | ||
|
|
ba0af0c16d | ||
|
|
c9f1247284 | ||
|
|
baa661532d | ||
|
|
43a5f410d9 | ||
|
|
0ab6a93e0c | ||
|
|
485df87560 | ||
|
|
176c447e87 | ||
|
|
aa9cb5e58e | ||
|
|
9a7be38462 | ||
|
|
678c2194db | ||
|
|
9f9c9e27de | ||
|
|
f86996705b | ||
|
|
254aa664d7 | ||
|
|
c8371ee824 | ||
|
|
02e0250555 | ||
|
|
e1b025cab6 | ||
|
|
626854f1b7 | ||
|
|
98468d1c4d | ||
|
|
1062aeb136 | ||
|
|
aa93e5d8d2 |
11
.gitignore
vendored
11
.gitignore
vendored
@@ -16,13 +16,4 @@ examples/**/example*
|
||||
cmd/wails/wails
|
||||
.DS_Store
|
||||
tmp
|
||||
node_modules/
|
||||
package.json.md5
|
||||
v2/test/**/frontend/dist
|
||||
v2/test/**/build/
|
||||
v2/test/frameless/icon.png
|
||||
v2/test/hidden/icon.png
|
||||
v2/internal/ffenestri/runtime.c
|
||||
v2/internal/runtime/assets/desktop.js
|
||||
v2/test/kitchensink/frontend/public/bundle.*
|
||||
v2/pkg/parser/testproject/frontend/wails
|
||||
node_modules/
|
||||
@@ -32,3 +32,6 @@ Wails is what it is because of the time and effort given by these great people.
|
||||
* [Zámbó, Levente](https://github.com/Lyimmi)
|
||||
* [artem](https://github.com/Unix4ever)
|
||||
* [Tim Kipp](https://github.com/timkippdev)
|
||||
* [Dmitry Gomzyakov](https://github.com/kyoto44)
|
||||
* [Arthur Wiebe](https://github.com/artooro)
|
||||
* [Ilgıt Yıldırım](https://github.com/ilgityildirim)
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<a href="https://houndci.com"><img src="https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg"/></a>
|
||||
<a href="https://github.com/avelino/awesome-go" rel="nofollow"><img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome"></a>
|
||||
<a href="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/release/badge.svg?branch=master" alt="Release Pipelines"></a>
|
||||
<a href="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=masterr" rel="nofollow"><img src="https://github.com/wailsapp/wails/workflows/latest-pre/badge.svg?branch=master" alt="Pre-Release Pipelines"></a>
|
||||
</p>
|
||||
|
||||
The traditional method of providing web interfaces to Go programs is via a built-in web server. Wails offers a different approach: it provides the ability to wrap both Go code and a web frontend into a single binary. Tools are provided to make this easy for you by handling project creation, compilation and bundling. All you have to do is get creative!
|
||||
@@ -57,7 +56,7 @@ _Ubuntu: 16.04, 18.04, 19.04_
|
||||
|
||||
_Also succesfully tested on: Zorin 15, Parrot 4.7, Linuxmint 19, Elementary 5, Kali, Neon_, Pop!_OS
|
||||
|
||||
#### Arch Linux / ArchLabs
|
||||
#### Arch Linux / ArchLabs / Ctlos Linux
|
||||
|
||||
`sudo pacman -S webkit2gtk gtk3`
|
||||
|
||||
|
||||
13
app.go
13
app.go
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/wailsapp/wails/lib/ipc"
|
||||
"github.com/wailsapp/wails/lib/logger"
|
||||
"github.com/wailsapp/wails/lib/renderer"
|
||||
wailsruntime "github.com/wailsapp/wails/runtime"
|
||||
)
|
||||
|
||||
// -------------------------------- Compile time Flags ------------------------------
|
||||
@@ -20,6 +21,16 @@ import (
|
||||
// BuildMode indicates what mode we are in
|
||||
var BuildMode = cmd.BuildModeProd
|
||||
|
||||
// Runtime is the Go Runtime struct
|
||||
type Runtime = wailsruntime.Runtime
|
||||
|
||||
// Store is a state store used for syncing with
|
||||
// the front end
|
||||
type Store = wailsruntime.Store
|
||||
|
||||
// CustomLogger is a specialised logger
|
||||
type CustomLogger = logger.CustomLogger
|
||||
|
||||
// ----------------------------------------------------------------------------------
|
||||
|
||||
// App defines the main application struct
|
||||
@@ -125,7 +136,7 @@ func (a *App) start() error {
|
||||
a.ipc.Start(a.eventManager, a.bindingManager)
|
||||
|
||||
// Create the runtime
|
||||
a.runtime = NewRuntime(a.eventManager, a.renderer)
|
||||
a.runtime = wailsruntime.NewRuntime(a.eventManager, a.renderer)
|
||||
|
||||
// Start binding manager and give it our renderer
|
||||
err = a.bindingManager.Start(a.renderer, a.runtime)
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"github.com/leaanthony/spinner"
|
||||
)
|
||||
|
||||
const xgoVersion = "1.0.1"
|
||||
|
||||
var fs = NewFSHelper()
|
||||
|
||||
// ValidateFrontendConfig checks if the frontend config is valid
|
||||
@@ -90,16 +92,17 @@ func InitializeCrossCompilation(verbose bool) error {
|
||||
}
|
||||
|
||||
var packSpinner *spinner.Spinner
|
||||
msg := fmt.Sprintf("Pulling wailsapp/xgo:%s docker image... (may take a while)", xgoVersion)
|
||||
if !verbose {
|
||||
packSpinner = spinner.New("Pulling wailsapp/xgo:latest docker image... (may take a while)")
|
||||
packSpinner = spinner.New(msg)
|
||||
packSpinner.SetSpinSpeed(50)
|
||||
packSpinner.Start()
|
||||
} else {
|
||||
println("Pulling wailsapp/xgo:latest docker image... (may take a while)")
|
||||
println(msg)
|
||||
}
|
||||
|
||||
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
|
||||
"pull", "wailsapp/xgo:latest"})
|
||||
"pull", fmt.Sprintf("wailsapp/xgo:%s", xgoVersion)})
|
||||
|
||||
if err != nil {
|
||||
if packSpinner != nil {
|
||||
@@ -114,7 +117,7 @@ func InitializeCrossCompilation(verbose bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildDocker builds the project using the cross compiling wailsapp/xgo:latest container
|
||||
// BuildDocker builds the project using the cross compiling wailsapp/xgo:<xgoVersion> container
|
||||
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
|
||||
var packSpinner *spinner.Spinner
|
||||
if buildMode == BuildModeBridge {
|
||||
@@ -146,18 +149,24 @@ func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOpt
|
||||
"-e", "FLAG_RACE=false",
|
||||
"-e", "FLAG_BUILDMODE=default",
|
||||
"-e", "FLAG_TRIMPATH=false",
|
||||
"-e", fmt.Sprintf("TARGETS=%s", projectOptions.Platform+"/"+projectOptions.Architecture),
|
||||
"-e", fmt.Sprintf("TARGETS=%s/%s", projectOptions.Platform, projectOptions.Architecture),
|
||||
"-e", "GOPROXY=",
|
||||
"-e", "GO111MODULE=on",
|
||||
"wailsapp/xgo:latest",
|
||||
".",
|
||||
} {
|
||||
buildCommand.Add(arg)
|
||||
}
|
||||
|
||||
if projectOptions.GoPath != "" {
|
||||
buildCommand.Add("-v")
|
||||
buildCommand.Add(fmt.Sprintf("%s:/go", projectOptions.GoPath))
|
||||
}
|
||||
|
||||
buildCommand.Add(fmt.Sprintf("wailsapp/xgo:%s", xgoVersion))
|
||||
buildCommand.Add(".")
|
||||
|
||||
compileMessage := fmt.Sprintf(
|
||||
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:latest",
|
||||
projectOptions.Platform, projectOptions.Architecture)
|
||||
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:%s",
|
||||
projectOptions.Platform, projectOptions.Architecture, xgoVersion)
|
||||
|
||||
if buildMode == BuildModeDebug {
|
||||
compileMessage += " (Debug Mode)"
|
||||
@@ -216,10 +225,6 @@ func BuildNative(binaryName string, forceRebuild bool, buildMode string, project
|
||||
buildCommand.Add("go")
|
||||
|
||||
buildCommand.Add("build")
|
||||
if buildMode == BuildModeBridge {
|
||||
// Ignore errors
|
||||
buildCommand.Add("-i")
|
||||
}
|
||||
|
||||
if binaryName != "" {
|
||||
// Alter binary name based on OS
|
||||
|
||||
@@ -63,6 +63,8 @@ const (
|
||||
PopOS
|
||||
// Solus distribution
|
||||
Solus
|
||||
// Ctlos Linux distribution
|
||||
Ctlos
|
||||
)
|
||||
|
||||
// DistroInfo contains all the information relating to a linux distribution
|
||||
@@ -129,6 +131,8 @@ func parseOsRelease(osRelease string) *DistroInfo {
|
||||
result.Distribution = Arch
|
||||
case "archlabs":
|
||||
result.Distribution = ArchLabs
|
||||
case "ctlos":
|
||||
result.Distribution = Ctlos
|
||||
case "debian":
|
||||
result.Distribution = Debian
|
||||
case "ubuntu":
|
||||
|
||||
@@ -193,7 +193,16 @@ distributions:
|
||||
name: ArchLabs
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
libraries: *archdefaultlibraries
|
||||
ctlos:
|
||||
id: ctlos
|
||||
releases:
|
||||
default:
|
||||
version: default
|
||||
name: Ctlos Linux
|
||||
gccversioncommand: *gccdumpversion
|
||||
programs: *archdefaultprograms
|
||||
libraries: *archdefaultlibraries
|
||||
manjaro:
|
||||
id: manjaro
|
||||
releases:
|
||||
|
||||
@@ -162,6 +162,7 @@ type ProjectOptions struct {
|
||||
Platform string
|
||||
Architecture string
|
||||
LdFlags string
|
||||
GoPath string
|
||||
}
|
||||
|
||||
// Defaults sets the default project template
|
||||
|
||||
@@ -276,7 +276,7 @@ func CheckDependencies(logger *Logger) (bool, error) {
|
||||
switch distroInfo.Distribution {
|
||||
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS:
|
||||
libraryChecker = DpkgInstalled
|
||||
case Arch, ArcoLinux, ArchLabs, Manjaro, ManjaroARM:
|
||||
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM:
|
||||
libraryChecker = PacmanInstalled
|
||||
case CentOS, Fedora, Tumbleweed, Leap:
|
||||
libraryChecker = RpmInstalled
|
||||
|
||||
@@ -5,7 +5,7 @@ const routes: Routes = [];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forRoot(routes)
|
||||
RouterModule.forRoot(routes,{useHash:true})
|
||||
],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
||||
@@ -57,26 +57,23 @@ If you're building a single-page app (SPA) with multiple routes, sirv needs to b
|
||||
"start": "sirv public --single"
|
||||
```
|
||||
|
||||
|
||||
## Deploying to the web
|
||||
|
||||
### With [now](https://zeit.co/now)
|
||||
### With [Vercel](https://vercel.com)
|
||||
|
||||
Install `now` if you haven't already:
|
||||
Install `vercel` if you haven't already:
|
||||
|
||||
```bash
|
||||
npm install -g now
|
||||
npm install -g vercel
|
||||
```
|
||||
|
||||
Then, from within your project folder:
|
||||
|
||||
```bash
|
||||
cd public
|
||||
now deploy --name my-project
|
||||
vercel deploy --name my-project
|
||||
```
|
||||
|
||||
As an alternative, use the [Now desktop client](https://zeit.co/download) and simply drag the unzipped project folder to the taskbar icon.
|
||||
|
||||
### With [surge](https://surge.sh/)
|
||||
|
||||
Install `surge` if you haven't already:
|
||||
31
cmd/templates/svelte/frontend/package.json.template
Normal file
31
cmd/templates/svelte/frontend/package.json.template
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "{{.NPMProjectName}}",
|
||||
"author": "{{.Author.Name}}<{{.Author.Email}}>",
|
||||
"scripts": {
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"start": "sirv public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-runtime": "^7.11.5",
|
||||
"@babel/preset-env": "^7.11.5",
|
||||
"@rollup/plugin-commonjs": "^14.0.0",
|
||||
"@rollup/plugin-image": "^2.0.5",
|
||||
"@rollup/plugin-node-resolve": "^8.0.0",
|
||||
"core-js": "^3.6.5",
|
||||
"rollup": "^2.3.4",
|
||||
"rollup-plugin-babel": "^4.4.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-polyfill": "^3.0.0",
|
||||
"rollup-plugin-svelte": "^6.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^1.0.0",
|
||||
"@wailsapp/runtime": "^1.0.10",
|
||||
"svelte-simple-modal": "^0.6.0"
|
||||
}
|
||||
}
|
||||
BIN
cmd/templates/svelte/frontend/public/favicon.png
Normal file
BIN
cmd/templates/svelte/frontend/public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
17
cmd/templates/svelte/frontend/public/index.html
Normal file
17
cmd/templates/svelte/frontend/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||
|
||||
<title>Svelte app</title>
|
||||
|
||||
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||
<link rel='stylesheet' href='/build/bundle.css'>
|
||||
|
||||
<script defer src='/build/bundle.js'></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,45 +3,51 @@ import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import autoPreprocess from 'svelte-preprocess';
|
||||
import { string } from "rollup-plugin-string";
|
||||
import url from '@rollup/plugin-url';
|
||||
import image from '@rollup/plugin-image';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import polyfill from 'rollup-plugin-polyfill';
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
function serve() {
|
||||
let server;
|
||||
|
||||
function toExit() {
|
||||
if (server) server.kill(0);
|
||||
}
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (server) return;
|
||||
server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
|
||||
process.on('SIGTERM', toExit);
|
||||
process.on('exit', toExit);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
input: 'src/main.js',
|
||||
output: {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: 'public/bundle.js'
|
||||
file: 'public/build/bundle.js'
|
||||
},
|
||||
onwarn: handleRollupWarning,
|
||||
plugins: [
|
||||
|
||||
// Embed binary files
|
||||
url({
|
||||
include: ['**/*.woff', '**/*.woff2'],
|
||||
limit: Infinity,
|
||||
}),
|
||||
|
||||
// Embed text files
|
||||
string({
|
||||
include: ["**/*.jsx","**/*.go", "**/*.txt"],
|
||||
}),
|
||||
|
||||
image(),
|
||||
svelte({
|
||||
preprocess: autoPreprocess(),
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css: css => {
|
||||
css.write('public/bundle.css');
|
||||
},
|
||||
emitCss: true,
|
||||
css.write('bundle.css');
|
||||
}
|
||||
}),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
@@ -51,25 +57,10 @@ export default {
|
||||
// https://github.com/rollup/plugins/tree/master/packages/commonjs
|
||||
resolve({
|
||||
browser: true,
|
||||
dedupe: ['svelte']
|
||||
dedupe: ['svelte', 'svelte/transition', 'svelte/internal']
|
||||
}),
|
||||
commonjs(),
|
||||
|
||||
// PostCSS preprocessing
|
||||
postcss({
|
||||
extensions: ['.css', '.scss'],
|
||||
extract: true,
|
||||
minimize: false,
|
||||
use: [
|
||||
['sass', {
|
||||
includePaths: [
|
||||
'./src/theme',
|
||||
'./node_modules'
|
||||
]
|
||||
}]
|
||||
],
|
||||
}),
|
||||
|
||||
// In dev mode, call `npm run start` once
|
||||
// the bundle has been generated
|
||||
!production && serve(),
|
||||
@@ -78,6 +69,37 @@ export default {
|
||||
// browser on changes when not in production
|
||||
!production && livereload('public'),
|
||||
|
||||
// Credit: https://blog.az.sg/posts/svelte-and-ie11/
|
||||
babel({
|
||||
extensions: [ '.js', '.jsx', '.es6', '.es', '.mjs', '.svelte', '.html' ],
|
||||
runtimeHelpers: true,
|
||||
exclude: [ 'node_modules/@babel/**', 'node_modules/core-js/**' ],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: '> 0.25%, not dead, IE 11',
|
||||
modules: false,
|
||||
spec: true,
|
||||
useBuiltIns: 'usage',
|
||||
forceAllTransforms: true,
|
||||
corejs: 3,
|
||||
},
|
||||
|
||||
]
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
[
|
||||
'@babel/plugin-transform-runtime',
|
||||
{
|
||||
useESModules: true
|
||||
}
|
||||
]
|
||||
]
|
||||
}),
|
||||
polyfill(['@webcomponents/webcomponentsjs']),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser()
|
||||
@@ -86,25 +108,3 @@ export default {
|
||||
clearScreen: false
|
||||
}
|
||||
};
|
||||
|
||||
function handleRollupWarning(warning) {
|
||||
console.error('ERROR: ' + warning.toString());
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function serve() {
|
||||
let started = false;
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (!started) {
|
||||
started = true;
|
||||
|
||||
require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], {
|
||||
stdio: ['ignore', 'inherit', 'inherit'],
|
||||
shell: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
69
cmd/templates/svelte/frontend/src/App.svelte
Normal file
69
cmd/templates/svelte/frontend/src/App.svelte
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
|
||||
<script>
|
||||
import Modal from 'svelte-simple-modal';
|
||||
import HelloWorld from './components/HelloWorld.svelte'
|
||||
import logo from './logo.png';
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="App">
|
||||
<header class="App-header">
|
||||
<Modal>
|
||||
<img src={logo} class="App-logo" alt="logo" />
|
||||
<p>Welcome to your new <code>wails/svelte</code> project.</p>
|
||||
<HelloWorld/>
|
||||
</Modal>
|
||||
</header>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
:global(body) {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { getContext } from 'svelte';
|
||||
import ModalContent from './ModalContent.svelte'
|
||||
|
||||
const { open } = getContext('simple-modal');
|
||||
|
||||
const handleOpenModal = () => {
|
||||
window.backend.basic().then((result) => {
|
||||
open(ModalContent, { message: result });
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<p><button on:click={handleOpenModal}>Hello</button></p>
|
||||
</main>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script>
|
||||
export let message;
|
||||
</script>
|
||||
|
||||
<p>
|
||||
{message}
|
||||
</p>
|
||||
|
Before Width: | Height: | Size: 301 KiB After Width: | Height: | Size: 301 KiB |
13
cmd/templates/svelte/frontend/src/main.js
Normal file
13
cmd/templates/svelte/frontend/src/main.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import App from './App.svelte';
|
||||
|
||||
import * as Wails from '@wailsapp/runtime';
|
||||
|
||||
let app;
|
||||
|
||||
Wails.Init(() => {
|
||||
app = new App({
|
||||
target: document.body,
|
||||
});
|
||||
});
|
||||
|
||||
export default app;
|
||||
5
cmd/templates/svelte/go.mod.template
Normal file
5
cmd/templates/svelte/go.mod.template
Normal file
@@ -0,0 +1,5 @@
|
||||
module {{.BinaryName}}
|
||||
|
||||
require (
|
||||
github.com/wailsapp/wails {{.WailsVersion}}
|
||||
)
|
||||
27
cmd/templates/svelte/main.go.template
Normal file
27
cmd/templates/svelte/main.go.template
Normal file
@@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/leaanthony/mewn"
|
||||
"github.com/wailsapp/wails"
|
||||
)
|
||||
|
||||
func basic() string {
|
||||
return "World!"
|
||||
}
|
||||
|
||||
func main() {
|
||||
js := mewn.String("./frontend/public/build/bundle.js")
|
||||
css := mewn.String("./frontend/public/build/bundle.css")
|
||||
|
||||
app := wails.CreateApp(&wails.AppConfig{
|
||||
Width: 1024,
|
||||
Height: 768,
|
||||
Title: "{{.Name}}",
|
||||
JS: js,
|
||||
CSS: css,
|
||||
Colour: "#131313",
|
||||
})
|
||||
|
||||
app.Bind(basic)
|
||||
app.Run()
|
||||
}
|
||||
14
cmd/templates/svelte/template.json
Executable file
14
cmd/templates/svelte/template.json
Executable file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Svelte",
|
||||
"version": "1.0.0",
|
||||
"shortdescription": "A basic Svelte template",
|
||||
"description": "A basic Svelte template",
|
||||
"install": "npm install",
|
||||
"build": "npm run build",
|
||||
"author": "Tim Kipp <timkipp.22.developer@gmail.com>",
|
||||
"created": "2020-09-06 13:06:10.469848 -0700 PDT m=+213.578828559",
|
||||
"frontenddir": "frontend",
|
||||
"serve": "npm run dev",
|
||||
"bridge": "src",
|
||||
"wailsdir": ""
|
||||
}
|
||||
46
cmd/templates/vanilla/counter.go
Normal file
46
cmd/templates/vanilla/counter.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/wailsapp/wails"
|
||||
)
|
||||
|
||||
// Counter is what we use for counting
|
||||
type Counter struct {
|
||||
r *wails.Runtime
|
||||
store *wails.Store
|
||||
}
|
||||
|
||||
// WailsInit is called when the component is being initialised
|
||||
func (c *Counter) WailsInit(runtime *wails.Runtime) error {
|
||||
c.r = runtime
|
||||
c.store = runtime.Store.New("Counter", 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RandomValue sets the counter to a random value
|
||||
func (c *Counter) RandomValue() {
|
||||
c.store.Set(rand.Intn(1000))
|
||||
}
|
||||
|
||||
// Increment will increment the counter
|
||||
func (c *Counter) Increment() {
|
||||
|
||||
increment := func(data int) int {
|
||||
return data + 1
|
||||
}
|
||||
|
||||
// Update the store using the increment function
|
||||
c.store.Update(increment)
|
||||
}
|
||||
|
||||
// Decrement will decrement the counter
|
||||
func (c *Counter) Decrement() {
|
||||
|
||||
decrement := func(data int) int {
|
||||
return data - 1
|
||||
}
|
||||
// Update the store using the decrement function
|
||||
c.store.Update(decrement)
|
||||
}
|
||||
@@ -2,20 +2,35 @@
|
||||
html,
|
||||
body {
|
||||
background-color: white;
|
||||
width: 1024px;
|
||||
height: 768px;
|
||||
color: black;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
background-color: rgb(254,254,254);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
width:100%;
|
||||
text-align: center;
|
||||
margin-top: 3rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1rem;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 1rem;
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
|
||||
@@ -4,6 +4,8 @@ const runtime = require('@wailsapp/runtime');
|
||||
// Main entry point
|
||||
function start() {
|
||||
|
||||
var mystore = runtime.Store.New('Counter');
|
||||
|
||||
// Ensure the default app div is 100% wide/high
|
||||
var app = document.getElementById('app');
|
||||
app.style.width = '100%';
|
||||
@@ -13,17 +15,32 @@ function start() {
|
||||
app.innerHTML = `
|
||||
<div class='logo'></div>
|
||||
<div class='container'>
|
||||
<button id='button'>Click Me!</button>
|
||||
<div id='result'/>
|
||||
<button onClick='window.backend.Counter.Increment()'>
|
||||
Increment Counter
|
||||
</button>
|
||||
<button onClick='window.backend.Counter.Decrement()'>
|
||||
Decrement Counter
|
||||
</button>
|
||||
</div>
|
||||
<div class='result'>Counter: <span id='counter'></span></div>
|
||||
<div class='container'>
|
||||
<input id='newCounter' type="number" value="0"/>
|
||||
<button id='setvalue'>Set Counter Value</button>
|
||||
<button onclick='window.backend.Counter.RandomValue()'>Set to Random Value</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Connect button to Go method
|
||||
document.getElementById('button').onclick = function() {
|
||||
window.backend.basic().then( function(result) {
|
||||
document.getElementById('result').innerText = result;
|
||||
});
|
||||
// Connect counter value button to Go method
|
||||
document.getElementById('setvalue').onclick = function() {
|
||||
let newValue = parseInt(document.getElementById('newCounter').value,10);
|
||||
mystore.set(newValue);
|
||||
};
|
||||
|
||||
mystore.subscribe( function(state) {
|
||||
document.getElementById('counter').innerText = state;
|
||||
});
|
||||
|
||||
mystore.set(0);
|
||||
};
|
||||
|
||||
// We provide our entrypoint as a callback for runtime.Init
|
||||
|
||||
@@ -5,10 +5,6 @@ import (
|
||||
"github.com/wailsapp/wails"
|
||||
)
|
||||
|
||||
func basic() string {
|
||||
return "Hello World!"
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
js := mewn.String("./frontend/build/main.js")
|
||||
@@ -22,6 +18,6 @@ func main() {
|
||||
CSS: css,
|
||||
Colour: "#131313",
|
||||
})
|
||||
app.Bind(basic)
|
||||
app.Bind(&Counter{})
|
||||
app.Run()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cmd
|
||||
|
||||
// Version - Wails version
|
||||
const Version = "v1.7.2-pre3"
|
||||
const Version = "v1.8.1-pre1"
|
||||
|
||||
@@ -26,6 +26,7 @@ func init() {
|
||||
var packageApp = false
|
||||
var forceRebuild = false
|
||||
var debugMode = false
|
||||
var gopath = ""
|
||||
var typescriptFilename = ""
|
||||
var verbose = false
|
||||
var platform = ""
|
||||
@@ -42,7 +43,8 @@ func init() {
|
||||
BoolFlag("d", "Build in Debug mode", &debugMode).
|
||||
BoolFlag("verbose", "Verbose output", &verbose).
|
||||
StringFlag("t", "Generate Typescript definitions to given file (at runtime)", &typescriptFilename).
|
||||
StringFlag("ldflags", "Extra options for -ldflags", &ldflags)
|
||||
StringFlag("ldflags", "Extra options for -ldflags", &ldflags).
|
||||
StringFlag("gopath", "Specify your GOPATH location. Mounted to /go during cross-compilation.", &gopath)
|
||||
|
||||
var b strings.Builder
|
||||
for _, plat := range getSupportedPlatforms() {
|
||||
@@ -97,6 +99,7 @@ func init() {
|
||||
|
||||
// Add ldflags
|
||||
projectOptions.LdFlags = ldflags
|
||||
projectOptions.GoPath = gopath
|
||||
|
||||
// Validate config
|
||||
// Check if we have a frontend
|
||||
|
||||
2
go.mod
2
go.mod
@@ -22,7 +22,7 @@ require (
|
||||
github.com/syossan27/tebata v0.0.0-20180602121909-b283fe4bc5ba
|
||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8
|
||||
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
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4
|
||||
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-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-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/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/AlecAivazis/survey.v1 v1.8.4 h1:10xXXN3wgIhPheb5NI58zFgZv32Ana7P3Tl4shW+0Qc=
|
||||
|
||||
@@ -196,7 +196,7 @@ func (b *boundMethod) processWailsInit() error {
|
||||
// It must be *wails.Runtime
|
||||
inputName := b.inputs[0].String()
|
||||
b.log.Debugf("WailsInit input type: %s", inputName)
|
||||
if inputName != "*wails.Runtime" {
|
||||
if inputName != "*runtime.Runtime" {
|
||||
return fmt.Errorf("Invalid WailsInit() definition. Expected input to be wails.Runtime, but got %s", inputName)
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func (b *boundMethod) processWailsInit() error {
|
||||
}
|
||||
|
||||
func (b *boundMethod) processWailsShutdown() error {
|
||||
// We must have only 1 input, it must be *wails.Runtime
|
||||
// We must not have any inputs
|
||||
if len(b.inputs) != 0 {
|
||||
return fmt.Errorf("Invalid WailsShutdown() definition. Expected 0 inputs, but got %d", len(b.inputs))
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ package renderer
|
||||
import "github.com/leaanthony/mewn"
|
||||
|
||||
func init() {
|
||||
mewn.AddAsset(".", "../../runtime/assets/wails.js", "1f8b08000000000000ff94586d8f1bb711fe2b2ba255481f8f91dc14a977c318cee582a838df053ebbfda00a01b53b92e85b915b2e758a20f1bf17b3efba97a2050c7bc519728633cf3c33f468b533a9d7d650c38e8fca45208f216917234f1d3bea1585b95b30077ee74c84df02fe28acf365825bacc42579d4b1e3793c9af246181f43489a4d0637a52acfa96df772cbfb6fcfb815b91c4dfab5e0c5561aee452a817b91c9de550edcb1a317163fd9e974b7fc0aa91719acb481df9c2dc0f943a57604b3db8253cb1ce2d184afc1c72eb0c0bd70727875b233f5ee8c8ca43f146057d1fd61bbb4f9785cff2bbcbdf74e9bf567b51e8f5fb3f85c971f1f55be83987cb4d92e0712187f6d33f9fd77281bb576db6852bbebcfae5f25653a86f1981ae9a9618cff6d0c6d864ca257f43b94125b9922b2bd93198ff18fe82df59b30974e36cea50e94076a7679cef0382f1c75afb9ee38c960a576b9274f235edfc204c6df560e95555cfa201bb6b28e56308ab4890cf322a38e5bde5d17d8b103112c82586a93557e71cb588b2f873132f2399a9fdcf67da7d19f2a1adf43fc82b04330fa059c28c281714073f6494a1ac5264485b3dee225c54695777bd306abae02dc8067149210eea917a59cb040e76718f788cb12228c59ea49823900c61b4f66467bc2e9739f6781b12699c7903489abc34a7e86e56efde2ae5d609dd6ccacec8b4af940e99fca194ce54b7aab81deb573d6bda8950db47e515ee52f6a95ed756c7b1d5befb29cdc1560be7cba7971dfa13dbdd1fb456359bda0b86c0d68395ff4dca7da1c34a1c45cc68663fa962a7d98fd1c7b5ea8436e55164348ce9854afe85e9bcceec55ee9bc5c3a9dade1fdf325b1876569d307f0a20484358b1b25f8c383332a17da3cda07a086712d72306bbff971d2d50cc849023fb482042e2e989ec3821a16e8dfefef6e455d6e7a75a08eb1d0dd2dad11ab28c9ed9af0630e8f90c7866fa12cd51a620803e51dde28a524abb16306a2bc11e90a3043c9aa91ec5b940c855923841a1a4351d98856351e0c0b78cf3d2fcefad2639b9bb64c77793e927e3c6e3e4e27eae5847103fbe83767b7ba04da67de715b675527993d6a692ec825b9d85316f61b9d032de67a5193de8f13867aa92cc17fd65bb03b4f8708b2b4423725572acf236f2372612e48e4f516b2c8eebc883ec1bf7750fa68f6731c910bcd5860dcb3044dc8a3af8ffc55992c8738e50e903a62cb1d94367f84d885c4bb03e6096147f811c94f9bf5adda221633e555fc24cfc82b9a8554f97453c5d29ad2e620aa582330c21008eb33ea7aa4a446a720171543757a073caad15a53f293b3fb129ce8ea6f98c2e5ebba750d1a16f6b2c179ea0e85b7435ac6801b8999fba28dffcbdb0fcea9039d76547fb651acc17f5226b3db7f60a341f4cc278b9798fcdd64f2fdf4ddbbb77ffdeefbef26efde4ddf7c547e235cb597b25011c0b663f4c8d00a5fa3675d77444da44de99549b17f01637ee3ec3e42873f1f0ae800618cf511a62d52519aabb28c5419a9a83d90b040fd4697dc30ee11b197538ebfc555433067cdac9dbd842a8afc50b73fc3f8e5742411f6132925f59772ca42e09bb352b96b4b6533370b897f9d4ef345d31fd0e96d75d30425a2d8951bea06b9bcee3be9139ccd17a2cc75da7aa4dc7ab705e34b562fd329b2af3c9a015221248a127804e309f77565df9f39fb80e610f16d5dc33efaa50d03417d7261181f4d7a7cd79aa369e89dbeeab24eafda794695a55e9bd36918d49e45a709fcd0dd60c8a6d5ddbdec64735824ed3687038b67ff53bff7dc311cd67050f6384c876ec0604d042b30f4610c0dce110a6032790c7cd83fe4f0c7e9d44b7bfd2ac75fe5f116f63fd5b4113f9f8fe60b915a932a4f8d288b5c7b4a0461d56797c4f3939118a16d45d3ae15393949dc0fade0729ab80b39adadd40f04dc377aa096b141729b6a21ad139693489711968e8a1e55aeb3e8ab7a5465ea74e1239d81f17aa5c109c258e2e776814c3fb70b790c5519cded22d4ad1c44610bca1aabfabf5ad5ff9f55bd904ff80afbf0e0d1d4c2a6ab910a037d6e938e6f0df7c8b3dd8cd9f799b3f20769705a5cbf20edb921701f280bbc2590e7d94e8ccc20b5197cf934bbb2dbc21a3098780745ae52a0dffeabbcf876cd0961fdd27c72f94e5dae16c7b701457ffed3b80a823b1c1b4a28942b713e694a12dacb9399a943895a51a1ca12326c91ed0015475dfc413483072722fad8cc2011611d34595293ec8efabaa9d7196c38c4c93ac278aaceb895450bb7b6cf4bd20625faa6b3ea38f9a6cab983b52e3d64a3d188b496cedba61e5ac5ee9a8372ed4060c5591f673c831c3c44e8056f4e786f45dddc69b3c0625ca9ba3c0581f4c802bfb55eaf0ef1b39e83dcdcd395af58bca159c6ebc2ab961ae21a561eaea31fd56c8b95cb30757a983a18a42ea3e76943c7a2128cc7cc99ca3d115d238347c8ec91ac0839d8ae6b51cdc663dff287e35316aac6e343e01fb2ecbe2aa92737ac2f95d9b42a8fe6e9799d03fea2a4ae424c8cf0f0879786779a4b9b1d903dc164571b9d67880e188fafab21686630de57f7f72fb0deabb6fc2107c212c042fce0bdd3cb9d074a90d7092768ffdbb42c09e3202addfb0d807f3ffc21d2b2fc5cf919c3996f4f6ca2ceadcda07abcf7c20da8ec74ea7eaec137ce953f1d3eab6af0a30495080e3aeccc427d6bede319867af6dbd50dc2da803b8b80aebb3c4e82fc561e6fec3a76bc19d262cbabe496f1f1ce3cc9d25df368bd331f77b9d7450ef11dbfde6a1f5ff35f4139bf04759ed9fef184f34a728f3818f056554433e3c13daabc7aa3227b399cb5bbd5e1b08db34860950f1fd20763f739646b889fbcbb466866309365dd2ca6fa4dd1cee0b78936ade3035e309c7c831c8707518cd2eff1d7c18c32434386b27075f6c6e3b7ac6dc1d654257efe1f569e57cf8ee1167163d7a2e9456fdebc79135da9dd7ae3a32f6653314956134e8432c2f86b5b7bc2c4d9e835ad2f9f6e50035ed7b8d106a25b8b5afe75ad2b9befb6a6d173afeb5521401dcb02bfc687a0cecb185fcb901116162cf94f000000ffff04075a367c140000")
|
||||
mewn.AddAsset(".", "../../runtime/assets/wails.js", "1f8b08000000000000ff94587f6f1b37d2fe2a2be27d5532a6b752ae875e56dd06a9eba23e38761127d73f744241ed8e24262b52478eac0812bffb61f6b76cb9770704b1440ec9e13ccfcc3cd460b135196a6bb8118747e5224c0f61d20c46c09d38e805c7a99b0907b87526a2cf317cdd58877e424b6c4a43e941274e16c9602cebc9e410c2a45e646851a68a82db66adb4b2fb0c42dab84807a36e2c40bc4e8d84384b51429ca79dab12a51307882d7d14c7e3fdfc336418e7b0d0067e7376030ef7a5d901cc760d4ecd0b480623b9044c5c104142ecd2fed5d9d654ab73364871bf01bb881ef6ebb92d86c3ea6f8cf6019d36cb8f6a391cbe74e2735b797854c51612f6dee6db025810f2a5c5ec8f3fc0d766cdb2c1a872174fae5f82321ee270c84d0adc0821ff36c4062133d10bfe1dcd325b1ec5d2e64e6638a47f717752b788b07469ed5ce6402170b32d0a41db41ecb87bc97527590e0bb52d903d8d78750b13847c5d3ae4cbb874413662611d2f69146913190171ce9db4b2bd2e8a434b229c8578ae4d5efa25ad100dbf1cc5c8a4cfd9fce4b66f5b8b6ed7b8f63d2467265b06935f2899621285443ace3e81a436ac43b471162d5d325e297fbf334db0aa2ca005b4c726654c0287d8a72311f8f484e340bcf41051cc326413c20085ac3db9311a99e4cf7dfe1a84a8c13c84490d5c1556f633ccb7cbb3ab8a205aab1bb3b0678d7ccfe877e50c4179ce6ed1b3bb76cebab35679cfea1785aa386bb56bae639bebd86a9595ec7e03e6d387dbb3ebf6cdeeb5dd2f9ad2ea8ce1bc39403707e86aa196ec0e7667d75c356b543a9d75f5326b70abc34ff8274612e473957db9f93901b951fbc2aa3cc13039a9be7ac177dae47617ef942efcdce97c096f9f0fc53b987b9b7d018c3d502a88a43682af08cea822d6e6d17ea1b220555c8059e2eac7519b67988e26f8433331c18b0ba1a638e34604fef787fbbbb84a51bdd873274468efb6ad589e7156d8259387021ea1488c5c83f76a0909869e714137da7296577c33bd295f4fe99264fd99453db36b98d59fcceb49a8e8d49fdad5538b8a434604bae7463e9ef4b265834d93dadba218a4301cd61f8e470ee9484803bbe83767d7da03ef9077d256a8ea496e0f3a3517ec925d6cb808bb952e803f4ef5ac2a943f8e44450c0ff851afc16e91f719647999119c5da9a288d046ecc25cb008f51af2c86e318e3ec0bfb6e031baf93989d88516220809624247a407acb6fc5599bc80444907546e122b1d785b3c42e2c204dd9e7022da3179a082a9cdf24ead898bb942953cc1996a9116215398ad28969935de161097b12662843e115627e56ec959c5ce985d9455adb5dbd356b5d58ab39f9cdd7970719bb37d08e72fdb56796b44d8a435cf33b7dfa0ed97720ab84909b94fdae05f5ebf734eedf9b86d0f270be325e0076572bbfe073527e2e374343b57fddf8c46df8fdfbc79fdd7efbeff6ef4e6cdf8d57b85abd8956bb908650158b75d2032bce4d7e059a71e701369e351998c7a1e0a812b67771139fc71bf819610c6588c08b6484559a1bc8f948f54d46cc844e0b8d25e1a2181187b3996f43dbeaa0bcc49036cf45aac369b625fb54c23e4e5789012ed47699a72b84cc72204797d922af74daa5c4fcd2ca5ff8ec7e9acee29e4f4babce98466e2cdd6afb8eb61f9d075df273c9bce625fe80c6a8f945b6ed760d08b6a988fa962a707d3632a8649c6193c824126a1caecbb1367bfd071c4f826af6117fdd28481913dbb30420e461dbf2bcbc138744e5f5560913948975254299b07fdfa5ba356a3f53b0d45da478499361ab52ab4879c35dd6a3a6be85762e4b7739f393d8713906c153f234285a4879ed24371801465df87f89a42e1e3ebb546aa94baf089df9b2cf1681d24db4dae10f2f97ee1ac4130794259f9040610a4b617d65dab6cd5ab4d461c0c0741c95e3953ed764e5571101347ce96b5e39c83f7e6cfdc23b236de9d3a507166a39c2f1b18a4e63ff92a84c4e1b0891e2924fadc41fbb94d68feb991b7ca7bbd34c7637fbfae418ea94136e4ec37ca8a1f693b37c5d9a459e648bf82f8afe41f482748bbd3bb09e86d155abd29eae428f3bccb9050c7b80e5c7a388d7adaff723c76b39d7dc9ca9bf47007bb9faa8e903c07763a8b336b3285dcc47e5310c76226ca8f6d7e9eee4c59828dca18b72ac3a5a389fba199b81c4fdc453aae4ea9de8b65767de156885eded6a9c51a27ac644d86a9e851153a8f3eab474579b4c148e760502f34b898093181a99d51139fda597a0865859cda59a8941dc61bbbe1a23e55ffe9a9fa7f3b55cfd227ad882456ef0dddd0a62d7f25073a6c276d2b3512a885b64f8e4e429c2421a6861e0fcb33b35dd90f1202174136bde139da1393e690d91c3e7db8b9b2eb8d35600878079b4265c0bffda7bff876291913ddd07474f9465d2e6687d781a6feffff8614042abf789ab975b56d6a6aca6e4c154ab28a36ca7bc849fd34da3889daf8635c6b4ac9e2e87d2d2f23265a6a8a4955890b0e955eab10acdb834b316e76d5b9b4e96343b746c2a5ac094af44d7baa93ec9b1273074bed11f2c160c09a934e1591ee9f4ac2a900e51aad67e3138926640e052044e485c46a87b736ae741baf07444223a580e31853e71341de59d48b7df24c4e50dbedca15940dbaeea0425689570ed585ab9f79344e7e68ea4e94b982a0d37de8b0075dce4f6123c7220f06093953ba174765c18fa8694769d96b836d0509d7623884a67e383916a1d41410827c97e70f654a3db96175a9dc66657ad4bf445c1740df38abb2908909c4085f3135b2b59cdb7c4fd5134c7eb5d2454eecc0e1f0a1ec513786e27df5f070a6eabd7816ee0b200e5022be43747abe45e08cea3a938cceff36f39e09897169fbb002c0b7fd2f71e6fdc7d2cf044f7c7b7226d9dcd9bcfa2da79b5c81ca8fc7f6eb12b076ceffb4ffa84a4dcf191931d2b0e2e484ead61a93af14ea9bdfae6e89d606dc4904542b4082bc4d0fb776993859ebefc4caaa9b27877bf304a5fbfa378c7bf37e5ba0de1490dc4b5225c983fc1594c339a85364bb773149d1c91df1a057b7ca24ba3108ee5115e54f1654bd1c3da3dad1fe3b8a646610a50fefb22fc6ee0ac897903c79520fe8989edcce5b99adba45d1d6d06713ad1ac77b75c148f60dd538da8853941e4a29a3e51fc94d4f867ead2489089f4f9ef1f25634add89a32d54f7fc70459be2c4f14d4ad5dc6754f7af5ead5abe84a6d972b8c3e99555951f2aaf04434c7847c6969573849febe64f5e9c32d59e0cb16b7da407467c90a5eb6bab2c5766d6a3bf7b25d1902b2b122c88746221656e5a49cc34c4cfe1d0000ffff6a61b37f93160000")
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1431,6 +1431,13 @@ struct webview_priv
|
||||
style = WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU;
|
||||
}
|
||||
|
||||
// Scale
|
||||
// Credit: https://github.com/webview/webview/issues/54#issuecomment-379528243
|
||||
HDC hDC = GetDC(NULL);
|
||||
w->width = GetDeviceCaps(hDC, 88)*w->width/96.0;
|
||||
w->height = GetDeviceCaps(hDC, 90)*w->height/96.0;
|
||||
ReleaseDC(NULL, hDC);
|
||||
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = w->width;
|
||||
|
||||
32
runtime.go
32
runtime.go
@@ -1,32 +0,0 @@
|
||||
package wails
|
||||
|
||||
import (
|
||||
"github.com/wailsapp/wails/lib/interfaces"
|
||||
"github.com/wailsapp/wails/lib/logger"
|
||||
"github.com/wailsapp/wails/runtime"
|
||||
)
|
||||
|
||||
// CustomLogger type alias
|
||||
type CustomLogger = logger.CustomLogger
|
||||
|
||||
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
|
||||
type Runtime struct {
|
||||
Events *runtime.Events
|
||||
Log *runtime.Log
|
||||
Dialog *runtime.Dialog
|
||||
Window *runtime.Window
|
||||
Browser *runtime.Browser
|
||||
FileSystem *runtime.FileSystem
|
||||
}
|
||||
|
||||
// NewRuntime creates a new Runtime struct
|
||||
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
|
||||
return &Runtime{
|
||||
Events: runtime.NewEvents(eventManager),
|
||||
Log: runtime.NewLog(),
|
||||
Dialog: runtime.NewDialog(renderer),
|
||||
Window: runtime.NewWindow(renderer),
|
||||
Browser: runtime.NewBrowser(),
|
||||
FileSystem: runtime.NewFileSystem(),
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -15,6 +15,7 @@ import { NewBinding } from './bindings';
|
||||
import { Callback } from './calls';
|
||||
import { AddScript, InjectCSS } from './utils';
|
||||
import { AddIPCListener } from './ipc';
|
||||
import * as Store from './store';
|
||||
|
||||
// Initialise global if not already
|
||||
window.wails = window.wails || {};
|
||||
@@ -42,6 +43,7 @@ var runtime = {
|
||||
Heartbeat,
|
||||
Acknowledge,
|
||||
},
|
||||
Store,
|
||||
_: internal,
|
||||
};
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ The lightweight framework for web-like apps
|
||||
*/
|
||||
export function New(name, optionalDefault) {
|
||||
|
||||
// This is the store state
|
||||
var state;
|
||||
var data;
|
||||
|
||||
// Check we are initialised
|
||||
if( !window.wails) {
|
||||
@@ -30,51 +29,46 @@ export function New(name, optionalDefault) {
|
||||
let callbacks = [];
|
||||
|
||||
// Subscribe to updates by providing a callback
|
||||
let subscribe = function(callback) {
|
||||
this.subscribe = (callback) => {
|
||||
callbacks.push(callback);
|
||||
};
|
||||
|
||||
// sets the store state to the provided `newstate` value
|
||||
let set = function(newstate) {
|
||||
// sets the store data to the provided `newdata` value
|
||||
this.set = (newdata) => {
|
||||
|
||||
state = newstate;
|
||||
data = newdata;
|
||||
|
||||
// Emit a notification to back end
|
||||
window.wails.Events.Emit('wails:sync:store:updatedbyfrontend:'+name, JSON.stringify(state));
|
||||
window.wails.Events.Emit('wails:sync:store:updatedbyfrontend:'+name, JSON.stringify(data));
|
||||
|
||||
// Notify callbacks
|
||||
callbacks.forEach( function(callback) {
|
||||
callback(state);
|
||||
callback(data);
|
||||
});
|
||||
};
|
||||
|
||||
// update mutates the value in the store by calling the
|
||||
// provided method with the current value. The value returned
|
||||
// by the updater function will be set as the new store value
|
||||
let update = function(updater) {
|
||||
var newValue = updater(state);
|
||||
this.update = (updater) => {
|
||||
var newValue = updater(data);
|
||||
this.set(newValue);
|
||||
};
|
||||
|
||||
// get returns the current value of the store
|
||||
let get = function() {
|
||||
return state;
|
||||
};
|
||||
|
||||
// Setup event callback
|
||||
window.wails.Events.On('wails:sync:store:updatedbybackend:'+name, function(jsonEncodedState) {
|
||||
window.wails.Events.On('wails:sync:store:updatedbybackend:'+name, function(result) {
|
||||
|
||||
// Parse state
|
||||
let newState = JSON.parse(jsonEncodedState);
|
||||
// Parse data
|
||||
result = JSON.parse(result);
|
||||
|
||||
// Todo: Potential preprocessing?
|
||||
|
||||
// Save state
|
||||
state = newState;
|
||||
// Save data
|
||||
data = result;
|
||||
|
||||
// Notify callbacks
|
||||
callbacks.forEach( function(callback) {
|
||||
callback(state);
|
||||
callback(data);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -84,10 +78,5 @@ export function New(name, optionalDefault) {
|
||||
this.set(optionalDefault);
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
get,
|
||||
set,
|
||||
update,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
1103
runtime/js/package-lock.json
generated
1103
runtime/js/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -13,10 +13,12 @@ const Log = require('./log');
|
||||
const Browser = require('./browser');
|
||||
const Events = require('./events');
|
||||
const Init = require('./init');
|
||||
const Store = require('./store');
|
||||
|
||||
module.exports = {
|
||||
Log: Log,
|
||||
Browser: Browser,
|
||||
Events: Events,
|
||||
Init: Init
|
||||
Init: Init,
|
||||
Store: Store,
|
||||
};
|
||||
492
runtime/js/runtime/package-lock.json
generated
Normal file
492
runtime/js/runtime/package-lock.json
generated
Normal file
@@ -0,0 +1,492 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
||||
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1",
|
||||
"wrap-ansi": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||
"dev": true
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
|
||||
"dev": true
|
||||
},
|
||||
"dts-dom": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/dts-dom/-/dts-dom-3.6.0.tgz",
|
||||
"integrity": "sha512-on5jxTgt+A6r0Zyyz6ZRHXaAO7J1VPnOd6+AmvI1vH440AlAZZNc5rUHzgPuTjGlrVr1rOWQYNl7ZJK6rDohbw==",
|
||||
"dev": true
|
||||
},
|
||||
"dts-gen": {
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/dts-gen/-/dts-gen-0.5.8.tgz",
|
||||
"integrity": "sha512-kIAV6dlHaF7r5J+tIuOC1BJls2P72YM0cyWQUR88zcJEpX2ccRZe+HmXLfkkvfPwjvSO3FEqUiyC8On/grx5qw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dts-dom": "^3.6.0",
|
||||
"parse-git-config": "^1.1.1",
|
||||
"typescript": "^3.5.1",
|
||||
"yargs": "^4.8.1"
|
||||
}
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-exists": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fs-exists-sync": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
|
||||
"integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
|
||||
"dev": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
|
||||
"dev": true
|
||||
},
|
||||
"git-config-path": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/git-config-path/-/git-config-path-1.0.1.tgz",
|
||||
"integrity": "sha1-bTP37WPbDQ4RgTFQO6s6ykfVRmQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"fs-exists-sync": "^0.1.0",
|
||||
"homedir-polyfill": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
|
||||
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
|
||||
"dev": true
|
||||
},
|
||||
"homedir-polyfill": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
|
||||
"integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parse-passwd": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
|
||||
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"dev": true
|
||||
},
|
||||
"invert-kv": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
|
||||
"dev": true
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
|
||||
"dev": true
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-utf8": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
|
||||
"integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
|
||||
"dev": true
|
||||
},
|
||||
"lcid": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
||||
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"invert-kv": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"parse-json": "^2.2.0",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0",
|
||||
"strip-bom": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"lodash.assign": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
|
||||
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"hosted-git-info": "^2.1.4",
|
||||
"resolve": "^1.10.0",
|
||||
"semver": "2 || 3 || 4 || 5",
|
||||
"validate-npm-package-license": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"os-locale": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
||||
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lcid": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"parse-git-config": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/parse-git-config/-/parse-git-config-1.1.1.tgz",
|
||||
"integrity": "sha1-06mYQxcTL1c5hxK7pDjhKVkN34w=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"fs-exists-sync": "^0.1.0",
|
||||
"git-config-path": "^1.0.1",
|
||||
"ini": "^1.3.4"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
|
||||
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"error-ex": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"parse-passwd": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
|
||||
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
|
||||
"dev": true
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-type": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
||||
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"pify": "^2.0.0",
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
|
||||
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
|
||||
"dev": true
|
||||
},
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
|
||||
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"load-json-file": "^1.0.0",
|
||||
"normalize-package-data": "^2.3.2",
|
||||
"path-type": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"read-pkg-up": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
|
||||
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^1.0.0",
|
||||
"read-pkg": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"dev": true
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
|
||||
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
|
||||
"dev": true
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
|
||||
"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"spdx-expression-parse": "^3.0.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-exceptions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
|
||||
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
|
||||
"dev": true
|
||||
},
|
||||
"spdx-expression-parse": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
|
||||
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"spdx-exceptions": "^2.1.0",
|
||||
"spdx-license-ids": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"spdx-license-ids": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
|
||||
"integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-utf8": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.7",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
|
||||
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
|
||||
"dev": true
|
||||
},
|
||||
"validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"spdx-correct": "^3.0.0",
|
||||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"which-module": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
|
||||
"integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
|
||||
"dev": true
|
||||
},
|
||||
"window-size": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz",
|
||||
"integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=",
|
||||
"dev": true
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^1.0.1",
|
||||
"strip-ansi": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz",
|
||||
"integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^3.2.0",
|
||||
"decamelize": "^1.1.1",
|
||||
"get-caller-file": "^1.0.1",
|
||||
"lodash.assign": "^4.0.3",
|
||||
"os-locale": "^1.4.0",
|
||||
"read-pkg-up": "^1.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^1.0.1",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^1.0.1",
|
||||
"which-module": "^1.0.0",
|
||||
"window-size": "^0.2.0",
|
||||
"y18n": "^3.2.1",
|
||||
"yargs-parser": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz",
|
||||
"integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^3.0.0",
|
||||
"lodash.assign": "^4.0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wailsapp/runtime",
|
||||
"version": "1.0.11",
|
||||
"version": "1.1.1",
|
||||
"description": "Wails Javascript runtime library",
|
||||
"main": "main.js",
|
||||
"types": "runtime.d.ts",
|
||||
@@ -25,4 +25,4 @@
|
||||
"devDependencies": {
|
||||
"dts-gen": "^0.5.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
runtime/js/runtime/runtime.d.ts
vendored
3
runtime/js/runtime/runtime.d.ts
vendored
@@ -21,6 +21,9 @@ declare const wailsapp__runtime: {
|
||||
Info(message: string): void;
|
||||
Warning(message: string): void;
|
||||
};
|
||||
Store: {
|
||||
New(name: string, optionalDefault?: any): any;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
29
runtime/runtime.go
Normal file
29
runtime/runtime.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package runtime
|
||||
|
||||
import "github.com/wailsapp/wails/lib/interfaces"
|
||||
|
||||
// Runtime is the Wails Runtime Interface, given to a user who has defined the WailsInit method
|
||||
type Runtime struct {
|
||||
Events *Events
|
||||
Log *Log
|
||||
Dialog *Dialog
|
||||
Window *Window
|
||||
Browser *Browser
|
||||
FileSystem *FileSystem
|
||||
Store *StoreProvider
|
||||
}
|
||||
|
||||
// NewRuntime creates a new Runtime struct
|
||||
func NewRuntime(eventManager interfaces.EventManager, renderer interfaces.Renderer) *Runtime {
|
||||
result := &Runtime{
|
||||
Events: NewEvents(eventManager),
|
||||
Log: NewLog(),
|
||||
Dialog: NewDialog(renderer),
|
||||
Window: NewWindow(renderer),
|
||||
Browser: NewBrowser(),
|
||||
FileSystem: NewFileSystem(),
|
||||
}
|
||||
// We need a reference to itself
|
||||
result.Store = NewStoreProvider(result)
|
||||
return result
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// package runtime contains all the methods and data structures related to the
|
||||
// Package runtime contains all the methods and data structures related to the
|
||||
// runtime library of Wails. This includes both Go and JS runtimes.
|
||||
package runtime
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"log"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
@@ -32,8 +32,8 @@ type StoreProvider struct {
|
||||
runtime *Runtime
|
||||
}
|
||||
|
||||
// newStore creates new stores using the provided Runtime reference.
|
||||
func newStore(runtime *Runtime) *StoreProvider {
|
||||
// NewStoreProvider creates new stores using the provided Runtime reference.
|
||||
func NewStoreProvider(runtime *Runtime) *StoreProvider {
|
||||
return &StoreProvider{
|
||||
runtime: runtime,
|
||||
}
|
||||
@@ -56,11 +56,6 @@ type Store struct {
|
||||
errorHandler func(error)
|
||||
}
|
||||
|
||||
func fatal(err error) {
|
||||
println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// New creates a new store
|
||||
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
|
||||
|
||||
@@ -230,7 +225,7 @@ func (s *Store) Subscribe(callback interface{}) {
|
||||
|
||||
err := s.callbackCheck(callback)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
callbackFunc := reflect.ValueOf(callback)
|
||||
@@ -281,7 +276,7 @@ func (s *Store) Update(updater interface{}) {
|
||||
|
||||
err := s.updaterCheck(updater)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Build args
|
||||
@@ -291,5 +286,13 @@ func (s *Store) Update(updater interface{}) {
|
||||
results := reflect.ValueOf(updater).Call(args)
|
||||
|
||||
// We will only have 1 result. Set the store to it
|
||||
s.Set(results[0].Interface())
|
||||
err = s.Set(results[0].Interface())
|
||||
if err != nil && s.errorHandler != nil {
|
||||
s.errorHandler(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of the data that's kept in the current state / Store
|
||||
func (s *Store) Get() interface{} {
|
||||
return s.data.Interface()
|
||||
}
|
||||
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,113 +0,0 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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 target type flag
|
||||
description := "Type of application to build. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
// Setup production flag
|
||||
production := false
|
||||
command.BoolFlag("production", "Build in production mode", &production)
|
||||
|
||||
// Setup pack flag
|
||||
pack := false
|
||||
command.BoolFlag("pack", "Create a platform specific package", &pack)
|
||||
|
||||
compilerCommand := "go"
|
||||
command.StringFlag("compiler", "Use a different go compiler to build, eg go1.15beta1", &compilerCommand)
|
||||
|
||||
// Setup Platform flag
|
||||
platform := runtime.GOOS
|
||||
command.StringFlag("platform", "Platform to target", &platform)
|
||||
|
||||
// Quiet Build
|
||||
quiet := false
|
||||
command.BoolFlag("q", "Supress 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
|
||||
if production {
|
||||
mode = build.Production
|
||||
}
|
||||
|
||||
// Create BuildOptions
|
||||
buildOptions := &build.Options{
|
||||
Logger: logger,
|
||||
OutputType: outputType,
|
||||
Mode: mode,
|
||||
Pack: pack,
|
||||
Platform: platform,
|
||||
LDFlags: ldflags,
|
||||
Compiler: compilerCommand,
|
||||
}
|
||||
|
||||
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,261 +0,0 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/leaanthony/slicer"
|
||||
"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"
|
||||
)
|
||||
|
||||
// AddSubcommand adds the `dev` command for the Wails application
|
||||
func AddSubcommand(app *clir.Cli, w io.Writer) error {
|
||||
|
||||
command := app.NewSubCommand("dev", "Development mode")
|
||||
|
||||
outputType := "desktop"
|
||||
|
||||
validTargetTypes := slicer.String([]string{"desktop", "hybrid", "server"})
|
||||
|
||||
// Setup target type flag
|
||||
description := "Type of application to develop. Valid types: " + validTargetTypes.Join(",")
|
||||
command.StringFlag("t", description, &outputType)
|
||||
|
||||
// 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("m", "Extensions to trigger rebuilds (comma separated) eg go,js,css,html", &extensions)
|
||||
|
||||
command.Action(func() error {
|
||||
|
||||
// Validate inputs
|
||||
if !validTargetTypes.Contains(outputType) {
|
||||
return fmt.Errorf("output type '%s' is not valid", outputType)
|
||||
}
|
||||
|
||||
// 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 buildFrontend bool = true
|
||||
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...")
|
||||
debugBinaryProcess = restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||
|
||||
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") {
|
||||
watcher.Add(event.Name)
|
||||
logger.Println("Watching directory: %s", event.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check for file writes
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
|
||||
// logger.Println("modified file: %s", event.Name)
|
||||
var rebuild bool = false
|
||||
|
||||
// Iterate all file patterns
|
||||
for _, pattern := range extensionsThatTriggerARebuild {
|
||||
rebuild = strings.HasSuffix(event.Name, pattern)
|
||||
if err != nil {
|
||||
logger.Fatal(err.Error())
|
||||
}
|
||||
if rebuild {
|
||||
// Only build frontend when the file isn't a Go file
|
||||
buildFrontend = !strings.HasSuffix(event.Name, "go")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !rebuild {
|
||||
logger.Println("Filename change: %s did not match extension list %s", event.Name, extensions)
|
||||
return
|
||||
}
|
||||
|
||||
if buildFrontend {
|
||||
logger.Println("Full rebuild triggered: %s updated", event.Name)
|
||||
} else {
|
||||
logger.Println("Partial build triggered: %s updated", event.Name)
|
||||
}
|
||||
|
||||
// Do a rebuild
|
||||
|
||||
// Try and build the app
|
||||
newBinaryProcess := restartApp(logger, outputType, ldflags, compilerCommand, buildFrontend, debugBinaryProcess)
|
||||
|
||||
// If we have a new process, save it
|
||||
if newBinaryProcess != nil {
|
||||
debugBinaryProcess = newBinaryProcess
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// Get project dir
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get all subdirectories
|
||||
dirs, err := fs.GetSubdirectories(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup a watcher for non-node_modules directories
|
||||
dirs.Each(func(dir string) {
|
||||
if strings.Contains(dir, "node_modules") {
|
||||
return
|
||||
}
|
||||
logger.Println("Watching directory: %s", dir)
|
||||
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:
|
||||
println()
|
||||
// Notify debouncer to quit
|
||||
debounceQuit <- true
|
||||
quit = true
|
||||
}
|
||||
}
|
||||
|
||||
// Kill the current program if running
|
||||
if debugBinaryProcess != nil {
|
||||
debugBinaryProcess.Kill()
|
||||
}
|
||||
|
||||
logger.Println("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, buildFrontend bool, debugBinaryProcess *process.Process) *process.Process {
|
||||
|
||||
appBinary, err := buildApp(logger, outputType, ldflags, compilerCommand, buildFrontend)
|
||||
println()
|
||||
if err != nil {
|
||||
logger.Println("[ERROR] Build Failed: %s", err.Error())
|
||||
return nil
|
||||
}
|
||||
logger.Println("Build new binary: %s", appBinary)
|
||||
|
||||
// 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)
|
||||
err = newProcess.Start()
|
||||
if err != nil {
|
||||
// Remove binary
|
||||
fs.DeleteFile(appBinary)
|
||||
logger.Fatal("Unable to start application: %s", err.Error())
|
||||
}
|
||||
|
||||
return newProcess
|
||||
}
|
||||
|
||||
func buildApp(logger *clilogger.CLILogger, outputType string, ldflags string, compilerCommand string, buildFrontend bool) (string, error) {
|
||||
|
||||
// Create random output file
|
||||
outputFile := fmt.Sprintf("debug-%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: !buildFrontend,
|
||||
}
|
||||
|
||||
return build.Build(buildOptions)
|
||||
|
||||
}
|
||||
@@ -1,154 +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 {
|
||||
fmt.Fprintf(w, "\n%s\t%s", "Package Manager:", "Not Found")
|
||||
w.Flush()
|
||||
println()
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(w, "%s\t%s\n", "Package Manager: ", info.PM.Name())
|
||||
|
||||
// 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,91 +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 `dev` 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", "Supress 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("")
|
||||
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
// logger.Println(" Original Go Package Path:", pkg.Gopackage.PkgPath)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package initialise
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/internal/templates"
|
||||
"github.com/wailsapp/wails/v2/pkg/clilogger"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
options.Logger.Println("")
|
||||
if options.GenerateVSCode {
|
||||
options.Logger.Println("VSCode config files generated.")
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/leaanthony/clir"
|
||||
"github.com/wailsapp/wails/v2/cmd/wails/internal/commands/build"
|
||||
"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 main() {
|
||||
|
||||
var err error
|
||||
version := "v2.0.0-alpha"
|
||||
|
||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
||||
|
||||
build.AddBuildSubcommand(app, os.Stdout)
|
||||
err = initialise.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 = app.Run()
|
||||
if err != nil {
|
||||
println("\n\nERROR: " + err.Error())
|
||||
}
|
||||
}
|
||||
26
v2/go.mod
26
v2/go.mod
@@ -1,26 +0,0 @@
|
||||
module github.com/wailsapp/wails/v2
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/fatih/structtag v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/leaanthony/clir v1.0.4
|
||||
github.com/leaanthony/gosod v0.0.4
|
||||
github.com/leaanthony/slicer v1.5.0
|
||||
github.com/matryer/is v1.4.0
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // 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/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
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
nhooyr.io/websocket v1.8.6
|
||||
)
|
||||
130
v2/go.sum
130
v2/go.sum
@@ -1,130 +0,0 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
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 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
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/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/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leaanthony/clir v1.0.4 h1:Dov2y9zWJmZr7CjaCe86lKa4b5CSxskGAt2yBkoDyiU=
|
||||
github.com/leaanthony/clir v1.0.4/go.mod h1:k/RBkdkFl18xkkACMCLt09bhiZnrGORoxmomeMvDpE0=
|
||||
github.com/leaanthony/gosod v0.0.4 h1:v4hepo4IyL8E8c9qzDsvYcA0KGh7Npf8As74K5ibQpI=
|
||||
github.com/leaanthony/gosod v0.0.4/go.mod h1:nGMCb1PJfXwBDbOAike78jEYlpqge+xUKFf0iBKjKxU=
|
||||
github.com/leaanthony/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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
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/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 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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 h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
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 h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
||||
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
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/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
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,9 +0,0 @@
|
||||
// +build debug
|
||||
|
||||
package app
|
||||
|
||||
// Init initialises the application for a debug environment
|
||||
func (a *App) Init() error {
|
||||
a.debug = true
|
||||
return nil
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// +build !desktop,!hybrid,!server
|
||||
|
||||
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/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
|
||||
}
|
||||
|
||||
// CreateApp returns a null application
|
||||
func CreateApp(options *options.App) *App {
|
||||
return &App{}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Bind the dummy interface
|
||||
func (a *App) Bind(dummy interface{}) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
// +build desktop,!server
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"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/signal"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
"github.com/wailsapp/wails/v2/internal/runtime"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
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
|
||||
dispatcher *messagedispatcher.Dispatcher
|
||||
|
||||
// Indicates if the app is in debug mode
|
||||
debug bool
|
||||
|
||||
// This is our binding DB
|
||||
bindings *binding.Bindings
|
||||
|
||||
// LogLevel Store
|
||||
loglevelStore *runtime.Store
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *options.App) *App {
|
||||
|
||||
// Merge default options
|
||||
options.MergeDefaults()
|
||||
|
||||
// Set up logger
|
||||
myLogger := logger.New(options.Logger)
|
||||
myLogger.SetLogLevel(options.LogLevel)
|
||||
|
||||
window := ffenestri.NewApplicationWithConfig(options, myLogger)
|
||||
|
||||
result := &App{
|
||||
window: window,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
logger: myLogger,
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
}
|
||||
|
||||
result.options = options
|
||||
|
||||
// Initialise the app
|
||||
result.Init()
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
// Run the application
|
||||
func (a *App) Run() error {
|
||||
|
||||
// Setup signal handler
|
||||
signal, err := signal.NewManager(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.signal = signal
|
||||
a.signal.Start()
|
||||
|
||||
// Start the service bus
|
||||
a.servicebus.Debug()
|
||||
a.servicebus.Start()
|
||||
|
||||
// Start the runtime
|
||||
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.runtime = runtime
|
||||
a.runtime.Start()
|
||||
|
||||
// Application Stores
|
||||
a.loglevelStore = a.runtime.GoRuntime().Store.New("loglevel", a.options.LogLevel)
|
||||
|
||||
// Start the binding subsystem
|
||||
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, a.runtime.GoRuntime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.binding = binding
|
||||
a.binding.Start()
|
||||
|
||||
// Start the logging subsystem
|
||||
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.log = log
|
||||
a.log.Start()
|
||||
|
||||
// create the dispatcher
|
||||
dispatcher, err := messagedispatcher.New(a.servicebus, a.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.dispatcher = dispatcher
|
||||
dispatcher.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()
|
||||
|
||||
// Dump bindings as a debug
|
||||
bindingDump, err := a.bindings.ToJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := a.window.Run(dispatcher, bindingDump)
|
||||
a.logger.Trace("Ffenestri.Run() exited")
|
||||
a.servicebus.Stop()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,207 +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,
|
||||
|
||||
// This should be controlled by the compile time flags...
|
||||
DevTools: true,
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
// +build !debug
|
||||
|
||||
package app
|
||||
|
||||
// Init initialises the application for a production environment
|
||||
func (a *App) Init() error {
|
||||
println("Processing production cli options")
|
||||
return nil
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
// +build server,!desktop
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"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/servicebus"
|
||||
"github.com/wailsapp/wails/v2/internal/subsystem"
|
||||
"github.com/wailsapp/wails/v2/internal/webserver"
|
||||
)
|
||||
|
||||
// App defines a Wails application structure
|
||||
type App struct {
|
||||
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
|
||||
webserver *webserver.WebServer
|
||||
|
||||
debug bool
|
||||
}
|
||||
|
||||
// Create App
|
||||
func CreateApp(options *Options) *App {
|
||||
options.mergeDefaults()
|
||||
// We ignore the inputs (for now)
|
||||
|
||||
// TODO: Allow logger output override on CLI
|
||||
myLogger := logger.New(os.Stdout)
|
||||
myLogger.SetLogLevel(logger.TRACE)
|
||||
|
||||
result := &App{
|
||||
bindings: binding.NewBindings(myLogger),
|
||||
logger: myLogger,
|
||||
servicebus: servicebus.New(myLogger),
|
||||
webserver: webserver.NewWebServer(myLogger),
|
||||
}
|
||||
|
||||
// Initialise app
|
||||
result.Init()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
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")
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// Bind a struct to the application by passing in
|
||||
// a pointer to it
|
||||
func (a *App) Bind(structPtr interface{}) {
|
||||
|
||||
// Add the struct to the bindings
|
||||
err := a.bindings.Add(structPtr)
|
||||
if err != nil {
|
||||
a.logger.Fatal("Error during binding: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -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,70 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
)
|
||||
|
||||
type Bindings struct {
|
||||
db *DB
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
// NewBindings returns a new Bindings object
|
||||
func NewBindings(logger *logger.Logger) *Bindings {
|
||||
return &Bindings{
|
||||
db: newDB(),
|
||||
logger: logger.CustomLogger("Bindings"),
|
||||
}
|
||||
}
|
||||
|
||||
// Add the given struct methods to the Bindings
|
||||
func (b *Bindings) Add(structPtr interface{}) error {
|
||||
|
||||
methods, err := getMethods(structPtr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannout 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]
|
||||
|
||||
// Is this WailsInit?
|
||||
if method.IsWailsInit() {
|
||||
err := b.db.AddWailsInit(method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.logger.Trace("Registered WailsInit method: %s", method.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// Is this WailsShutdown?
|
||||
if method.IsWailsShutdown() {
|
||||
err := b.db.AddWailsShutdown(method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.logger.Trace("Registered WailsShutdown method: %s", method.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
// 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,150 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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:"-"`
|
||||
}
|
||||
|
||||
// IsWailsInit returns true if the method name is "WailsInit"
|
||||
func (b *BoundMethod) IsWailsInit() bool {
|
||||
return strings.HasSuffix(b.Name, "WailsInit")
|
||||
}
|
||||
|
||||
// IsWailsShutdown returns true if the method name is "WailsShutdown"
|
||||
func (b *BoundMethod) IsWailsShutdown() bool {
|
||||
return strings.HasSuffix(b.Name, "WailsShutdown")
|
||||
}
|
||||
|
||||
// VerifyWailsInit checks if the WailsInit signature is correct
|
||||
func (b *BoundMethod) VerifyWailsInit() error {
|
||||
// Must only have 1 input
|
||||
if b.InputCount() != 1 {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Check input type
|
||||
if !b.Inputs[0].IsType("*runtime.Runtime") {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Must only have 1 output
|
||||
if b.OutputCount() != 1 {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Check output type
|
||||
if !b.Outputs[0].IsError() {
|
||||
return fmt.Errorf("invalid method signature for %s: expected `WailsInit(*wails.Runtime) error`", b.Name)
|
||||
}
|
||||
|
||||
// Input must be of type Runtime
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyWailsShutdown checks if the WailsShutdown signature is correct
|
||||
func (b *BoundMethod) VerifyWailsShutdown() error {
|
||||
// Must have no inputs
|
||||
if b.InputCount() != 0 {
|
||||
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||
}
|
||||
|
||||
// Must have no outputs
|
||||
if b.OutputCount() != 0 {
|
||||
return fmt.Errorf("invalid method signature for WailsShutdown: expected `WailsShutdown()`")
|
||||
}
|
||||
|
||||
// Input must be of type Runtime
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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())
|
||||
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,150 +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
|
||||
|
||||
// These are slices of methods registered using WailsInit and WailsShutdown
|
||||
wailsInitMethods []*BoundMethod
|
||||
wailsShutdownMethods []*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
|
||||
|
||||
}
|
||||
|
||||
// AddWailsInit checks the given method is a WailsInit method and if it
|
||||
// is, adds it to the list of WailsInit methods
|
||||
func (d *DB) AddWailsInit(method *BoundMethod) error {
|
||||
err := method.VerifyWailsInit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.wailsInitMethods = append(d.wailsInitMethods, method)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddWailsShutdown checks the given method is a WailsInit method and if it
|
||||
// is, adds it to the list of WailsShutdown methods
|
||||
func (d *DB) AddWailsShutdown(method *BoundMethod) error {
|
||||
err := method.VerifyWailsShutdown()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock the db whilst processing and unlock on return
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
d.wailsShutdownMethods = append(d.wailsShutdownMethods, method)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// WailsInitMethods returns the list of registered WailsInit methods
|
||||
func (d *DB) WailsInitMethods() []*BoundMethod {
|
||||
return d.wailsInitMethods
|
||||
}
|
||||
|
||||
// WailsShutdownMethods returns the list of registered WailsInit methods
|
||||
func (d *DB) WailsShutdownMethods() []*BoundMethod {
|
||||
return d.wailsShutdownMethods
|
||||
}
|
||||
@@ -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,123 +0,0 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// isStructPtr returns true if the value given is a struct
|
||||
func isStruct(value interface{}) bool {
|
||||
return reflect.ValueOf(value).Kind() == reflect.Struct
|
||||
}
|
||||
|
||||
func 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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// convertArgToValue
|
||||
func convertArgToValue(input json.RawMessage, target *Parameter) (result reflect.Value, err error) {
|
||||
|
||||
// Catch type conversion panics thrown by convert
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Modify error
|
||||
err = fmt.Errorf("%s", r.(string)[23:])
|
||||
}
|
||||
}()
|
||||
|
||||
// Do the conversion
|
||||
|
||||
// Handle nil values
|
||||
if input == nil {
|
||||
switch target.reflectType.Kind() {
|
||||
case reflect.Chan,
|
||||
reflect.Func,
|
||||
reflect.Interface,
|
||||
reflect.Map,
|
||||
reflect.Ptr,
|
||||
reflect.Slice:
|
||||
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||
default:
|
||||
return reflect.Zero(target.reflectType), fmt.Errorf("Unable to use null value")
|
||||
}
|
||||
} else {
|
||||
result = reflect.ValueOf(input).Convert(target.reflectType)
|
||||
}
|
||||
|
||||
// We don't like doing this but it's the only way to
|
||||
// handle recover() correctly
|
||||
return
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,181 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"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
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// DEBUG is the global Ffenestri debug flag.
|
||||
// TODO: move to compile time.
|
||||
var DEBUG bool = true
|
||||
|
||||
// Application is our main application object
|
||||
type Application struct {
|
||||
config *options.App
|
||||
memory []unsafe.Pointer
|
||||
|
||||
// This is the main app pointer
|
||||
app unsafe.Pointer
|
||||
|
||||
// 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) *Application {
|
||||
return &Application{
|
||||
config: config,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
|
||||
// NewApplication creates a new Application with the default config
|
||||
func NewApplication(logger *logger.Logger) *Application {
|
||||
return &Application{
|
||||
config: options.Default,
|
||||
logger: logger.CustomLogger("Ffenestri"),
|
||||
}
|
||||
}
|
||||
|
||||
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) 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)
|
||||
app := C.NewApplication(title, width, height, resizable, devtools, fullscreen, startHidden, logLevel)
|
||||
|
||||
// Save app reference
|
||||
a.app = unsafe.Pointer(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
|
||||
a.processPlatformSettings()
|
||||
|
||||
// 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.")
|
||||
}
|
||||
|
||||
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,35 +0,0 @@
|
||||
#ifndef __FFENESTRI_H__
|
||||
#define __FFENESTRI_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden, int logLevel);
|
||||
extern void SetMinWindowSize(void *app, int minWidth, int minHeight);
|
||||
extern void SetMaxWindowSize(void *app, int maxWidth, int maxHeight);
|
||||
extern void Run(void *app, int argc, char **argv);
|
||||
extern void DestroyApplication(void *app);
|
||||
extern void SetDebug(void *app, int flag);
|
||||
extern void SetBindings(void *app, const char *bindings);
|
||||
extern void ExecJS(void *app, const char *script);
|
||||
extern void Hide(void *app);
|
||||
extern void Show(void *app);
|
||||
extern void Center(void *app);
|
||||
extern void Maximise(void *app);
|
||||
extern void Unmaximise(void *app);
|
||||
extern void ToggleMaximise(void *app);
|
||||
extern void Minimise(void *app);
|
||||
extern void Unminimise(void *app);
|
||||
extern void ToggleMinimise(void *app);
|
||||
extern void SetColour(void *app, int red, int green, int blue, int alpha);
|
||||
extern void SetSize(void *app, int width, int height);
|
||||
extern void SetPosition(void *app, int x, int y);
|
||||
extern void Quit(void *app);
|
||||
extern void SetTitle(void *app, const char *title);
|
||||
extern void Fullscreen(void *app);
|
||||
extern void UnFullscreen(void *app);
|
||||
extern void ToggleFullscreen(void *app);
|
||||
extern void DisableFrame(void *app);
|
||||
extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolveAliases, int treatPackagesAsDirectories);
|
||||
extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
#endif
|
||||
@@ -1,156 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
|
||||
#cgo linux CFLAGS: -DFFENESTRI_LINUX=1
|
||||
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "ffenestri.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client is our implentation of messageDispatcher.Client
|
||||
type Client struct {
|
||||
app *Application
|
||||
logger logger.CustomLogger
|
||||
}
|
||||
|
||||
func newClient(app *Application) *Client {
|
||||
return &Client{
|
||||
app: app,
|
||||
logger: app.logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Quit the application
|
||||
func (c *Client) Quit() {
|
||||
c.app.logger.Trace("Got shutdown message")
|
||||
C.Quit(c.app.app)
|
||||
}
|
||||
|
||||
// NotifyEvent will pass on the event message to the frontend
|
||||
func (c *Client) NotifyEvent(message string) {
|
||||
eventMessage := `window.wails._.Notify(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("eventMessage = %+v", eventMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(eventMessage))
|
||||
}
|
||||
|
||||
// CallResult contains the result of the call from JS
|
||||
func (c *Client) CallResult(message string) {
|
||||
callbackMessage := `window.wails._.Callback(` + strconv.Quote(message) + `);`
|
||||
c.app.logger.Trace("callbackMessage = %+v", callbackMessage)
|
||||
C.ExecJS(c.app.app, c.app.string2CString(callbackMessage))
|
||||
}
|
||||
|
||||
// WindowSetTitle sets the window title to the given string
|
||||
func (c *Client) WindowSetTitle(title string) {
|
||||
C.SetTitle(c.app.app, c.app.string2CString(title))
|
||||
}
|
||||
|
||||
// WindowFullscreen will set the window to be fullscreen
|
||||
func (c *Client) WindowFullscreen() {
|
||||
C.Fullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnFullscreen will unfullscreen the window
|
||||
func (c *Client) WindowUnFullscreen() {
|
||||
C.UnFullscreen(c.app.app)
|
||||
}
|
||||
|
||||
// WindowShow will show the window
|
||||
func (c *Client) WindowShow() {
|
||||
C.Show(c.app.app)
|
||||
}
|
||||
|
||||
// WindowHide will hide the window
|
||||
func (c *Client) WindowHide() {
|
||||
C.Hide(c.app.app)
|
||||
}
|
||||
|
||||
// WindowCenter will hide the window
|
||||
func (c *Client) WindowCenter() {
|
||||
C.Center(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMaximise will maximise the window
|
||||
func (c *Client) WindowMaximise() {
|
||||
C.Maximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowMinimise will minimise the window
|
||||
func (c *Client) WindowMinimise() {
|
||||
C.Minimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnmaximise will unmaximise the window
|
||||
func (c *Client) WindowUnmaximise() {
|
||||
C.Unmaximise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowUnminimise will unminimise the window
|
||||
func (c *Client) WindowUnminimise() {
|
||||
C.Unminimise(c.app.app)
|
||||
}
|
||||
|
||||
// WindowPosition will position the window to x,y on the
|
||||
// monitor that the window is mostly on
|
||||
func (c *Client) WindowPosition(x int, y int) {
|
||||
C.SetPosition(c.app.app, C.int(x), C.int(y))
|
||||
}
|
||||
|
||||
// WindowSize will resize the window to the given
|
||||
// width and height
|
||||
func (c *Client) WindowSize(width int, height int) {
|
||||
C.SetSize(c.app.app, C.int(width), C.int(height))
|
||||
}
|
||||
|
||||
// WindowSetColour sets the window colour
|
||||
func (c *Client) WindowSetColour(colour int) {
|
||||
r, g, b, a := intToColour(colour)
|
||||
C.SetColour(c.app.app, r, g, b, a)
|
||||
}
|
||||
|
||||
// OpenDialog will open a dialog with the given title and filter
|
||||
func (c *Client) OpenDialog(dialogOptions *options.OpenDialog, callbackID string) {
|
||||
C.OpenDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.AllowFiles),
|
||||
c.app.bool2Cint(dialogOptions.AllowDirectories),
|
||||
c.app.bool2Cint(dialogOptions.AllowMultiple),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.ResolveAliases),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
// SaveDialog will open a dialog with the given title and filter
|
||||
func (c *Client) SaveDialog(dialogOptions *options.SaveDialog, callbackID string) {
|
||||
C.SaveDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Filters),
|
||||
c.app.string2CString(dialogOptions.DefaultFilename),
|
||||
c.app.string2CString(dialogOptions.DefaultDirectory),
|
||||
c.app.bool2Cint(dialogOptions.ShowHiddenFiles),
|
||||
c.app.bool2Cint(dialogOptions.CanCreateDirectories),
|
||||
c.app.bool2Cint(dialogOptions.TreatPackagesAsDirectories),
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
package ffenestri
|
||||
|
||||
/*
|
||||
#cgo darwin CFLAGS: -DFFENESTRI_DARWIN=1
|
||||
#cgo darwin LDFLAGS: -framework WebKit -lobjc
|
||||
|
||||
extern void TitlebarAppearsTransparent(void *);
|
||||
extern void HideTitle(void *);
|
||||
extern void HideTitleBar(void *);
|
||||
extern void FullSizeContent(void *);
|
||||
extern void UseToolbar(void *);
|
||||
extern void HideToolbarSeparator(void *);
|
||||
extern void DisableFrame(void *);
|
||||
extern void SetAppearance(void *, const char *);
|
||||
extern void WebviewIsTransparent(void *);
|
||||
extern void SetWindowBackgroundIsTranslucent(void *);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
func (a *Application) processPlatformSettings() {
|
||||
|
||||
mac := a.config.Mac
|
||||
titlebar := mac.TitleBar
|
||||
|
||||
// HideTitle
|
||||
if titlebar.HideTitle {
|
||||
C.HideTitle(a.app)
|
||||
}
|
||||
|
||||
// HideTitleBar
|
||||
if titlebar.HideTitleBar {
|
||||
C.HideTitleBar(a.app)
|
||||
}
|
||||
|
||||
// Full Size Content
|
||||
if titlebar.FullSizeContent {
|
||||
C.FullSizeContent(a.app)
|
||||
}
|
||||
|
||||
// Toolbar
|
||||
if titlebar.UseToolbar {
|
||||
C.UseToolbar(a.app)
|
||||
}
|
||||
|
||||
if titlebar.HideToolbarSeparator {
|
||||
C.HideToolbarSeparator(a.app)
|
||||
}
|
||||
|
||||
if titlebar.TitlebarAppearsTransparent {
|
||||
C.TitlebarAppearsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Process window Appearance
|
||||
if mac.Appearance != "" {
|
||||
C.SetAppearance(a.app, a.string2CString(string(mac.Appearance)))
|
||||
}
|
||||
|
||||
// Check if the webview should be transparent
|
||||
if mac.WebviewIsTransparent {
|
||||
C.WebviewIsTransparent(a.app)
|
||||
}
|
||||
|
||||
// Check if window should be translucent
|
||||
if mac.WindowBackgroundIsTranslucent {
|
||||
C.SetWindowBackgroundIsTranslucent(a.app)
|
||||
}
|
||||
}
|
||||
@@ -1,984 +0,0 @@
|
||||
|
||||
#ifndef __FFENESTRI_LINUX_H__
|
||||
#define __FFENESTRI_LINUX_H__
|
||||
|
||||
#include "gtk/gtk.h"
|
||||
#include "webkit2/webkit2.h"
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// References to assets
|
||||
extern const unsigned char *assets[];
|
||||
extern const unsigned char runtime;
|
||||
extern const char *icon[];
|
||||
|
||||
// Constants
|
||||
#define PRIMARY_MOUSE_BUTTON 1
|
||||
#define MIDDLE_MOUSE_BUTTON 2
|
||||
#define SECONDARY_MOUSE_BUTTON 3
|
||||
|
||||
// MAIN DEBUG FLAG
|
||||
int debug;
|
||||
|
||||
// Credit: https://stackoverflow.com/a/8465083
|
||||
char *concat(const char *s1, const char *s2)
|
||||
{
|
||||
const size_t len1 = strlen(s1);
|
||||
const size_t len2 = strlen(s2);
|
||||
char *result = malloc(len1 + len2 + 1);
|
||||
memcpy(result, s1, len1);
|
||||
memcpy(result + len1, s2, len2 + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Debug works like sprintf but mutes if the global debug flag is true
|
||||
// Credit: https://stackoverflow.com/a/20639708
|
||||
void Debug(char *message, ...)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
char *temp = concat("TRACE | Ffenestri (C) | ", message);
|
||||
message = concat(temp, "\n");
|
||||
free(temp);
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
free(message);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
extern void messageFromWindowCallback(const char *);
|
||||
typedef void (*ffenestriCallback)(const char *);
|
||||
|
||||
struct Application
|
||||
{
|
||||
|
||||
// Gtk Data
|
||||
GtkApplication *application;
|
||||
GtkWindow *mainWindow;
|
||||
GtkWidget *webView;
|
||||
int signalInvoke;
|
||||
int signalWindowDrag;
|
||||
int signalButtonPressed;
|
||||
int signalButtonReleased;
|
||||
int signalLoadChanged;
|
||||
|
||||
// Saves the events for the drag mouse button
|
||||
GdkEventButton *dragButtonEvent;
|
||||
|
||||
// The number of the default drag button
|
||||
int dragButton;
|
||||
|
||||
// Window Data
|
||||
const char *title;
|
||||
char *id;
|
||||
int width;
|
||||
int height;
|
||||
int resizable;
|
||||
int devtools;
|
||||
int startHidden;
|
||||
int fullscreen;
|
||||
int minWidth;
|
||||
int minHeight;
|
||||
int maxWidth;
|
||||
int maxHeight;
|
||||
int frame;
|
||||
|
||||
// User Data
|
||||
char *HTML;
|
||||
|
||||
// Callback
|
||||
ffenestriCallback sendMessageToBackend;
|
||||
|
||||
// Bindings
|
||||
const char *bindings;
|
||||
|
||||
// Lock - used for sync operations (Should we be using g_mutex?)
|
||||
int lock;
|
||||
};
|
||||
|
||||
void *NewApplication(const char *title, int width, int height, int resizable, int devtools, int fullscreen, int startHidden)
|
||||
{
|
||||
// Setup main application struct
|
||||
struct Application *result = malloc(sizeof(struct Application));
|
||||
result->title = title;
|
||||
result->width = width;
|
||||
result->height = height;
|
||||
result->resizable = resizable;
|
||||
result->devtools = devtools;
|
||||
result->fullscreen = fullscreen;
|
||||
result->minWidth = 0;
|
||||
result->minHeight = 0;
|
||||
result->maxWidth = 0;
|
||||
result->maxHeight = 0;
|
||||
result->frame = 1;
|
||||
result->startHidden = startHidden;
|
||||
|
||||
// Default drag button is PRIMARY
|
||||
result->dragButton = PRIMARY_MOUSE_BUTTON;
|
||||
|
||||
result->sendMessageToBackend = (ffenestriCallback)messageFromWindowCallback;
|
||||
|
||||
// Create a unique ID based on the current unix timestamp
|
||||
char temp[11];
|
||||
sprintf(&temp[0], "%d", (int)time(NULL));
|
||||
result->id = concat("wails.app", &temp[0]);
|
||||
|
||||
// Create the main GTK application
|
||||
GApplicationFlags flags = G_APPLICATION_FLAGS_NONE;
|
||||
result->application = gtk_application_new(result->id, flags);
|
||||
|
||||
// Return the application struct
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
void DestroyApplication(struct Application *app)
|
||||
{
|
||||
Debug("Destroying Application");
|
||||
|
||||
g_application_quit(G_APPLICATION(app->application));
|
||||
|
||||
// Release the GTK ID string
|
||||
if (app->id != NULL)
|
||||
{
|
||||
free(app->id);
|
||||
app->id = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->id");
|
||||
}
|
||||
|
||||
// Free the bindings
|
||||
if (app->bindings != NULL)
|
||||
{
|
||||
free((void *)app->bindings);
|
||||
app->bindings = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->bindings");
|
||||
}
|
||||
|
||||
// Disconnect signal handlers
|
||||
WebKitUserContentManager *manager = webkit_web_view_get_user_content_manager((WebKitWebView *)app->webView);
|
||||
g_signal_handler_disconnect(manager, app->signalInvoke);
|
||||
if( app->frame == 0) {
|
||||
g_signal_handler_disconnect(manager, app->signalWindowDrag);
|
||||
g_signal_handler_disconnect(app->webView, app->signalButtonPressed);
|
||||
g_signal_handler_disconnect(app->webView, app->signalButtonReleased);
|
||||
}
|
||||
g_signal_handler_disconnect(app->webView, app->signalLoadChanged);
|
||||
|
||||
// Release the main GTK Application
|
||||
if (app->application != NULL)
|
||||
{
|
||||
g_object_unref(app->application);
|
||||
app->application = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug("Almost a double free for app->application");
|
||||
}
|
||||
Debug("Finished Destroying Application");
|
||||
}
|
||||
|
||||
// Quit will stop the gtk application and free up all the memory
|
||||
// used by the application
|
||||
void Quit(struct Application *app)
|
||||
{
|
||||
Debug("Quit Called");
|
||||
gtk_window_close((GtkWindow *)app->mainWindow);
|
||||
g_application_quit((GApplication *)app->application);
|
||||
DestroyApplication(app);
|
||||
}
|
||||
|
||||
// SetTitle sets the main window title to the given string
|
||||
void SetTitle(struct Application *app, const char *title)
|
||||
{
|
||||
gtk_window_set_title(app->mainWindow, title);
|
||||
}
|
||||
|
||||
// Fullscreen sets the main window to be fullscreen
|
||||
void Fullscreen(struct Application *app)
|
||||
{
|
||||
gtk_window_fullscreen(app->mainWindow);
|
||||
}
|
||||
|
||||
// UnFullscreen resets the main window after a fullscreen
|
||||
void UnFullscreen(struct Application *app)
|
||||
{
|
||||
gtk_window_unfullscreen(app->mainWindow);
|
||||
}
|
||||
|
||||
void setMinMaxSize(struct Application *app)
|
||||
{
|
||||
GdkGeometry size;
|
||||
size.min_width = size.min_height = size.max_width = size.max_height = 0;
|
||||
int flags = 0;
|
||||
if (app->maxHeight > 0 && app->maxWidth > 0)
|
||||
{
|
||||
size.max_height = app->maxHeight;
|
||||
size.max_width = app->maxWidth;
|
||||
flags |= GDK_HINT_MAX_SIZE;
|
||||
}
|
||||
if (app->minHeight > 0 && app->minWidth > 0)
|
||||
{
|
||||
size.min_height = app->minHeight;
|
||||
size.min_width = app->minWidth;
|
||||
flags |= GDK_HINT_MIN_SIZE;
|
||||
}
|
||||
gtk_window_set_geometry_hints(app->mainWindow, NULL, &size, flags);
|
||||
}
|
||||
|
||||
char *fileDialogInternal(struct Application *app, GtkFileChooserAction chooserAction, char **args) {
|
||||
GtkFileChooserNative *native;
|
||||
GtkFileChooserAction action = chooserAction;
|
||||
gint res;
|
||||
char *filename;
|
||||
|
||||
char *title = args[0];
|
||||
char *filter = args[1];
|
||||
|
||||
native = gtk_file_chooser_native_new(title,
|
||||
app->mainWindow,
|
||||
action,
|
||||
"_Open",
|
||||
"_Cancel");
|
||||
|
||||
GtkFileChooser *chooser = GTK_FILE_CHOOSER(native);
|
||||
|
||||
// If we have filters, process them
|
||||
if (filter[0] != '\0') {
|
||||
GtkFileFilter *file_filter = gtk_file_filter_new();
|
||||
gchar **filters = g_strsplit(filter, ",", -1);
|
||||
gint i;
|
||||
for(i = 0; filters && filters[i]; i++) {
|
||||
gtk_file_filter_add_pattern(file_filter, filters[i]);
|
||||
// Debug("Adding filter pattern: %s\n", filters[i]);
|
||||
}
|
||||
gtk_file_filter_set_name(file_filter, filter);
|
||||
gtk_file_chooser_add_filter(chooser, file_filter);
|
||||
g_strfreev(filters);
|
||||
}
|
||||
|
||||
res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native));
|
||||
if (res == GTK_RESPONSE_ACCEPT)
|
||||
{
|
||||
filename = gtk_file_chooser_get_filename(chooser);
|
||||
}
|
||||
|
||||
g_object_unref(native);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
// openFileDialogInternal opens a dialog to select a file
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *openFileDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_OPEN, args);
|
||||
}
|
||||
|
||||
// saveFileDialogInternal opens a dialog to select a file
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *saveFileDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SAVE, args);
|
||||
}
|
||||
|
||||
|
||||
// openDirectoryDialogInternal opens a dialog to select a directory
|
||||
// NOTE: The result is a string that will need to be freed!
|
||||
char *openDirectoryDialogInternal(struct Application *app, char **args)
|
||||
{
|
||||
return fileDialogInternal(app, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, args);
|
||||
}
|
||||
|
||||
void SetMinWindowSize(struct Application *app, int minWidth, int minHeight)
|
||||
{
|
||||
app->minWidth = minWidth;
|
||||
app->minHeight = minHeight;
|
||||
}
|
||||
|
||||
void SetMaxWindowSize(struct Application *app, int maxWidth, int maxHeight)
|
||||
{
|
||||
app->maxWidth = maxWidth;
|
||||
app->maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
// SetColour sets the colour of the webview to the given colour string
|
||||
int SetColour(struct Application *app, const char *colourString)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
gboolean result = gdk_rgba_parse(&rgba, colourString);
|
||||
if (result == FALSE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// Debug("Setting webview colour to: %s", colourString);
|
||||
webkit_web_view_get_background_color((WebKitWebView *)(app->webView), &rgba);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DisableFrame disables the window frame
|
||||
void DisableFrame(struct Application *app)
|
||||
{
|
||||
app->frame = 0;
|
||||
}
|
||||
|
||||
void syncCallback(GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
void *data)
|
||||
{
|
||||
struct Application *app = (struct Application *)data;
|
||||
app->lock = 0;
|
||||
}
|
||||
|
||||
void syncEval(struct Application *app, const gchar *script)
|
||||
{
|
||||
|
||||
WebKitWebView *webView = (WebKitWebView *)(app->webView);
|
||||
|
||||
// wait for lock to free
|
||||
while (app->lock == 1)
|
||||
{
|
||||
g_main_context_iteration(0, true);
|
||||
}
|
||||
// Set lock
|
||||
app->lock = 1;
|
||||
|
||||
webkit_web_view_run_javascript(
|
||||
webView,
|
||||
script,
|
||||
NULL, syncCallback, (void*)app);
|
||||
|
||||
while (app->lock == 1)
|
||||
{
|
||||
g_main_context_iteration(0, true);
|
||||
}
|
||||
}
|
||||
|
||||
void asyncEval(WebKitWebView *webView, const gchar *script)
|
||||
{
|
||||
webkit_web_view_run_javascript(
|
||||
webView,
|
||||
script,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
typedef void (*dispatchMethod)(struct Application *app, void *);
|
||||
|
||||
struct dispatchData
|
||||
{
|
||||
struct Application *app;
|
||||
dispatchMethod method;
|
||||
void *args;
|
||||
};
|
||||
|
||||
gboolean executeMethod(gpointer data)
|
||||
{
|
||||
struct dispatchData *d = (struct dispatchData *)data;
|
||||
(d->method)(d->app, d->args);
|
||||
g_free(d);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ExecJS(struct Application *app, char *js)
|
||||
{
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)syncEval;
|
||||
data->args = js;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
typedef char *(*dialogMethod)(struct Application *app, void *);
|
||||
|
||||
struct dialogCall
|
||||
{
|
||||
struct Application *app;
|
||||
dialogMethod method;
|
||||
void *args;
|
||||
void *filter;
|
||||
char *result;
|
||||
int done;
|
||||
};
|
||||
|
||||
gboolean executeMethodWithReturn(gpointer data)
|
||||
{
|
||||
struct dialogCall *d = (struct dialogCall *)data;
|
||||
|
||||
d->result = (d->method)(d->app, d->args);
|
||||
d->done = 1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char *OpenFileDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)openFileDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
g_free(data);
|
||||
return data->result;
|
||||
}
|
||||
|
||||
char *SaveFileDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)saveFileDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
Debug("Dialog done");
|
||||
Debug("Result = %s\n", data->result);
|
||||
|
||||
g_free(data);
|
||||
// Fingers crossed this wasn't freed by g_free above
|
||||
return data->result;
|
||||
}
|
||||
|
||||
char *OpenDirectoryDialog(struct Application *app, char *title, char *filter)
|
||||
{
|
||||
struct dialogCall *data = (struct dialogCall *)g_new(struct dialogCall, 1);
|
||||
data->result = NULL;
|
||||
data->done = 0;
|
||||
data->method = (dialogMethod)openDirectoryDialogInternal;
|
||||
const char* dialogArgs[]={ title, filter };
|
||||
data->args = dialogArgs;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethodWithReturn, data);
|
||||
|
||||
while (data->done == 0)
|
||||
{
|
||||
usleep(100000);
|
||||
}
|
||||
Debug("Directory Dialog done");
|
||||
Debug("Result = %s\n", data->result);
|
||||
g_free(data);
|
||||
// Fingers crossed this wasn't freed by g_free above
|
||||
return data->result;
|
||||
}
|
||||
|
||||
// Sets the icon to the XPM stored in icon
|
||||
void setIcon(struct Application *app)
|
||||
{
|
||||
GdkPixbuf *appIcon = gdk_pixbuf_new_from_xpm_data((const char **)icon);
|
||||
gtk_window_set_icon(app->mainWindow, appIcon);
|
||||
}
|
||||
|
||||
static void load_finished_cb(WebKitWebView *webView,
|
||||
WebKitLoadEvent load_event,
|
||||
struct Application *app)
|
||||
{
|
||||
switch (load_event)
|
||||
{
|
||||
// case WEBKIT_LOAD_STARTED:
|
||||
// /* New load, we have now a provisional URI */
|
||||
// // printf("Start downloading %s\n", webkit_web_view_get_uri(web_view));
|
||||
// /* Here we could start a spinner or update the
|
||||
// * location bar with the provisional URI */
|
||||
// break;
|
||||
// case WEBKIT_LOAD_REDIRECTED:
|
||||
// // printf("Redirected to: %s\n", webkit_web_view_get_uri(web_view));
|
||||
// break;
|
||||
// case WEBKIT_LOAD_COMMITTED:
|
||||
// /* The load is being performed. Current URI is
|
||||
// * the final one and it won't change unless a new
|
||||
// * load is requested or a navigation within the
|
||||
// * same page is performed */
|
||||
// // printf("Loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||
// break;
|
||||
case WEBKIT_LOAD_FINISHED:
|
||||
/* Load finished, we can now stop the spinner */
|
||||
// printf("Finished loading: %s\n", webkit_web_view_get_uri(web_view));
|
||||
|
||||
// Bindings
|
||||
Debug("Binding Methods");
|
||||
syncEval(app, app->bindings);
|
||||
|
||||
// Runtime
|
||||
Debug("Setting up Wails runtime");
|
||||
syncEval(app, &runtime);
|
||||
|
||||
// Loop over assets
|
||||
int index = 1;
|
||||
while (1)
|
||||
{
|
||||
// Get next asset pointer
|
||||
const char *asset = assets[index];
|
||||
|
||||
// If we have no more assets, break
|
||||
if (asset == 0x00)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// sync eval the asset
|
||||
syncEval(app, asset);
|
||||
index++;
|
||||
};
|
||||
|
||||
// Set the icon
|
||||
setIcon(app);
|
||||
|
||||
// Setup fullscreen
|
||||
if (app->fullscreen)
|
||||
{
|
||||
Debug("Going fullscreen");
|
||||
Fullscreen(app);
|
||||
}
|
||||
|
||||
// Setup resize
|
||||
gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
|
||||
if (app->resizable)
|
||||
{
|
||||
gtk_window_set_default_size(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_set_size_request(GTK_WIDGET(app->mainWindow), app->width, app->height);
|
||||
gtk_window_resize(GTK_WINDOW(app->mainWindow), app->width, app->height);
|
||||
// Fix the min/max to the window size for good measure
|
||||
app->minHeight = app->maxHeight = app->height;
|
||||
app->minWidth = app->maxWidth = app->width;
|
||||
}
|
||||
gtk_window_set_resizable(GTK_WINDOW(app->mainWindow), app->resizable ? TRUE : FALSE);
|
||||
setMinMaxSize(app);
|
||||
|
||||
// Centre by default
|
||||
gtk_window_set_position(app->mainWindow, GTK_WIN_POS_CENTER);
|
||||
|
||||
// Show window and focus
|
||||
if( app->startHidden == 0) {
|
||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||
gtk_widget_grab_focus(app->webView);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean disable_context_menu_cb(
|
||||
WebKitWebView *web_view,
|
||||
WebKitContextMenu *context_menu,
|
||||
GdkEvent *event,
|
||||
WebKitHitTestResult *hit_test_result,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void printEvent(const char *message, GdkEventButton *event)
|
||||
{
|
||||
Debug("%s: [button:%d] [x:%f] [y:%f] [time:%d]",
|
||||
message,
|
||||
event->button,
|
||||
event->x_root,
|
||||
event->y_root,
|
||||
event->time);
|
||||
}
|
||||
|
||||
|
||||
static void dragWindow(WebKitUserContentManager *contentManager,
|
||||
WebKitJavascriptResult *result,
|
||||
struct Application *app)
|
||||
{
|
||||
// If we get this message erroneously, ignore
|
||||
if (app->dragButtonEvent == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore non-toplevel widgets
|
||||
GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(app->webView));
|
||||
if (!GTK_IS_WINDOW(window))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Initiate the drag
|
||||
printEvent("Starting drag with event", app->dragButtonEvent);
|
||||
|
||||
gtk_window_begin_move_drag(app->mainWindow,
|
||||
app->dragButton,
|
||||
app->dragButtonEvent->x_root,
|
||||
app->dragButtonEvent->y_root,
|
||||
app->dragButtonEvent->time);
|
||||
// Clear the event
|
||||
app->dragButtonEvent = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
gboolean buttonPress(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
||||
{
|
||||
if (event->type == GDK_BUTTON_PRESS && event->button == app->dragButton)
|
||||
{
|
||||
app->dragButtonEvent = event;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean buttonRelease(GtkWidget *widget, GdkEventButton *event, struct Application *app)
|
||||
{
|
||||
if (event->type == GDK_BUTTON_RELEASE && event->button == app->dragButton)
|
||||
{
|
||||
app->dragButtonEvent = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void sendMessageToBackend(WebKitUserContentManager *contentManager,
|
||||
WebKitJavascriptResult *result,
|
||||
struct Application *app)
|
||||
{
|
||||
#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
|
||||
JSCValue *value = webkit_javascript_result_get_js_value(result);
|
||||
char *message = jsc_value_to_string(value);
|
||||
#else
|
||||
JSGlobalContextRef context = webkit_javascript_result_get_global_context(result);
|
||||
JSValueRef value = webkit_javascript_result_get_value(result);
|
||||
JSStringRef js = JSValueToStringCopy(context, value, NULL);
|
||||
size_t messageSize = JSStringGetMaximumUTF8CStringSize(js);
|
||||
char *message = g_new(char, messageSize);
|
||||
JSStringGetUTF8CString(js, message, messageSize);
|
||||
JSStringRelease(js);
|
||||
#endif
|
||||
app->sendMessageToBackend(message);
|
||||
g_free(message);
|
||||
}
|
||||
|
||||
void SetDebug(struct Application *app, int flag)
|
||||
{
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
// getCurrentMonitorGeometry gets the geometry of the monitor
|
||||
// that the window is mostly on.
|
||||
GdkRectangle getCurrentMonitorGeometry(GtkWindow *window) {
|
||||
// Get the monitor that the window is currently on
|
||||
GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(window));
|
||||
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||
GdkMonitor *monitor = gdk_display_get_monitor_at_window (display, gdk_window);
|
||||
|
||||
// Get the geometry of the monitor
|
||||
GdkRectangle result;
|
||||
gdk_monitor_get_geometry (monitor,&result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*******************
|
||||
* Window Position *
|
||||
*******************/
|
||||
|
||||
// Position holds an x/y corrdinate
|
||||
struct Position {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// Internal call for setting the position of the window.
|
||||
void setPositionInternal(struct Application *app, struct Position *pos) {
|
||||
|
||||
// Get the monitor geometry
|
||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
||||
|
||||
// Move the window relative to the monitor
|
||||
gtk_window_move(app->mainWindow, m.x + pos->x, m.y + pos->y);
|
||||
|
||||
// Free memory
|
||||
free(pos);
|
||||
}
|
||||
|
||||
// SetPosition sets the position of the window to the given x/y
|
||||
// coordinates. The x/y values are relative to the monitor
|
||||
// the window is mostly on.
|
||||
void SetPosition(struct Application *app, int x, int y) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)setPositionInternal;
|
||||
struct Position *pos = malloc(sizeof(struct Position));
|
||||
pos->x = x;
|
||||
pos->y = y;
|
||||
data->args = pos;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
/***************
|
||||
* Window Size *
|
||||
***************/
|
||||
|
||||
// Size holds a width/height
|
||||
struct Size {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
// Internal call for setting the size of the window.
|
||||
void setSizeInternal(struct Application *app, struct Size *size) {
|
||||
gtk_window_resize(app->mainWindow, size->width, size->height);
|
||||
free(size);
|
||||
}
|
||||
|
||||
// SetSize sets the size of the window to the given width/height
|
||||
void SetSize(struct Application *app, int width, int height) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)setSizeInternal;
|
||||
struct Size *size = malloc(sizeof(struct Size));
|
||||
size->width = width;
|
||||
size->height = height;
|
||||
data->args = size;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// centerInternal will center the main window on the monitor it is mostly in
|
||||
void centerInternal(struct Application *app)
|
||||
{
|
||||
// Get the geometry of the monitor
|
||||
GdkRectangle m = getCurrentMonitorGeometry(app->mainWindow);
|
||||
|
||||
// Get the window width/height
|
||||
int windowWidth, windowHeight;
|
||||
gtk_window_get_size(app->mainWindow, &windowWidth, &windowHeight);
|
||||
|
||||
// Place the window at the center of the monitor
|
||||
gtk_window_move(app->mainWindow, ((m.width - windowWidth) / 2) + m.x, ((m.height - windowHeight) / 2) + m.y);
|
||||
}
|
||||
|
||||
// Center the window
|
||||
void Center(struct Application *app) {
|
||||
|
||||
// Setup a call to centerInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)centerInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// hideInternal hides the main window
|
||||
void hideInternal(struct Application *app) {
|
||||
gtk_widget_hide (GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Hide places the hideInternal method onto the main thread for execution
|
||||
void Hide(struct Application *app) {
|
||||
|
||||
// Setup a call to hideInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)hideInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// showInternal shows the main window
|
||||
void showInternal(struct Application *app) {
|
||||
gtk_widget_show_all(GTK_WIDGET(app->mainWindow));
|
||||
gtk_widget_grab_focus(app->webView);
|
||||
}
|
||||
|
||||
// Show places the showInternal method onto the main thread for execution
|
||||
void Show(struct Application *app) {
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)showInternal;
|
||||
data->app = app;
|
||||
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// maximiseInternal maximises the main window
|
||||
void maximiseInternal(struct Application *app) {
|
||||
gtk_window_maximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Maximise places the maximiseInternal method onto the main thread for execution
|
||||
void Maximise(struct Application *app) {
|
||||
|
||||
// Setup a call to maximiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)maximiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// unmaximiseInternal unmaximises the main window
|
||||
void unmaximiseInternal(struct Application *app) {
|
||||
gtk_window_unmaximize(GTK_WIDGET(app->mainWindow));
|
||||
}
|
||||
|
||||
// Unmaximise places the unmaximiseInternal method onto the main thread for execution
|
||||
void Unmaximise(struct Application *app) {
|
||||
|
||||
// Setup a call to unmaximiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)unmaximiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
// minimiseInternal minimises the main window
|
||||
void minimiseInternal(struct Application *app) {
|
||||
gtk_window_iconify(app->mainWindow);
|
||||
}
|
||||
|
||||
// Minimise places the minimiseInternal method onto the main thread for execution
|
||||
void Minimise(struct Application *app) {
|
||||
|
||||
// Setup a call to minimiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)minimiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
// unminimiseInternal unminimises the main window
|
||||
void unminimiseInternal(struct Application *app) {
|
||||
gtk_window_present(app->mainWindow);
|
||||
}
|
||||
|
||||
// Unminimise places the unminimiseInternal method onto the main thread for execution
|
||||
void Unminimise(struct Application *app) {
|
||||
|
||||
// Setup a call to unminimiseInternal on the main thread
|
||||
struct dispatchData *data = (struct dispatchData *)g_new(struct dispatchData, 1);
|
||||
data->method = (dispatchMethod)unminimiseInternal;
|
||||
data->app = app;
|
||||
|
||||
// Add call to main thread
|
||||
gdk_threads_add_idle(executeMethod, data);
|
||||
}
|
||||
|
||||
|
||||
void SetBindings(struct Application *app, const char *bindings)
|
||||
{
|
||||
const char *temp = concat("window.wailsbindings = \"", bindings);
|
||||
const char *jscall = concat(temp, "\";");
|
||||
free((void *)temp);
|
||||
app->bindings = jscall;
|
||||
}
|
||||
|
||||
// This is called when the close button on the window is pressed
|
||||
gboolean close_button_pressed(GtkWidget *widget,
|
||||
GdkEvent *event,
|
||||
struct Application *app)
|
||||
{
|
||||
app->sendMessageToBackend("WC"); // Window Close
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void setupWindow(struct Application *app)
|
||||
{
|
||||
|
||||
// Create the window
|
||||
GtkWidget *mainWindow = gtk_application_window_new(app->application);
|
||||
// Save reference
|
||||
app->mainWindow = GTK_WINDOW(mainWindow);
|
||||
|
||||
// Setup frame
|
||||
gtk_window_set_decorated((GtkWindow *)mainWindow, app->frame);
|
||||
|
||||
// Setup title
|
||||
gtk_window_set_title(GTK_WINDOW(mainWindow), app->title);
|
||||
|
||||
// Setup script handler
|
||||
WebKitUserContentManager *contentManager = webkit_user_content_manager_new();
|
||||
|
||||
// Setup the invoke handler
|
||||
webkit_user_content_manager_register_script_message_handler(contentManager, "external");
|
||||
app->signalInvoke = g_signal_connect(contentManager, "script-message-received::external", G_CALLBACK(sendMessageToBackend), app);
|
||||
|
||||
// Setup the window drag handler if this is a frameless app
|
||||
if ( app->frame == 0 ) {
|
||||
webkit_user_content_manager_register_script_message_handler(contentManager, "windowDrag");
|
||||
app->signalWindowDrag = g_signal_connect(contentManager, "script-message-received::windowDrag", G_CALLBACK(dragWindow), app);
|
||||
// Setup the mouse handlers
|
||||
app->signalButtonPressed = g_signal_connect(app->webView, "button-press-event", G_CALLBACK(buttonPress), app);
|
||||
app->signalButtonReleased = g_signal_connect(app->webView, "button-release-event", G_CALLBACK(buttonRelease), app);
|
||||
}
|
||||
GtkWidget *webView = webkit_web_view_new_with_user_content_manager(contentManager);
|
||||
|
||||
// Save reference
|
||||
app->webView = webView;
|
||||
|
||||
// Add the webview to the window
|
||||
gtk_container_add(GTK_CONTAINER(mainWindow), webView);
|
||||
|
||||
|
||||
// Load default HTML
|
||||
app->signalLoadChanged = g_signal_connect(G_OBJECT(webView), "load-changed", G_CALLBACK(load_finished_cb), app);
|
||||
|
||||
// Load the user's HTML
|
||||
// assets[0] is the HTML because the asset array is bundled like that by convention
|
||||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webView), assets[0]);
|
||||
|
||||
// Check if we want to enable the dev tools
|
||||
if (app->devtools)
|
||||
{
|
||||
WebKitSettings *settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webView));
|
||||
// webkit_settings_set_enable_write_console_messages_to_stdout(settings, true);
|
||||
webkit_settings_set_enable_developer_extras(settings, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_signal_connect(G_OBJECT(webView), "context-menu", G_CALLBACK(disable_context_menu_cb), app);
|
||||
}
|
||||
|
||||
// Listen for close button signal
|
||||
g_signal_connect(GTK_WIDGET(mainWindow), "delete-event", G_CALLBACK(close_button_pressed), app);
|
||||
}
|
||||
|
||||
static void activate(GtkApplication* _, struct Application *app)
|
||||
{
|
||||
setupWindow(app);
|
||||
}
|
||||
|
||||
void Run(struct Application *app, int argc, char **argv)
|
||||
{
|
||||
g_signal_connect(app->application, "activate", G_CALLBACK(activate), app);
|
||||
g_application_run(G_APPLICATION(app->application), argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 Joseph A. Adams (joeyadams3.14159@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
Source: http://git.ozlabs.org/?p=ccan;a=tree;f=ccan/json;hb=HEAD
|
||||
*/
|
||||
|
||||
#ifndef CCAN_JSON_H
|
||||
#define CCAN_JSON_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum {
|
||||
JSON_NULL,
|
||||
JSON_BOOL,
|
||||
JSON_STRING,
|
||||
JSON_NUMBER,
|
||||
JSON_ARRAY,
|
||||
JSON_OBJECT,
|
||||
} JsonTag;
|
||||
|
||||
typedef struct JsonNode JsonNode;
|
||||
|
||||
struct JsonNode
|
||||
{
|
||||
/* only if parent is an object or array (NULL otherwise) */
|
||||
JsonNode *parent;
|
||||
JsonNode *prev, *next;
|
||||
|
||||
/* only if parent is an object (NULL otherwise) */
|
||||
char *key; /* Must be valid UTF-8. */
|
||||
|
||||
JsonTag tag;
|
||||
union {
|
||||
/* JSON_BOOL */
|
||||
bool bool_;
|
||||
|
||||
/* JSON_STRING */
|
||||
char *string_; /* Must be valid UTF-8. */
|
||||
|
||||
/* JSON_NUMBER */
|
||||
double number_;
|
||||
|
||||
/* JSON_ARRAY */
|
||||
/* JSON_OBJECT */
|
||||
struct {
|
||||
JsonNode *head, *tail;
|
||||
} children;
|
||||
};
|
||||
};
|
||||
|
||||
/*** Encoding, decoding, and validation ***/
|
||||
|
||||
JsonNode *json_decode (const char *json);
|
||||
char *json_encode (const JsonNode *node);
|
||||
char *json_encode_string (const char *str);
|
||||
char *json_stringify (const JsonNode *node, const char *space);
|
||||
void json_delete (JsonNode *node);
|
||||
|
||||
bool json_validate (const char *json);
|
||||
|
||||
/*** Lookup and traversal ***/
|
||||
|
||||
JsonNode *json_find_element (JsonNode *array, int index);
|
||||
JsonNode *json_find_member (JsonNode *object, const char *key);
|
||||
|
||||
JsonNode *json_first_child (const JsonNode *node);
|
||||
|
||||
#define json_foreach(i, object_or_array) \
|
||||
for ((i) = json_first_child(object_or_array); \
|
||||
(i) != NULL; \
|
||||
(i) = (i)->next)
|
||||
|
||||
/*** Construction and manipulation ***/
|
||||
|
||||
JsonNode *json_mknull(void);
|
||||
JsonNode *json_mkbool(bool b);
|
||||
JsonNode *json_mkstring(const char *s);
|
||||
JsonNode *json_mknumber(double n);
|
||||
JsonNode *json_mkarray(void);
|
||||
JsonNode *json_mkobject(void);
|
||||
|
||||
void json_append_element(JsonNode *array, JsonNode *element);
|
||||
void json_prepend_element(JsonNode *array, JsonNode *element);
|
||||
void json_append_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
void json_prepend_member(JsonNode *object, const char *key, JsonNode *value);
|
||||
|
||||
void json_remove_from_parent(JsonNode *node);
|
||||
|
||||
/*** Debugging ***/
|
||||
|
||||
/*
|
||||
* Look for structure and encoding problems in a JsonNode or its descendents.
|
||||
*
|
||||
* If a problem is detected, return false, writing a description of the problem
|
||||
* to errmsg (unless errmsg is NULL).
|
||||
*/
|
||||
bool json_check(const JsonNode *node, char errmsg[256]);
|
||||
|
||||
#endif
|
||||
@@ -1,202 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
)
|
||||
|
||||
// LocalDirectory gets the caller's file directory
|
||||
// Equivalent to node's __DIRNAME
|
||||
func LocalDirectory() string {
|
||||
_, thisFile, _, _ := runtime.Caller(1)
|
||||
return filepath.Dir(thisFile)
|
||||
}
|
||||
|
||||
// RelativeToCwd returns an absolute path based on the cwd
|
||||
// and the given relative path
|
||||
func RelativeToCwd(relativePath string) (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Join(cwd, relativePath), nil
|
||||
}
|
||||
|
||||
// Mkdir will create the given directory
|
||||
func Mkdir(dirname string) error {
|
||||
return os.Mkdir(dirname, 0755)
|
||||
}
|
||||
|
||||
// DeleteFile will delete the given file
|
||||
func DeleteFile(filename string) error {
|
||||
return os.Remove(filename)
|
||||
}
|
||||
|
||||
// CopyFile from source to target
|
||||
func CopyFile(source string, target string) error {
|
||||
s, err := os.Open(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
d, err := os.Create(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(d, s); err != nil {
|
||||
d.Close()
|
||||
return err
|
||||
}
|
||||
return d.Close()
|
||||
}
|
||||
|
||||
// DirExists - Returns true if the given path resolves to a directory on the filesystem
|
||||
func DirExists(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsDir()
|
||||
}
|
||||
|
||||
// FileExists returns a boolean value indicating whether
|
||||
// the given file exists
|
||||
func FileExists(path string) bool {
|
||||
fi, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// RelativePath returns a qualified path created by joining the
|
||||
// directory of the calling file and the given relative path.
|
||||
//
|
||||
// Example: RelativePath("..") in *this* file would give you '/path/to/wails2/v2/internal`
|
||||
func RelativePath(relativepath string, optionalpaths ...string) string {
|
||||
_, thisFile, _, _ := runtime.Caller(1)
|
||||
localDir := filepath.Dir(thisFile)
|
||||
|
||||
// If we have optional paths, join them to the relativepath
|
||||
if len(optionalpaths) > 0 {
|
||||
paths := []string{relativepath}
|
||||
paths = append(paths, optionalpaths...)
|
||||
relativepath = filepath.Join(paths...)
|
||||
}
|
||||
result, err := filepath.Abs(filepath.Join(localDir, relativepath))
|
||||
if err != nil {
|
||||
// I'm allowing this for 1 reason only: It's fatal if the path
|
||||
// supplied is wrong as it's only used internally in Wails. If we get
|
||||
// that path wrong, we should know about it immediately. The other reason is
|
||||
// that it cuts down a ton of unnecassary error handling.
|
||||
panic(err)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustLoadString attempts to load a string and will abort with a fatal message if
|
||||
// something goes wrong
|
||||
func MustLoadString(filename string) string {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Unable to load file '%s': %s\n", filename, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return *(*string)(unsafe.Pointer(&data))
|
||||
}
|
||||
|
||||
// MD5File returns the md5sum of the given file
|
||||
func MD5File(filename string) (string, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// MustMD5File will call MD5File and abort the program on error
|
||||
func MustMD5File(filename string) string {
|
||||
result, err := MD5File(filename)
|
||||
if err != nil {
|
||||
println("FATAL: Unable to MD5Sum file:", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// MustWriteString will attempt to write the given data to the given filename
|
||||
// It will abort the program in the event of a failure
|
||||
func MustWriteString(filename string, data string) {
|
||||
err := ioutil.WriteFile(filename, []byte(data), 0755)
|
||||
if err != nil {
|
||||
fatal("Unable to write file", filename, ":", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// fatal will print the optional messages and die
|
||||
func fatal(message ...string) {
|
||||
if len(message) > 0 {
|
||||
print("FATAL:")
|
||||
for text := range message {
|
||||
print(text)
|
||||
}
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// GetSubdirectories returns a list of subdirectories for the given root directory
|
||||
func GetSubdirectories(rootDir string) (*slicer.StringSlicer, error) {
|
||||
var result slicer.StringSlicer
|
||||
|
||||
// Iterate root dir
|
||||
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If we have a directory, save it
|
||||
if info.IsDir() {
|
||||
result.Add(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return &result, err
|
||||
}
|
||||
|
||||
func DirIsEmpty(dir string) (bool, error) {
|
||||
|
||||
if !DirExists(dir) {
|
||||
return false, fmt.Errorf("DirIsEmpty called with a non-existant directory: %s", dir)
|
||||
}
|
||||
|
||||
// CREDIT: https://stackoverflow.com/a/30708914/8325411
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Readdirnames(1) // Or f.Readdir(1)
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
return false, err // Either not empty or error, suits both cases
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestRelativePath(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
is.Equal(err, nil)
|
||||
|
||||
// Check current directory
|
||||
actual := RelativePath(".")
|
||||
is.Equal(actual, cwd)
|
||||
|
||||
// Check 2 parameters
|
||||
actual = RelativePath("..", "fs")
|
||||
is.Equal(actual, cwd)
|
||||
|
||||
// Check 3 parameters including filename
|
||||
actual = RelativePath("..", "fs", "fs.go")
|
||||
expected := filepath.Join(cwd, "fs.go")
|
||||
is.Equal(actual, expected)
|
||||
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tdewolff/minify"
|
||||
"github.com/tdewolff/minify/js"
|
||||
)
|
||||
|
||||
type assetTypes struct {
|
||||
JS string
|
||||
CSS string
|
||||
FAVICON string
|
||||
HTML string
|
||||
}
|
||||
|
||||
// AssetTypes is an enum for the asset type keys
|
||||
var AssetTypes *assetTypes = &assetTypes{
|
||||
JS: "javascript",
|
||||
CSS: "css",
|
||||
FAVICON: "favicon",
|
||||
HTML: "html",
|
||||
}
|
||||
|
||||
// Asset describes an asset type and its path
|
||||
type Asset struct {
|
||||
Type string
|
||||
Path string
|
||||
Data string
|
||||
}
|
||||
|
||||
// Load the asset from disk
|
||||
func (a *Asset) Load(basedirectory string) error {
|
||||
assetpath := filepath.Join(basedirectory, a.Path)
|
||||
data, err := ioutil.ReadFile(assetpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Data = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsString returns the data as a READ ONLY string
|
||||
func (a *Asset) AsString() string {
|
||||
return a.Data
|
||||
}
|
||||
|
||||
// AsCHexData processes the asset data so it may be used by C
|
||||
func (a *Asset) AsCHexData() string {
|
||||
|
||||
// This will be our final string to hexify
|
||||
dataString := a.Data
|
||||
|
||||
switch a.Type {
|
||||
case AssetTypes.HTML:
|
||||
|
||||
// Escape HTML
|
||||
var re = regexp.MustCompile(`\s{2,}`)
|
||||
result := re.ReplaceAllString(a.Data, ``)
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\r\n", "")
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
|
||||
// Inject wailsloader code
|
||||
result = strings.Replace(result, `</body>`, `<script id='wailsloader'>window.wailsloader = { html: true, runtime: false, userjs: false, usercss: false };var self=document.querySelector('#wailsloader');self.parentNode.removeChild(self);</script></body>`, 1)
|
||||
|
||||
url := url.URL{Path: result}
|
||||
urlString := strings.ReplaceAll(url.String(), "/", "%2f")
|
||||
|
||||
// Save Data uRI string
|
||||
dataString = "data:text/html;charset=utf-8," + urlString
|
||||
|
||||
case AssetTypes.CSS:
|
||||
|
||||
// Escape CSS data
|
||||
var re = regexp.MustCompile(`\s{2,}`)
|
||||
result := re.ReplaceAllString(a.Data, ``)
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\r\n", "")
|
||||
result = strings.ReplaceAll(result, "\n", "")
|
||||
result = strings.ReplaceAll(result, "\t", "")
|
||||
result = strings.ReplaceAll(result, `\`, `\\`)
|
||||
result = strings.ReplaceAll(result, `"`, `\"`)
|
||||
result = strings.ReplaceAll(result, `'`, `\'`)
|
||||
result = strings.ReplaceAll(result, ` {`, `{`)
|
||||
result = strings.ReplaceAll(result, `: `, `:`)
|
||||
dataString = fmt.Sprintf("window.wails._.InjectCSS(\"%s\");", result)
|
||||
|
||||
case AssetTypes.JS:
|
||||
m := minify.New()
|
||||
m.AddFunc("application/javascript", js.Minify)
|
||||
var err error
|
||||
dataString, err = m.String("application/javascript", a.Data+";")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
a.Data = dataString
|
||||
}
|
||||
|
||||
// Get byte data of the string
|
||||
bytes := *(*[]byte)(unsafe.Pointer(&dataString))
|
||||
|
||||
// Create a strings builder
|
||||
var cdata strings.Builder
|
||||
|
||||
// Set buffer size to 4k
|
||||
cdata.Grow(4096)
|
||||
|
||||
// Convert each byte to hex
|
||||
for _, b := range bytes {
|
||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||
}
|
||||
|
||||
return cdata.String()
|
||||
}
|
||||
|
||||
// Dump will output the asset to the terminal
|
||||
func (a *Asset) Dump() {
|
||||
fmt.Printf("{ Type: %s, Path: %s, Data: %+v }\n", a.Type, a.Path, a.Data[:10])
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/leaanthony/slicer"
|
||||
"github.com/wailsapp/wails/v2/internal/assetdb"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// AssetBundle is a collection of Assets
|
||||
type AssetBundle struct {
|
||||
assets []*Asset
|
||||
basedirectory string
|
||||
}
|
||||
|
||||
// NewAssetBundle creates a new AssetBundle struct containing
|
||||
// the given html and all the assets referenced by it
|
||||
func NewAssetBundle(pathToHTML string) (*AssetBundle, error) {
|
||||
|
||||
// Create result
|
||||
result := &AssetBundle{
|
||||
basedirectory: filepath.Dir(pathToHTML),
|
||||
}
|
||||
|
||||
err := result.loadAssets(pathToHTML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// loadAssets processes the given html file and loads in
|
||||
// all referenced assets
|
||||
func (a *AssetBundle) loadAssets(pathToHTML string) error {
|
||||
|
||||
// Save HTML
|
||||
htmlAsset := &Asset{
|
||||
Type: AssetTypes.HTML,
|
||||
Path: filepath.Base(pathToHTML),
|
||||
}
|
||||
err := htmlAsset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, htmlAsset)
|
||||
|
||||
return a.processHTML(htmlAsset.AsString())
|
||||
}
|
||||
|
||||
// Credit to: https://drstearns.github.io/tutorials/tokenizing/
|
||||
func (a *AssetBundle) processHTML(htmldata string) error {
|
||||
|
||||
// Tokenize the html
|
||||
buf := bytes.NewBufferString(htmldata)
|
||||
tokenizer := html.NewTokenizer(buf)
|
||||
|
||||
for {
|
||||
//get the next token type
|
||||
tokenType := tokenizer.Next()
|
||||
|
||||
//if it's an error token, we either reached
|
||||
//the end of the file, or the HTML was malformed
|
||||
if tokenType == html.ErrorToken {
|
||||
err := tokenizer.Err()
|
||||
if err == io.EOF {
|
||||
//end of the file, break out of the loop
|
||||
break
|
||||
}
|
||||
//otherwise, there was an error tokenizing,
|
||||
//which likely means the HTML was malformed.
|
||||
//since this is a simple command-line utility,
|
||||
//we can just use log.Fatalf() to report the error
|
||||
//and exit the process with a non-zero status code
|
||||
return tokenizer.Err()
|
||||
}
|
||||
|
||||
//process the token according to the token type...
|
||||
if tokenType == html.StartTagToken {
|
||||
//get the token
|
||||
token := tokenizer.Token()
|
||||
|
||||
//if the name of the element is "title"
|
||||
if "link" == token.Data {
|
||||
//the next token should be the page title
|
||||
tokenType = tokenizer.Next()
|
||||
//just make sure it's actually a text token
|
||||
asset := &Asset{}
|
||||
for _, attr := range token.Attr {
|
||||
// Favicon
|
||||
if attr.Key == "rel" && attr.Val == "icon" {
|
||||
asset.Type = AssetTypes.FAVICON
|
||||
}
|
||||
if attr.Key == "href" {
|
||||
asset.Path = attr.Val
|
||||
}
|
||||
// stylesheet
|
||||
if attr.Key == "rel" && attr.Val == "stylesheet" {
|
||||
asset.Type = AssetTypes.CSS
|
||||
}
|
||||
}
|
||||
err := asset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, asset)
|
||||
}
|
||||
if "script" == token.Data {
|
||||
|
||||
tokenType = tokenizer.Next()
|
||||
//just make sure it's actually a text token
|
||||
asset := &Asset{Type: AssetTypes.JS}
|
||||
for _, attr := range token.Attr {
|
||||
if attr.Key == "src" {
|
||||
asset.Path = attr.Val
|
||||
break
|
||||
}
|
||||
}
|
||||
err := asset.Load(a.basedirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.assets = append(a.assets, asset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteToCFile dumps all the assets to C files in the given directory
|
||||
func (a *AssetBundle) WriteToCFile(targetDir string) (string, error) {
|
||||
|
||||
// Write out the assets.c file
|
||||
var cdata strings.Builder
|
||||
|
||||
// Write header
|
||||
header := `// assets.c
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Ă‚ MODIWL.
|
||||
// This file was auto-generated. DO NOT MODIFY.
|
||||
|
||||
`
|
||||
cdata.WriteString(header)
|
||||
|
||||
// Loop over the Assets
|
||||
var err error
|
||||
assetVariables := slicer.String()
|
||||
var variableName string
|
||||
for index, asset := range a.assets {
|
||||
// For desktop we ignore the favicon
|
||||
if asset.Type == AssetTypes.FAVICON {
|
||||
continue
|
||||
}
|
||||
variableName = fmt.Sprintf("%s%d", asset.Type, index)
|
||||
assetCdata := fmt.Sprintf("const unsigned char %s[]={ %s0x00 };\n", variableName, asset.AsCHexData())
|
||||
cdata.WriteString(assetCdata)
|
||||
assetVariables.Add(variableName)
|
||||
}
|
||||
|
||||
if assetVariables.Length() > 0 {
|
||||
cdata.WriteString(fmt.Sprintf("\nconst unsigned char *assets[] = { %s, 0x00 };", assetVariables.Join(", ")))
|
||||
} else {
|
||||
cdata.WriteString("\nconst unsigned char *assets[] = { 0x00 };")
|
||||
}
|
||||
|
||||
// Save file
|
||||
assetsFile := filepath.Join(targetDir, "assets.c")
|
||||
err = ioutil.WriteFile(assetsFile, []byte(cdata.String()), 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return assetsFile, nil
|
||||
}
|
||||
|
||||
// ConvertToAssetDB returns an assetdb.AssetDB initialized with
|
||||
// the items in the AssetBundle
|
||||
func (a *AssetBundle) ConvertToAssetDB() (*assetdb.AssetDB, error) {
|
||||
assetdb := assetdb.NewAssetDB()
|
||||
|
||||
// Loop over the Assets
|
||||
for _, asset := range a.assets {
|
||||
assetdb.AddAsset(asset.Path, []byte(asset.Data))
|
||||
}
|
||||
|
||||
return assetdb, nil
|
||||
}
|
||||
|
||||
// Dump will output the assets to the terminal
|
||||
func (a *AssetBundle) Dump() {
|
||||
println("Assets:")
|
||||
for _, asset := range a.assets {
|
||||
asset.Dump()
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CustomLogger defines what a user can do with a logger
|
||||
type CustomLogger interface {
|
||||
// Writeln writes directly to the output with no log level plus line ending
|
||||
Writeln(message string) error
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
Write(message string) error
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
Trace(format string, args ...interface{}) error
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
Debug(format string, args ...interface{}) error
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
Info(format string, args ...interface{}) error
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
Warning(format string, args ...interface{}) error
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
Error(format string, args ...interface{}) error
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
Fatal(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// customLogger is a utlility to log messages to a number of destinations
|
||||
type customLogger struct {
|
||||
logger *Logger
|
||||
name string
|
||||
}
|
||||
|
||||
// New creates a new customLogger. You may pass in a number of `io.Writer`s that
|
||||
// are the targets for the logs
|
||||
func newcustomLogger(logger *Logger, name string) *customLogger {
|
||||
result := &customLogger{
|
||||
name: name,
|
||||
logger: logger,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *customLogger) Writeln(message string) error {
|
||||
return l.logger.Writeln(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *customLogger) Write(message string) error {
|
||||
return l.logger.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *customLogger) Trace(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Trace(format, args...)
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *customLogger) Debug(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Debug(format, args...)
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *customLogger) Info(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Info(format, args...)
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *customLogger) Warning(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Warning(format, args...)
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *customLogger) Error(format string, args ...interface{}) error {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
return l.logger.Error(format, args...)
|
||||
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *customLogger) Fatal(format string, args ...interface{}) {
|
||||
format = fmt.Sprintf("%s | %s", l.name, format)
|
||||
l.logger.Fatal(format, args...)
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||
)
|
||||
|
||||
// LogLevel is an alias for the public LogLevel
|
||||
type LogLevel = logger.LogLevel
|
||||
|
||||
// Logger is a utlility to log messages to a number of destinations
|
||||
type Logger struct {
|
||||
output logger.Logger
|
||||
logLevel LogLevel
|
||||
showLevelInLog bool
|
||||
}
|
||||
|
||||
// New creates a new Logger. You may pass in a number of `io.Writer`s that
|
||||
// are the targets for the logs
|
||||
func New(output logger.Logger) *Logger {
|
||||
result := &Logger{
|
||||
logLevel: logger.INFO,
|
||||
showLevelInLog: true,
|
||||
output: output,
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CustomLogger creates a new custom logger that prints out a name/id
|
||||
// before the messages
|
||||
func (l *Logger) CustomLogger(name string) CustomLogger {
|
||||
return newcustomLogger(l, name)
|
||||
}
|
||||
|
||||
// HideLogLevel removes the loglevel text from the start of each logged line
|
||||
func (l *Logger) HideLogLevel() {
|
||||
l.showLevelInLog = true
|
||||
}
|
||||
|
||||
// SetLogLevel sets the minimum level of logs that will be output
|
||||
func (l *Logger) SetLogLevel(level LogLevel) {
|
||||
l.logLevel = level
|
||||
}
|
||||
|
||||
// Writeln writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Writeln(message string) error {
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// Write writes directly to the output with no log level
|
||||
func (l *Logger) Write(message string) error {
|
||||
return l.output.Print(message)
|
||||
}
|
||||
|
||||
// Print writes directly to the output with no log level
|
||||
// Appends a carriage return to the message
|
||||
func (l *Logger) Print(message string) error {
|
||||
return l.Write(message)
|
||||
}
|
||||
|
||||
// Trace level logging. Works like Sprintf.
|
||||
func (l *Logger) Trace(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.TRACE {
|
||||
return l.output.Trace(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug level logging. Works like Sprintf.
|
||||
func (l *Logger) Debug(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.DEBUG {
|
||||
return l.output.Debug(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Info level logging. Works like Sprintf.
|
||||
func (l *Logger) Info(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.INFO {
|
||||
return l.output.Info(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Warning level logging. Works like Sprintf.
|
||||
func (l *Logger) Warning(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.WARNING {
|
||||
return l.output.Warning(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Error level logging. Works like Sprintf.
|
||||
func (l *Logger) Error(format string, args ...interface{}) error {
|
||||
if l.logLevel <= logger.ERROR {
|
||||
return l.output.Error(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fatal level logging. Works like Sprintf.
|
||||
func (l *Logger) Fatal(format string, args ...interface{}) {
|
||||
err := l.output.Fatal(fmt.Sprintf(format, args...))
|
||||
// Not much we can do but print it out before exiting
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package messagedispatcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/wailsapp/wails/v2/internal/logger"
|
||||
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
|
||||
"github.com/wailsapp/wails/v2/internal/servicebus"
|
||||
"github.com/wailsapp/wails/v2/pkg/options"
|
||||
)
|
||||
|
||||
// Client defines what a frontend client can do
|
||||
type Client interface {
|
||||
Quit()
|
||||
NotifyEvent(message string)
|
||||
CallResult(message string)
|
||||
OpenDialog(dialogOptions *options.OpenDialog, callbackID string)
|
||||
SaveDialog(dialogOptions *options.SaveDialog, callbackID string)
|
||||
WindowSetTitle(title string)
|
||||
WindowShow()
|
||||
WindowHide()
|
||||
WindowCenter()
|
||||
WindowMaximise()
|
||||
WindowUnmaximise()
|
||||
WindowMinimise()
|
||||
WindowUnminimise()
|
||||
WindowPosition(x int, y int)
|
||||
WindowSize(width int, height int)
|
||||
WindowFullscreen()
|
||||
WindowUnFullscreen()
|
||||
WindowSetColour(colour int)
|
||||
DarkModeEnabled(callbackID string)
|
||||
}
|
||||
|
||||
// DispatchClient is what the frontends use to interface with the
|
||||
// dispatcher
|
||||
type DispatchClient struct {
|
||||
id string
|
||||
logger logger.CustomLogger
|
||||
|
||||
bus *servicebus.ServiceBus
|
||||
|
||||
// Client
|
||||
frontend Client
|
||||
}
|
||||
|
||||
func newDispatchClient(id string, frontend Client, logger logger.CustomLogger, bus *servicebus.ServiceBus) *DispatchClient {
|
||||
|
||||
return &DispatchClient{
|
||||
id: id,
|
||||
frontend: frontend,
|
||||
logger: logger,
|
||||
bus: bus,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DispatchMessage is called by the front ends. It is passed
|
||||
// an IPC message, translates it to a more concrete message
|
||||
// type then publishes it on the service bus.
|
||||
func (d *DispatchClient) DispatchMessage(incomingMessage string) {
|
||||
|
||||
// Parse the message
|
||||
d.logger.Trace(fmt.Sprintf("Received message: %+v", incomingMessage))
|
||||
parsedMessage, err := message.Parse(incomingMessage)
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Save this client id
|
||||
parsedMessage.ClientID = d.id
|
||||
|
||||
d.logger.Trace("I got a parsedMessage: %+v", parsedMessage)
|
||||
|
||||
// Check error
|
||||
if err != nil {
|
||||
d.logger.Error(err.Error())
|
||||
// Hrm... what do we do with this?
|
||||
d.bus.PublishForTarget("generic:message", incomingMessage, d.id)
|
||||
return
|
||||
}
|
||||
|
||||
// Publish the parsed message
|
||||
d.bus.PublishForTarget(parsedMessage.Topic, parsedMessage.Data, d.id)
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CallMessage struct {
|
||||
Name string `json:"name"`
|
||||
Args []json.RawMessage `json:"args"`
|
||||
CallbackID string `json:"callbackID,omitempty"`
|
||||
}
|
||||
|
||||
// callMessageParser does what it says on the tin!
|
||||
func callMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Call messages must be at least 3 bytes `C{}``
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("call message was an invalid length")
|
||||
}
|
||||
|
||||
callMessage := new(CallMessage)
|
||||
|
||||
m := message[1:]
|
||||
|
||||
err := json.Unmarshal([]byte(m), callMessage)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
topic := "call:invoke"
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: topic, Data: callMessage}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// dialogMessageParser does what it says on the tin!
|
||||
func dialogMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Dialog messages must be at least 4 bytes
|
||||
if len(message) < 4 {
|
||||
return nil, fmt.Errorf("dialog message was an invalid length")
|
||||
}
|
||||
|
||||
var topic = "bad topic from dialogMessageParser"
|
||||
var responseMessage *parsedMessage
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
// Format of Dialog response messages: D<dialog type><callbackID>|<[]string as json encoded string>
|
||||
case 'D':
|
||||
dialogType := message[1]
|
||||
message = message[2:]
|
||||
idx := strings.IndexByte(message, '|')
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid dialog response message format")
|
||||
}
|
||||
callbackID := message[:idx+1]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
switch dialogType {
|
||||
case 'O':
|
||||
var data []string
|
||||
topic = "dialog:openselected:" + callbackID
|
||||
err := json.Unmarshal([]byte(payloadData), &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: data}
|
||||
case 'S':
|
||||
topic = "dialog:saveselected:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData}
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to dialogMessageParser()")
|
||||
}
|
||||
|
||||
return responseMessage, nil
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EventMessage struct {
|
||||
Name string `json:"name"`
|
||||
Data []interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type OnEventMessage struct {
|
||||
Name string
|
||||
Callback func(optionalData ...interface{})
|
||||
Counter int
|
||||
}
|
||||
|
||||
// eventMessageParser does what it says on the tin!
|
||||
func eventMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Event messages must be at least 2 bytes
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("event message was an invalid length")
|
||||
}
|
||||
|
||||
eventMessage := new(EventMessage)
|
||||
direction := message[1]
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
case 'E':
|
||||
m := message[2:]
|
||||
err := json.Unmarshal([]byte(m), eventMessage)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
topic := "event:emit:from:" + string(direction)
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: topic, Data: eventMessage}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
var logMessageMap = map[byte]string{
|
||||
'P': "log:print",
|
||||
'T': "log:trace",
|
||||
'D': "log:debug",
|
||||
'I': "log:info",
|
||||
'W': "log:warning",
|
||||
'E': "log:error",
|
||||
'F': "log:fatal",
|
||||
'S': "log:setlevel",
|
||||
}
|
||||
|
||||
// logMessageParser does what it says on the tin!
|
||||
func logMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Log messages must be at least 2 bytes
|
||||
if len(message) < 2 {
|
||||
return nil, fmt.Errorf("log message was an invalid length")
|
||||
}
|
||||
|
||||
// Switch on the log type
|
||||
messageTopic := logMessageMap[message[1]]
|
||||
|
||||
// If the type is invalid, raise error
|
||||
if messageTopic == "" {
|
||||
return nil, fmt.Errorf("log message type '%c' invalid", message[1])
|
||||
}
|
||||
|
||||
// Create a new parsed message struct
|
||||
parsedMessage := &parsedMessage{Topic: messageTopic, Data: message[2:]}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Parse
|
||||
type parsedMessage struct {
|
||||
Topic string
|
||||
ClientID string
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// Map of different message parsers based on the header byte of the message
|
||||
var messageParsers = map[byte]func(string) (*parsedMessage, error){
|
||||
'L': logMessageParser,
|
||||
'R': runtimeMessageParser,
|
||||
'E': eventMessageParser,
|
||||
'C': callMessageParser,
|
||||
'W': windowMessageParser,
|
||||
'D': dialogMessageParser,
|
||||
'S': systemMessageParser,
|
||||
}
|
||||
|
||||
// Parse will attempt to parse the given message
|
||||
func Parse(message string) (*parsedMessage, error) {
|
||||
|
||||
parseMethod := messageParsers[message[0]]
|
||||
if parseMethod == nil {
|
||||
return nil, fmt.Errorf("message type '%b' invalid", message[0])
|
||||
}
|
||||
|
||||
return parseMethod(message)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
// runtimeMessageParser does what it says on the tin!
|
||||
func runtimeMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Log messages must be at least 2 bytes
|
||||
if len(message) < 3 {
|
||||
return nil, fmt.Errorf("runtime message was an invalid length")
|
||||
}
|
||||
|
||||
// Switch on the runtime module type
|
||||
module := message[1]
|
||||
switch module {
|
||||
case 'B':
|
||||
return processBrowserMessage(message)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown message: %s", message)
|
||||
}
|
||||
|
||||
// processBrowserMessage expects messages of the following format:
|
||||
// RB<METHOD><DATA>
|
||||
// O = Open
|
||||
func processBrowserMessage(message string) (*parsedMessage, error) {
|
||||
method := message[2]
|
||||
switch method {
|
||||
case 'O':
|
||||
// Open URL
|
||||
target := message[3:]
|
||||
return &parsedMessage{Topic: "runtime:browser:open", Data: target}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown browser message: %s", message)
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package message
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// systemMessageParser does what it says on the tin!
|
||||
func systemMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: system messages must be at least 4 bytes
|
||||
if len(message) < 4 {
|
||||
return nil, fmt.Errorf("system message was an invalid length")
|
||||
}
|
||||
|
||||
var responseMessage *parsedMessage
|
||||
|
||||
// Remove 'S'
|
||||
message = message[1:]
|
||||
|
||||
// Switch the event type (with or without data)
|
||||
switch message[0] {
|
||||
// Format of system response messages: S<command><callbackID>|<payload>
|
||||
// DarkModeEnabled
|
||||
case 'D':
|
||||
message = message[1:]
|
||||
idx := strings.IndexByte(message, '|')
|
||||
if idx < 0 {
|
||||
return nil, fmt.Errorf("Invalid system response message format")
|
||||
}
|
||||
callbackID := message[:idx]
|
||||
payloadData := message[idx+1:]
|
||||
|
||||
topic := "systemresponse:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData == "T"}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||
}
|
||||
|
||||
return responseMessage, nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package message
|
||||
|
||||
import "fmt"
|
||||
|
||||
// windowMessageParser does what it says on the tin!
|
||||
func windowMessageParser(message string) (*parsedMessage, error) {
|
||||
|
||||
// Sanity check: Window messages must be at least 2 bytes
|
||||
if len(message) < 2 {
|
||||
return nil, fmt.Errorf("window message was an invalid length")
|
||||
}
|
||||
|
||||
// Extract event type
|
||||
windowEvent := message[1]
|
||||
parsedMessage := &parsedMessage{}
|
||||
|
||||
// Switch the windowEvent type
|
||||
switch windowEvent {
|
||||
|
||||
// Closed window
|
||||
case 'C':
|
||||
parsedMessage.Topic = "quit"
|
||||
parsedMessage.Data = "Window Closed"
|
||||
|
||||
// Center window
|
||||
case 'c':
|
||||
parsedMessage.Topic = "window:center"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Hide window
|
||||
case 'H':
|
||||
parsedMessage.Topic = "window:hide"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Show window
|
||||
case 'S':
|
||||
parsedMessage.Topic = "window:show"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Position window
|
||||
case 'p':
|
||||
parsedMessage.Topic = "window:position:" + message[3:]
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Set window size
|
||||
case 's':
|
||||
parsedMessage.Topic = "window:size:" + message[3:]
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Maximise window
|
||||
case 'M':
|
||||
parsedMessage.Topic = "window:maximise"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Unmaximise window
|
||||
case 'U':
|
||||
parsedMessage.Topic = "window:unmaximise"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Minimise window
|
||||
case 'm':
|
||||
parsedMessage.Topic = "window:minimise"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Unminimise window
|
||||
case 'u':
|
||||
parsedMessage.Topic = "window:unminimise"
|
||||
parsedMessage.Data = ""
|
||||
|
||||
// Unknown event type
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown message: %s", message)
|
||||
}
|
||||
|
||||
return parsedMessage, nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user