mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 04:59:38 +08:00
Feature: AssetServer Runtime (#2335)
* Tidy up runtime JS * Initial implementation of runtime over http * Update runtime deps. Fix test task. * Support Clipboard. Message Processor refactor. * Add `Window.Screen()` Clipboard `GetText` -> `Text` * Support most dialogs Better JS->Go object mapping Implement Go->JS callback mechanism Rename `window.runtime` -> `window.wails` to better reflect the Go API * Support SaveFile dialog * Remove go.work * Tidy up * Event->CustomEvent to prevent potential clash with native JS Event object Support Eventing * Support application calls * Support logging * Support named windows Remove debug info * Update v3 changes
This commit is contained in:
parent
88e79f2598
commit
5dbda4aead
@ -6,16 +6,19 @@ github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keL
|
|||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
|
github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
runtimeJSPath = "/wails/runtime.js"
|
runtimeJSPath = "/wails/runtime.js"
|
||||||
ipcJSPath = "/wails/ipc.js"
|
ipcJSPath = "/wails/ipc.js"
|
||||||
|
runtimePath = "/wails/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeAssets interface {
|
type RuntimeAssets interface {
|
||||||
@ -22,6 +23,10 @@ type RuntimeAssets interface {
|
|||||||
RuntimeDesktopJS() []byte
|
RuntimeDesktopJS() []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RuntimeHandler interface {
|
||||||
|
HandleRuntimeCall(w http.ResponseWriter, r *http.Request)
|
||||||
|
}
|
||||||
|
|
||||||
type AssetServer struct {
|
type AssetServer struct {
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
wsHandler http.Handler
|
wsHandler http.Handler
|
||||||
@ -34,6 +39,9 @@ type AssetServer struct {
|
|||||||
servingFromDisk bool
|
servingFromDisk bool
|
||||||
appendSpinnerToBody bool
|
appendSpinnerToBody bool
|
||||||
|
|
||||||
|
// Use http based runtime
|
||||||
|
runtimeHandler RuntimeHandler
|
||||||
|
|
||||||
assetServerWebView
|
assetServerWebView
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +85,10 @@ func NewAssetServerWithHandler(handler http.Handler, bindingsJSON string, servin
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *AssetServer) UseRuntimeHandler(handler RuntimeHandler) {
|
||||||
|
d.runtimeHandler = handler
|
||||||
|
}
|
||||||
|
|
||||||
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
if isWebSocket(req) {
|
if isWebSocket(req) {
|
||||||
// Forward WebSockets to the distinct websocket handler if it exists
|
// Forward WebSockets to the distinct websocket handler if it exists
|
||||||
@ -122,6 +134,13 @@ func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
case runtimeJSPath:
|
case runtimeJSPath:
|
||||||
d.writeBlob(rw, path, d.runtimeJS)
|
d.writeBlob(rw, path, d.runtimeJS)
|
||||||
|
|
||||||
|
case runtimePath:
|
||||||
|
if d.runtimeHandler != nil {
|
||||||
|
d.runtimeHandler.HandleRuntimeCall(rw, req)
|
||||||
|
} else {
|
||||||
|
d.handler.ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
|
||||||
case ipcJSPath:
|
case ipcJSPath:
|
||||||
content := d.runtime.DesktopIPC()
|
content := d.runtime.DesktopIPC()
|
||||||
if d.ipcJS != nil {
|
if d.ipcJS != nil {
|
||||||
|
18
v3/TODO.md
18
v3/TODO.md
@ -19,10 +19,20 @@ Informal and incomplete list of things needed in v3.
|
|||||||
- [ ] Implement alias for `window` in JS
|
- [ ] Implement alias for `window` in JS
|
||||||
- [ ] Implement runtime dispatcher
|
- [ ] Implement runtime dispatcher
|
||||||
- [ ] Log
|
- [ ] Log
|
||||||
- [ ] Same Window
|
- [x] Same Window
|
||||||
- [ ] Other Window
|
- [x] Other Window
|
||||||
- [ ] Dialogs
|
- [x] Dialogs
|
||||||
- [ ] Events
|
- [x] Info
|
||||||
|
- [x] Warning
|
||||||
|
- [x] Error
|
||||||
|
- [x] Question
|
||||||
|
- [x] OpenFile
|
||||||
|
- [x] SaveFile
|
||||||
|
- [x] Events
|
||||||
|
- [ ] Screens
|
||||||
|
- [x] Clipboard
|
||||||
|
- [ ] Application
|
||||||
|
- [ ] Create `.d.ts` file
|
||||||
|
|
||||||
## Templates
|
## Templates
|
||||||
|
|
||||||
|
@ -87,32 +87,15 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- npm install
|
- npm install
|
||||||
|
|
||||||
build-runtime-dev:
|
|
||||||
dir: internal/runtime/dev
|
|
||||||
deps:
|
|
||||||
- install-runtime-dev-deps
|
|
||||||
sources:
|
|
||||||
- ./*.js
|
|
||||||
generates:
|
|
||||||
- ../ipc_websocket.js
|
|
||||||
cmds:
|
|
||||||
- node build.js
|
|
||||||
|
|
||||||
build-runtime-ipc:
|
|
||||||
dir: internal/runtime
|
|
||||||
deps:
|
|
||||||
- install-runtime-dev-deps
|
|
||||||
sources:
|
|
||||||
- ./desktop/ipc.js
|
|
||||||
generates:
|
|
||||||
- ipc.js
|
|
||||||
cmds:
|
|
||||||
- npx esbuild desktop/ipc.js --bundle --minify --outfile=ipc.js
|
|
||||||
|
|
||||||
test-runtime:
|
test-runtime:
|
||||||
dir: internal/runtime
|
dir: internal/runtime
|
||||||
cmds:
|
cmds:
|
||||||
- npx vitest
|
- npx vitest run
|
||||||
|
|
||||||
|
update-runtime:
|
||||||
|
dir: internal/runtime
|
||||||
|
cmds:
|
||||||
|
- npx npm-check-updates -u
|
||||||
|
|
||||||
build-runtime-all:
|
build-runtime-all:
|
||||||
dir: internal/runtime
|
dir: internal/runtime
|
||||||
@ -123,10 +106,9 @@ tasks:
|
|||||||
- build-runtime-debug-darwin
|
- build-runtime-debug-darwin
|
||||||
- build-runtime-debug-windows
|
- build-runtime-debug-windows
|
||||||
- build-runtime-debug-linux
|
- build-runtime-debug-linux
|
||||||
- build-runtime-dev
|
|
||||||
- build-runtime-ipc
|
|
||||||
cmds:
|
cmds:
|
||||||
- task: test-runtime
|
- cmd: echo "build complete"
|
||||||
|
|
||||||
build-runtime:
|
build-runtime:
|
||||||
dir: internal/runtime
|
dir: internal/runtime
|
||||||
|
61
v3/V3 Changes.md
Normal file
61
v3/V3 Changes.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Changes for v3
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
In v3, there are 3 types of events:
|
||||||
|
|
||||||
|
- Application Events
|
||||||
|
- Window Events
|
||||||
|
- Custom Events
|
||||||
|
|
||||||
|
### Application Events
|
||||||
|
|
||||||
|
Application events are events that are emitted by the application. These events include native events such as `ApplicationDidFinishLaunching` on macOS.
|
||||||
|
|
||||||
|
### Window Events
|
||||||
|
|
||||||
|
Window events are events that are emitted by a window. These events include native events such as `WindowDidBecomeMain` on macOS.
|
||||||
|
|
||||||
|
### Custom Events
|
||||||
|
|
||||||
|
Events that the user defines are called `CustomEvents`. This is to differentiate them from the `Event` object that is used to communicate with the browser. CustomEvents are now objects that encapsulate all the details of an event. This includes the event name, the data, and the source of the event.
|
||||||
|
|
||||||
|
The data associated with a CustomEvent is now a single value. If multiple values are required, then a struct can be used.
|
||||||
|
|
||||||
|
### Event callbacks and `Emit` function signature
|
||||||
|
|
||||||
|
The signatures events callbacks (as used by `On`, `Once` & `OnMultiple`) have changed. In v2, the callback function received optional data. In v3, the callback function receives a `CustomEvent` object that contains all data related to the event.
|
||||||
|
|
||||||
|
Similarly, the `Emit` function has changed. Instead of taking a name and optional data, it now takes a single `CustomEvent` object that it will emit.
|
||||||
|
|
||||||
|
### `Off` and `OffAll`
|
||||||
|
|
||||||
|
In v2, `Off` and `OffAll` calls would remove events in both JS and Go. Due to the multi-window nature of v3, this has been changed so that these methods only apply to the context they are called in. For example, if you call `Off` in a window, it will only remove events for that window. If you use `Off` in Go, it will only remove events for Go.
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
There was a lot of requests for different types of logging in v2 so for v3 we have simplified things to make it as customisable as you want. There is now a single call `Log` that takes a LogMessage object. This object contains the message, the level, and the source, the log message and any data to be printed out. The default logger is the Console logger, however any number of outputs to log to can be added. Simply add custom loggers to the `options.Application.Logger.CustomLoggers` slice. The default logger does not have log level filtering, however custom loggers can be added that do.
|
||||||
|
|
||||||
|
### Developer notes
|
||||||
|
|
||||||
|
When emitting an event in Go, it will dispatch the event to local Go listeners and also each window in the application.
|
||||||
|
When emitting an event in JS, it now sends the event to the application. This will be processed as if it was emitted in Go, however the sender ID will be that of the window.
|
||||||
|
|
||||||
|
## Window
|
||||||
|
|
||||||
|
The Window API has largely remained the same, however there are a few changes to note.
|
||||||
|
|
||||||
|
- Windows now have a Name that identifies them. This is used to identify the window when emitting events.
|
||||||
|
|
||||||
|
## ClipBoard
|
||||||
|
|
||||||
|
The clipboard API has been simplified. There is now a single `Clipboard` object that can be used to read and write to the clipboard. The `Clipboard` object is available in both Go and JS. `SetText()` to set the text and `Text()` to get the text.
|
||||||
|
|
||||||
|
## Bindings
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
## Dialogs
|
||||||
|
|
||||||
|
Dialogs are now available in JavaScript!
|
||||||
|
|
21
v3/examples/events/assets/index.html
Normal file
21
v3/examples/events/assets/index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Title</title>
|
||||||
|
<style>body{ text-align: center; color: white; background-color: rgba(0,0,0,0); user-select: none; -ms-user-select: none; -webkit-user-select: none; }</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Events Demo</h1>
|
||||||
|
<br/>
|
||||||
|
<div id="results"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
wails.Events.On("myevent", function(data) {
|
||||||
|
let currentHTML = document.getElementById("results").innerHTML;
|
||||||
|
document.getElementById("results").innerHTML = currentHTML + "<br/>" + JSON.stringify(data);;
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
72
v3/examples/events/main.go
Normal file
72
v3/examples/events/main.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
_ "embed"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/options"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed assets
|
||||||
|
var assets embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
app := application.New(options.Application{
|
||||||
|
Name: "Events Demo",
|
||||||
|
Description: "A demo of the Events API",
|
||||||
|
Mac: options.Mac{
|
||||||
|
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Events.On("myevent", func(e *application.CustomEvent) {
|
||||||
|
log.Printf("[Go] CustomEvent received: %+v\n", e)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
|
||||||
|
for {
|
||||||
|
log.Println("Sending event")
|
||||||
|
app.Events.Emit(&application.CustomEvent{
|
||||||
|
Name: "myevent",
|
||||||
|
Data: "hello",
|
||||||
|
})
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
|
||||||
|
Title: "Events Demo",
|
||||||
|
Assets: options.Assets{
|
||||||
|
FS: assets,
|
||||||
|
},
|
||||||
|
Mac: options.MacWindow{
|
||||||
|
Backdrop: options.MacBackdropTranslucent,
|
||||||
|
TitleBar: options.TitleBarHiddenInsetUnified,
|
||||||
|
InvisibleTitleBarHeight: 50,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
app.NewWebviewWindowWithOptions(&options.WebviewWindow{
|
||||||
|
Title: "Events Demo",
|
||||||
|
Assets: options.Assets{
|
||||||
|
FS: assets,
|
||||||
|
},
|
||||||
|
Mac: options.MacWindow{
|
||||||
|
Backdrop: options.MacBackdropTranslucent,
|
||||||
|
TitleBar: options.TitleBarHiddenInsetUnified,
|
||||||
|
InvisibleTitleBarHeight: 50,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
err := app.Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,11 @@ go 1.19
|
|||||||
require (
|
require (
|
||||||
github.com/go-task/task/v3 v3.20.0
|
github.com/go-task/task/v3 v3.20.0
|
||||||
github.com/jackmordaunt/icns/v2 v2.2.1
|
github.com/jackmordaunt/icns/v2 v2.2.1
|
||||||
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/leaanthony/clir v1.6.0
|
github.com/leaanthony/clir v1.6.0
|
||||||
github.com/leaanthony/gosod v1.0.3
|
github.com/leaanthony/gosod v1.0.3
|
||||||
github.com/leaanthony/winicon v1.0.0
|
github.com/leaanthony/winicon v1.0.0
|
||||||
|
github.com/matryer/is v1.4.0
|
||||||
github.com/pterm/pterm v0.12.51
|
github.com/pterm/pterm v0.12.51
|
||||||
github.com/samber/lo v1.37.0
|
github.com/samber/lo v1.37.0
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
@ -33,6 +35,8 @@ require (
|
|||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||||
github.com/mattn/go-zglob v0.0.4 // indirect
|
github.com/mattn/go-zglob v0.0.4 // indirect
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
@ -53,3 +57,5 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
mvdan.cc/sh/v3 v3.6.0 // indirect
|
mvdan.cc/sh/v3 v3.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/wailsapp/wails/v2 => ../v2
|
||||||
|
10
v3/go.sum
10
v3/go.sum
@ -26,6 +26,7 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
|
|||||||
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
|
github.com/go-task/task/v3 v3.20.0 h1:pTavuhP+AiEpKLzh5I6Lja9Ux7ypYO5QMsEPTbhYEDc=
|
||||||
github.com/go-task/task/v3 v3.20.0/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
|
github.com/go-task/task/v3 v3.20.0/go.mod h1:y7rWakbLR5gFElGgo6rA2dyr6vU/zNIDVfn3S4Of6OI=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||||
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
|
github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI=
|
||||||
@ -36,6 +37,8 @@ github.com/jackmordaunt/icns/v2 v2.2.1 h1:MGklwYP2yohKn2Bw7XxlF69LZe98S1vUfl5OvA
|
|||||||
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
|
github.com/jackmordaunt/icns/v2 v2.2.1/go.mod h1:6aYIB9eSzyfHHMKqDf17Xrs1zetQPReAkiUSHzdw4cI=
|
||||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
@ -74,6 +77,10 @@ github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM=
|
|||||||
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
|
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
|
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 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
@ -107,6 +114,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
@ -119,8 +127,6 @@ github.com/tc-hib/winres v0.1.6 h1:qgsYHze+BxQPEYilxIz/KCQGaClvI2+yLBAZs+3+0B8=
|
|||||||
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
github.com/tc-hib/winres v0.1.6/go.mod h1:pe6dOR40VOrGz8PkzreVKNvEKnlE8t4yR8A8naL+t7A=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||||
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6 h1:Wn+nhnS+VytzE0PegUzSh4T3hXJCtggKGD/4U5H9+wQ=
|
|
||||||
github.com/wailsapp/wails/v2 v2.3.2-0.20230117193915-45c3a501d9e6/go.mod h1:zlNLI0E2c2qA6miiuAHtp0Bac8FaGH0tlhA19OssR/8=
|
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
@ -37,6 +37,6 @@ func GenerateBindings(options *GenerateBindingsOptions) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error writing models file: %v", err)
|
return fmt.Errorf("error writing models file: %v", err)
|
||||||
}
|
}
|
||||||
|
println("Generated models file '" + options.ModelsFilename + "'")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -3,22 +3,19 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
var RuntimeAssetsBundle = &RuntimeAssets{
|
var RuntimeAssetsBundle = &RuntimeAssets{
|
||||||
desktopIPC: DesktopIPC,
|
|
||||||
runtimeDesktopJS: DesktopRuntime,
|
runtimeDesktopJS: DesktopRuntime,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeAssets struct {
|
type RuntimeAssets struct {
|
||||||
desktopIPC []byte
|
|
||||||
websocketIPC []byte
|
|
||||||
runtimeDesktopJS []byte
|
runtimeDesktopJS []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) DesktopIPC() []byte {
|
func (r *RuntimeAssets) DesktopIPC() []byte {
|
||||||
return r.desktopIPC
|
return []byte("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
||||||
return r.websocketIPC
|
return []byte("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
||||||
|
@ -3,23 +3,19 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
var RuntimeAssetsBundle = &RuntimeAssets{
|
var RuntimeAssetsBundle = &RuntimeAssets{
|
||||||
desktopIPC: DesktopIPC,
|
|
||||||
websocketIPC: WebsocketIPC,
|
|
||||||
runtimeDesktopJS: DesktopRuntime,
|
runtimeDesktopJS: DesktopRuntime,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeAssets struct {
|
type RuntimeAssets struct {
|
||||||
desktopIPC []byte
|
|
||||||
websocketIPC []byte
|
|
||||||
runtimeDesktopJS []byte
|
runtimeDesktopJS []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) DesktopIPC() []byte {
|
func (r *RuntimeAssets) DesktopIPC() []byte {
|
||||||
return r.desktopIPC
|
return []byte("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
func (r *RuntimeAssets) WebsocketIPC() []byte {
|
||||||
return r.websocketIPC
|
return []byte("")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
func (r *RuntimeAssets) RuntimeDesktopJS() []byte {
|
||||||
|
3
v3/internal/runtime/desktop/README.md
Normal file
3
v3/internal/runtime/desktop/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# README
|
||||||
|
|
||||||
|
After updating any files in this directory, you must run `wails task build-runtime` to regenerate the compiled JS.
|
27
v3/internal/runtime/desktop/application.js
Normal file
27
v3/internal/runtime/desktop/application.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The electron alternative for Go
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
|
|
||||||
|
let call = newRuntimeCaller("application");
|
||||||
|
|
||||||
|
export function Hide() {
|
||||||
|
return call("Hide");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Show() {
|
||||||
|
return call("Show");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Quit() {
|
||||||
|
return call("Quit");
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
_ __ _ __
|
|
||||||
| | / /___ _(_) /____
|
|
||||||
| | /| / / __ `/ / / ___/
|
|
||||||
| |/ |/ / /_/ / / (__ )
|
|
||||||
|__/|__/\__,_/_/_/____/
|
|
||||||
The electron alternative for Go
|
|
||||||
(c) Lea Anthony 2019-present
|
|
||||||
*/
|
|
||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
import {Call} from './calls';
|
|
||||||
|
|
||||||
// This is where we bind go method wrappers
|
|
||||||
window.go = {};
|
|
||||||
|
|
||||||
export function SetBindings(bindingsMap) {
|
|
||||||
try {
|
|
||||||
bindingsMap = JSON.parse(bindingsMap);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise the bindings map
|
|
||||||
window.go = window.go || {};
|
|
||||||
|
|
||||||
// Iterate package names
|
|
||||||
Object.keys(bindingsMap).forEach((packageName) => {
|
|
||||||
|
|
||||||
// Create inner map if it doesn't exist
|
|
||||||
window.go[packageName] = window.go[packageName] || {};
|
|
||||||
|
|
||||||
// Iterate struct names
|
|
||||||
Object.keys(bindingsMap[packageName]).forEach((structName) => {
|
|
||||||
|
|
||||||
// Create inner map if it doesn't exist
|
|
||||||
window.go[packageName][structName] = window.go[packageName][structName] || {};
|
|
||||||
|
|
||||||
Object.keys(bindingsMap[packageName][structName]).forEach((methodName) => {
|
|
||||||
|
|
||||||
window.go[packageName][structName][methodName] = function () {
|
|
||||||
|
|
||||||
// No timeout by default
|
|
||||||
let timeout = 0;
|
|
||||||
|
|
||||||
// Actual function
|
|
||||||
function dynamic() {
|
|
||||||
const args = [].slice.call(arguments);
|
|
||||||
return Call([packageName, structName, methodName].join('.'), args, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow setting timeout to function
|
|
||||||
dynamic.setTimeout = function (newTimeout) {
|
|
||||||
timeout = newTimeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allow getting timeout to function
|
|
||||||
dynamic.getTimeout = function () {
|
|
||||||
return timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
return dynamic;
|
|
||||||
}();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
/**
|
|
||||||
* @description: Use the system default browser to open the url
|
|
||||||
* @param {string} url
|
|
||||||
* @return {void}
|
|
||||||
*/
|
|
||||||
export function BrowserOpenURL(url) {
|
|
||||||
window.WailsInvoke('BO:' + url);
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
/*
|
|
||||||
_ __ _ __
|
|
||||||
| | / /___ _(_) /____
|
|
||||||
| | /| / / __ `/ / / ___/
|
|
||||||
| |/ |/ / /_/ / / (__ )
|
|
||||||
|__/|__/\__,_/_/_/____/
|
|
||||||
The electron alternative for Go
|
|
||||||
(c) Lea Anthony 2019-present
|
|
||||||
*/
|
|
||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
export const callbacks = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a number from the native browser random function
|
|
||||||
*
|
|
||||||
* @returns number
|
|
||||||
*/
|
|
||||||
function cryptoRandom() {
|
|
||||||
var array = new Uint32Array(1);
|
|
||||||
return window.crypto.getRandomValues(array)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a number using da old-skool Math.Random
|
|
||||||
* I likes to call it LOLRandom
|
|
||||||
*
|
|
||||||
* @returns number
|
|
||||||
*/
|
|
||||||
function basicRandom() {
|
|
||||||
return Math.random() * 9007199254740991;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pick a random number function based on browser capability
|
|
||||||
var randomFunc;
|
|
||||||
if (window.crypto) {
|
|
||||||
randomFunc = cryptoRandom;
|
|
||||||
} else {
|
|
||||||
randomFunc = basicRandom;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call sends a message to the backend to call the binding with the
|
|
||||||
* given data. A promise is returned and will be completed when the
|
|
||||||
* backend responds. This will be resolved when the call was successful
|
|
||||||
* or rejected if an error is passed back.
|
|
||||||
* There is a timeout mechanism. If the call doesn't respond in the given
|
|
||||||
* time (in milliseconds) then the promise is rejected.
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} name
|
|
||||||
* @param {any=} args
|
|
||||||
* @param {number=} timeout
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function Call(name, args, timeout) {
|
|
||||||
|
|
||||||
// Timeout infinite by default
|
|
||||||
if (timeout == null) {
|
|
||||||
timeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let windowID = window.wails.window.ID();
|
|
||||||
|
|
||||||
// Create a promise
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
// Create a unique callbackID
|
|
||||||
var callbackID;
|
|
||||||
do {
|
|
||||||
callbackID = name + '-' + randomFunc();
|
|
||||||
} while (callbacks[callbackID]);
|
|
||||||
|
|
||||||
var timeoutHandle;
|
|
||||||
// Set timeout
|
|
||||||
if (timeout > 0) {
|
|
||||||
timeoutHandle = setTimeout(function () {
|
|
||||||
reject(Error('Call to ' + name + ' timed out. Request ID: ' + callbackID));
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store callback
|
|
||||||
callbacks[callbackID] = {
|
|
||||||
timeoutHandle: timeoutHandle,
|
|
||||||
reject: reject,
|
|
||||||
resolve: resolve
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const payload = {
|
|
||||||
name,
|
|
||||||
args,
|
|
||||||
callbackID,
|
|
||||||
windowID,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make the call
|
|
||||||
window.WailsInvoke('C' + JSON.stringify(payload));
|
|
||||||
} catch (e) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.ObfuscatedCall = (id, args, timeout) => {
|
|
||||||
|
|
||||||
// Timeout infinite by default
|
|
||||||
if (timeout == null) {
|
|
||||||
timeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a promise
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
// Create a unique callbackID
|
|
||||||
var callbackID;
|
|
||||||
do {
|
|
||||||
callbackID = id + '-' + randomFunc();
|
|
||||||
} while (callbacks[callbackID]);
|
|
||||||
|
|
||||||
var timeoutHandle;
|
|
||||||
// Set timeout
|
|
||||||
if (timeout > 0) {
|
|
||||||
timeoutHandle = setTimeout(function () {
|
|
||||||
reject(Error('Call to method ' + id + ' timed out. Request ID: ' + callbackID));
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store callback
|
|
||||||
callbacks[callbackID] = {
|
|
||||||
timeoutHandle: timeoutHandle,
|
|
||||||
reject: reject,
|
|
||||||
resolve: resolve
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const payload = {
|
|
||||||
id,
|
|
||||||
args,
|
|
||||||
callbackID,
|
|
||||||
windowID: window.wails.window.ID(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make the call
|
|
||||||
window.WailsInvoke('c' + JSON.stringify(payload));
|
|
||||||
} catch (e) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the backend to return data to a previously called
|
|
||||||
* binding invocation
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} incomingMessage
|
|
||||||
*/
|
|
||||||
export function Callback(incomingMessage) {
|
|
||||||
// Parse the message
|
|
||||||
let message;
|
|
||||||
try {
|
|
||||||
message = JSON.parse(incomingMessage);
|
|
||||||
} catch (e) {
|
|
||||||
const error = `Invalid JSON passed to callback: ${e.message}. Message: ${incomingMessage}`;
|
|
||||||
runtime.LogDebug(error);
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
let callbackID = message.callbackid;
|
|
||||||
let callbackData = callbacks[callbackID];
|
|
||||||
if (!callbackData) {
|
|
||||||
const error = `Callback '${callbackID}' not registered!!!`;
|
|
||||||
console.error(error); // eslint-disable-line
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
clearTimeout(callbackData.timeoutHandle);
|
|
||||||
|
|
||||||
delete callbacks[callbackID];
|
|
||||||
|
|
||||||
if (message.error) {
|
|
||||||
callbackData.reject(message.error);
|
|
||||||
} else {
|
|
||||||
callbackData.resolve(message.result);
|
|
||||||
}
|
|
||||||
}
|
|
23
v3/internal/runtime/desktop/clipboard.js
Normal file
23
v3/internal/runtime/desktop/clipboard.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The electron alternative for Go
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
|
|
||||||
|
let call = newRuntimeCaller("clipboard");
|
||||||
|
|
||||||
|
export function SetText(text) {
|
||||||
|
return call("SetText", {text});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Text() {
|
||||||
|
return call("Text");
|
||||||
|
}
|
85
v3/internal/runtime/desktop/dialogs.js
Normal file
85
v3/internal/runtime/desktop/dialogs.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The electron alternative for Go
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
|
|
||||||
|
import { nanoid } from 'nanoid/non-secure'
|
||||||
|
|
||||||
|
let call = newRuntimeCaller("dialog");
|
||||||
|
|
||||||
|
let dialogResponses = new Map();
|
||||||
|
|
||||||
|
function generateID() {
|
||||||
|
let result;
|
||||||
|
do {
|
||||||
|
result = nanoid();
|
||||||
|
} while (dialogResponses.has(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dialogCallback(id, data, isJSON) {
|
||||||
|
let p = dialogResponses.get(id);
|
||||||
|
if (p) {
|
||||||
|
if (isJSON) {
|
||||||
|
p.resolve(JSON.parse(data));
|
||||||
|
} else {
|
||||||
|
p.resolve(data);
|
||||||
|
}
|
||||||
|
dialogResponses.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function dialogErrorCallback(id, message) {
|
||||||
|
let p = dialogResponses.get(id);
|
||||||
|
if (p) {
|
||||||
|
p.reject(message);
|
||||||
|
dialogResponses.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dialog(type, options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let id = generateID();
|
||||||
|
options = options || {};
|
||||||
|
options["dialog-id"] = id;
|
||||||
|
dialogResponses.set(id, {resolve, reject});
|
||||||
|
call(type, options).catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
dialogResponses.delete(id);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function Info(options) {
|
||||||
|
return dialog("Info", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Warning(options) {
|
||||||
|
return dialog("Warning", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Error(options) {
|
||||||
|
return dialog("Error", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Question(options) {
|
||||||
|
return dialog("Question", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OpenFile(options) {
|
||||||
|
return dialog("OpenFile", options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveFile(options) {
|
||||||
|
return dialog("SaveFile", options);
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
_ __ _ __
|
_ __ _ __
|
||||||
| | / /___ _(_) /____
|
| | / /___ _(_) /____
|
||||||
| | /| / / __ `/ / / ___/
|
| | /| / / __ `/ / / ___/
|
||||||
| |/ |/ / /_/ / / (__ )
|
| |/ |/ / /_/ / / (__ )
|
||||||
|__/|__/\__,_/_/_/____/
|
|__/|__/\__,_/_/_/____/
|
||||||
The electron alternative for Go
|
The electron alternative for Go
|
||||||
(c) Lea Anthony 2019-present
|
(c) Lea Anthony 2019-present
|
||||||
*/
|
*/
|
||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
// Defines a single listener with a maximum number of times to callback
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
|
|
||||||
|
let call = newRuntimeCaller("events");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Listener class defines a listener! :-)
|
* The Listener class defines a listener! :-)
|
||||||
@ -31,7 +34,7 @@ class Listener {
|
|||||||
// Callback invokes the callback with the given data
|
// Callback invokes the callback with the given data
|
||||||
// Returns true if this listener should be destroyed
|
// Returns true if this listener should be destroyed
|
||||||
this.Callback = (data) => {
|
this.Callback = (data) => {
|
||||||
callback.apply(null, data);
|
callback(data);
|
||||||
// If maxCallbacks is infinite, return false (do not destroy)
|
// If maxCallbacks is infinite, return false (do not destroy)
|
||||||
if (this.maxCallbacks === -1) {
|
if (this.maxCallbacks === -1) {
|
||||||
return false;
|
return false;
|
||||||
@ -43,21 +46,41 @@ class Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const eventListeners = {};
|
|
||||||
|
/**
|
||||||
|
* CustomEvent defines a custom event. It is passed to event listeners.
|
||||||
|
*
|
||||||
|
* @class CustomEvent
|
||||||
|
*/
|
||||||
|
export class CustomEvent {
|
||||||
|
/**
|
||||||
|
* Creates an instance of CustomEvent.
|
||||||
|
* @param {string} name - Name of the event
|
||||||
|
* @param {any} data - Data associated with the event
|
||||||
|
* @memberof CustomEvent
|
||||||
|
*/
|
||||||
|
constructor(name, data) {
|
||||||
|
this.name = name;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const eventListeners = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function} callback
|
* @param {function(CustomEvent): void} callback
|
||||||
* @param {number} maxCallbacks
|
* @param {number} maxCallbacks
|
||||||
* @returns {function} A function to cancel the listener
|
* @returns {function} A function to cancel the listener
|
||||||
*/
|
*/
|
||||||
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
|
export function OnMultiple(eventName, callback, maxCallbacks) {
|
||||||
eventListeners[eventName] = eventListeners[eventName] || [];
|
let listeners = eventListeners.get(eventName) || [];
|
||||||
const thisListener = new Listener(eventName, callback, maxCallbacks);
|
const thisListener = new Listener(eventName, callback, maxCallbacks);
|
||||||
eventListeners[eventName].push(thisListener);
|
listeners.push(thisListener);
|
||||||
|
eventListeners.set(eventName, listeners);
|
||||||
return () => listenerOff(thisListener);
|
return () => listenerOff(thisListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,11 +89,11 @@ export function EventsOnMultiple(eventName, callback, maxCallbacks) {
|
|||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function} callback
|
* @param {function(CustomEvent): void} callback
|
||||||
* @returns {function} A function to cancel the listener
|
* @returns {function} A function to cancel the listener
|
||||||
*/
|
*/
|
||||||
export function EventsOn(eventName, callback) {
|
export function On(eventName, callback) {
|
||||||
return EventsOnMultiple(eventName, callback, -1);
|
return OnMultiple(eventName, callback, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,135 +101,87 @@ export function EventsOn(eventName, callback) {
|
|||||||
*
|
*
|
||||||
* @export
|
* @export
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {function} callback
|
* @param {function(CustomEvent): void} callback
|
||||||
* @returns {function} A function to cancel the listener
|
* @returns {function} A function to cancel the listener
|
||||||
*/
|
*/
|
||||||
export function EventsOnce(eventName, callback) {
|
export function Once(eventName, callback) {
|
||||||
return EventsOnMultiple(eventName, callback, 1);
|
return OnMultiple(eventName, callback, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function notifyListeners(eventData) {
|
/**
|
||||||
|
* listenerOff unregisters a listener previously registered with On
|
||||||
|
*
|
||||||
|
* @param {Listener} listener
|
||||||
|
*/
|
||||||
|
function listenerOff(listener) {
|
||||||
|
const eventName = listener.eventName;
|
||||||
|
// Remove local listener
|
||||||
|
let listeners = eventListeners.get(eventName).filter(l => l !== listener);
|
||||||
|
if (listeners.length === 0) {
|
||||||
|
eventListeners.delete(eventName);
|
||||||
|
} else {
|
||||||
|
eventListeners.set(eventName, listeners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the event name
|
/**
|
||||||
let eventName = eventData.name;
|
* dispatches an event to all listeners
|
||||||
|
*
|
||||||
// Check if we have any listeners for this event
|
* @export
|
||||||
if (eventListeners[eventName]) {
|
* @param {CustomEvent} event
|
||||||
|
*/
|
||||||
// Keep a list of listener indexes to destroy
|
export function dispatchCustomEvent(event) {
|
||||||
const newEventListenerList = eventListeners[eventName].slice();
|
console.log("dispatching event: ", {event});
|
||||||
|
let listeners = eventListeners.get(event.name);
|
||||||
// Iterate listeners
|
if (listeners) {
|
||||||
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
// iterate listeners and call callback. If callback returns true, remove listener
|
||||||
|
let toRemove = [];
|
||||||
// Get next listener
|
listeners.forEach(listener => {
|
||||||
const listener = eventListeners[eventName][count];
|
let remove = listener.Callback(event)
|
||||||
|
if (remove) {
|
||||||
let data = eventData.data;
|
toRemove.push(listener);
|
||||||
|
}
|
||||||
// Do the callback
|
});
|
||||||
const destroy = listener.Callback(data);
|
// remove listeners
|
||||||
if (destroy) {
|
if (toRemove.length > 0) {
|
||||||
// if the listener indicated to destroy itself, add it to the destroy list
|
listeners = listeners.filter(l => !toRemove.includes(l));
|
||||||
newEventListenerList.splice(count, 1);
|
if (listeners.length === 0) {
|
||||||
|
eventListeners.delete(event.name);
|
||||||
|
} else {
|
||||||
|
eventListeners.set(event.name, listeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update callbacks with new list of listeners
|
|
||||||
if (newEventListenerList.length === 0) {
|
|
||||||
removeListener(eventName);
|
|
||||||
} else {
|
|
||||||
eventListeners[eventName] = newEventListenerList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify informs frontend listeners that an event was emitted with the given data
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} notifyMessage - encoded notification message
|
|
||||||
|
|
||||||
*/
|
|
||||||
export function EventsNotify(notifyMessage) {
|
|
||||||
// Parse the message
|
|
||||||
let message;
|
|
||||||
try {
|
|
||||||
message = JSON.parse(notifyMessage);
|
|
||||||
} catch (e) {
|
|
||||||
const error = 'Invalid JSON passed to Notify: ' + notifyMessage;
|
|
||||||
throw new Error(error);
|
|
||||||
}
|
|
||||||
notifyListeners(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an event with the given name and data
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} eventName
|
|
||||||
*/
|
|
||||||
export function EventsEmit(eventName) {
|
|
||||||
|
|
||||||
const payload = {
|
|
||||||
name: eventName,
|
|
||||||
data: [].slice.apply(arguments).slice(1),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Notify JS listeners
|
|
||||||
notifyListeners(payload);
|
|
||||||
|
|
||||||
// Notify Go listeners
|
|
||||||
window.WailsInvoke('EE' + JSON.stringify(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeListener(eventName) {
|
|
||||||
// Remove local listeners
|
|
||||||
delete eventListeners[eventName];
|
|
||||||
|
|
||||||
// Notify Go listeners
|
|
||||||
window.WailsInvoke('EX' + eventName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Off unregisters a listener previously registered with On,
|
* Off unregisters a listener previously registered with On,
|
||||||
* optionally multiple listeneres can be unregistered via `additionalEventNames`
|
* optionally multiple listeners can be unregistered via `additionalEventNames`
|
||||||
|
*
|
||||||
|
[v3 CHANGE] Off only unregisters listeners within the current window
|
||||||
*
|
*
|
||||||
* @param {string} eventName
|
* @param {string} eventName
|
||||||
* @param {...string} additionalEventNames
|
* @param {...string} additionalEventNames
|
||||||
*/
|
*/
|
||||||
export function EventsOff(eventName, ...additionalEventNames) {
|
export function Off(eventName, ...additionalEventNames) {
|
||||||
removeListener(eventName)
|
let eventsToRemove = [eventName, ...additionalEventNames];
|
||||||
|
eventsToRemove.forEach(eventName => {
|
||||||
if (additionalEventNames.length > 0) {
|
eventListeners.delete(eventName);
|
||||||
additionalEventNames.forEach(eventName => {
|
})
|
||||||
removeListener(eventName)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Off unregisters all event listeners previously registered with On
|
* OffAll unregisters all listeners
|
||||||
*/
|
* [v3 CHANGE] OffAll only unregisters listeners within the current window
|
||||||
export function EventsOffAll() {
|
|
||||||
const eventNames = Object.keys(eventListeners);
|
|
||||||
for (let i = 0; i !== eventNames.length; i++) {
|
|
||||||
removeListener(eventNames[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* listenerOff unregisters a listener previously registered with EventsOn
|
|
||||||
*
|
*
|
||||||
* @param {Listener} listener
|
|
||||||
*/
|
*/
|
||||||
function listenerOff(listener) {
|
export function OffAll() {
|
||||||
const eventName = listener.eventName;
|
eventListeners.clear();
|
||||||
// Remove local listener
|
|
||||||
eventListeners[eventName] = eventListeners[eventName].filter(l => l !== listener);
|
|
||||||
|
|
||||||
// Clean up if there are no event listeners left
|
|
||||||
if (eventListeners[eventName].length === 0) {
|
|
||||||
removeListener(eventName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Emit emits an event to all listeners
|
||||||
|
*/
|
||||||
|
export function Emit(event) {
|
||||||
|
return call("Emit", event);
|
||||||
|
}
|
@ -1,132 +1,115 @@
|
|||||||
import { EventsOnMultiple, EventsNotify, eventListeners, EventsOn, EventsEmit, EventsOffAll, EventsOnce, EventsOff } from './events'
|
import { On, Off, OffAll, OnMultiple, CustomEvent, dispatchCustomEvent, eventListeners, Once } from './events'
|
||||||
import { expect, describe, it, beforeAll, vi, afterEach, beforeEach } from 'vitest'
|
import { expect, describe, it, vi, afterEach, beforeEach } from 'vitest'
|
||||||
// Edit an assertion and save to see HMR in action
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
window.WailsInvoke = vi.fn(() => {})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
EventsOffAll();
|
OffAll();
|
||||||
vi.resetAllMocks()
|
vi.resetAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('EventsOnMultiple', () => {
|
describe('OnMultiple', () => {
|
||||||
|
let testEvent = new CustomEvent('a', {})
|
||||||
|
|
||||||
it('should stop after a specified number of times', () => {
|
it('should stop after a specified number of times', () => {
|
||||||
const cb = vi.fn()
|
const cb = vi.fn()
|
||||||
EventsOnMultiple('a', cb, 5)
|
OnMultiple('a', cb, 5)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
expect(cb).toBeCalledTimes(5);
|
expect(cb).toBeCalledTimes(5);
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
|
||||||
expect(window.WailsInvoke).toHaveBeenLastCalledWith('EXa');
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a cancel fn', () => {
|
it('should return a cancel fn', () => {
|
||||||
const cb = vi.fn()
|
const cb = vi.fn()
|
||||||
const cancel = EventsOnMultiple('a', cb, 5)
|
const cancel = OnMultiple('a', cb, 5)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
cancel()
|
cancel()
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: {}}))
|
dispatchCustomEvent(testEvent)
|
||||||
expect(cb).toBeCalledTimes(2)
|
expect(cb).toBeCalledTimes(2)
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
|
||||||
expect(window.WailsInvoke).toHaveBeenLastCalledWith('EXa');
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('EventsOn', () => {
|
describe('On', () => {
|
||||||
it('should create a listener with a count of -1', () => {
|
it('should create a listener with a count of -1', () => {
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
expect(eventListeners['a'][0].maxCallbacks).toBe(-1)
|
expect(eventListeners.get("a")[0].maxCallbacks).toBe(-1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a cancel fn', () => {
|
it('should return a cancel fn', () => {
|
||||||
const cancel = EventsOn('a', () => {})
|
const cancel = On('a', () => {})
|
||||||
cancel();
|
cancel();
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
|
||||||
expect(window.WailsInvoke).toHaveBeenLastCalledWith('EXa');
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('EventsOnce', () => {
|
describe('Once', () => {
|
||||||
it('should create a listener with a count of 1', () => {
|
it('should create a listener with a count of 1', () => {
|
||||||
EventsOnce('a', () => {})
|
Once('a', () => {})
|
||||||
expect(eventListeners['a'][0].maxCallbacks).toBe(1)
|
expect(eventListeners.get("a")[0].maxCallbacks).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return a cancel fn', () => {
|
it('should return a cancel fn', () => {
|
||||||
const cancel = EventsOn('a', () => {})
|
const cancel = EventsOn('a', () => {})
|
||||||
cancel();
|
cancel();
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
|
||||||
expect(window.WailsInvoke).toHaveBeenLastCalledWith('EXa');
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
//
|
||||||
describe('EventsNotify', () => {
|
// describe('EventsNotify', () => {
|
||||||
it('should inform a listener', () => {
|
// it('should inform a listener', () => {
|
||||||
const cb = vi.fn()
|
// const cb = vi.fn()
|
||||||
EventsOn('a', cb)
|
// EventsOn('a', cb)
|
||||||
EventsNotify(JSON.stringify({name: 'a', data: ["one", "two", "three"]}))
|
// EventsNotify(JSON.stringify({name: 'a', data: ["one", "two", "three"]}))
|
||||||
expect(cb).toBeCalledTimes(1);
|
// expect(cb).toBeCalledTimes(1);
|
||||||
expect(cb).toHaveBeenLastCalledWith("one", "two", "three");
|
// expect(cb).toHaveBeenLastCalledWith("one", "two", "three");
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(0);
|
// expect(window.WailsInvoke).toBeCalledTimes(0);
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
describe('EventsEmit', () => {
|
// describe('EventsEmit', () => {
|
||||||
it('should emit an event', () => {
|
// it('should emit an event', () => {
|
||||||
EventsEmit('a', 'one', 'two', 'three')
|
// EventsEmit('a', 'one', 'two', 'three')
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
// expect(window.WailsInvoke).toBeCalledTimes(1);
|
||||||
const calledWith = window.WailsInvoke.calls[0][0];
|
// const calledWith = window.WailsInvoke.calls[0][0];
|
||||||
expect(calledWith.slice(0, 2)).toBe('EE')
|
// expect(calledWith.slice(0, 2)).toBe('EE')
|
||||||
expect(JSON.parse(calledWith.slice(2))).toStrictEqual({data: ["one", "two", "three"], name: "a"})
|
// expect(JSON.parse(calledWith.slice(2))).toStrictEqual({data: ["one", "two", "three"], name: "a"})
|
||||||
})
|
// })
|
||||||
})
|
// })
|
||||||
|
//
|
||||||
describe('EventsOff', () => {
|
describe('Off', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('b', () => {})
|
On('b', () => {})
|
||||||
EventsOn('c', () => {})
|
On('c', () => {})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should cancel all event listeners for a single type', () => {
|
it('should cancel all event listeners for a single type', () => {
|
||||||
EventsOff('a')
|
Off('a')
|
||||||
expect(eventListeners['a']).toBeUndefined()
|
expect(eventListeners.get('a')).toBeUndefined()
|
||||||
expect(eventListeners['b']).not.toBeUndefined()
|
expect(eventListeners.get('b')).not.toBeUndefined()
|
||||||
expect(eventListeners['c']).not.toBeUndefined()
|
expect(eventListeners.get('c')).not.toBeUndefined()
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(1);
|
|
||||||
expect(window.WailsInvoke).toHaveBeenLastCalledWith('EXa');
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should cancel all event listeners for multiple types', () => {
|
it('should cancel all event listeners for multiple types', () => {
|
||||||
EventsOff('a', 'b')
|
Off('a', 'b')
|
||||||
expect(eventListeners['a']).toBeUndefined()
|
expect(eventListeners.get('a')).toBeUndefined()
|
||||||
expect(eventListeners['b']).toBeUndefined()
|
expect(eventListeners.get('b')).toBeUndefined()
|
||||||
expect(eventListeners['c']).not.toBeUndefined()
|
expect(eventListeners.get('c')).not.toBeUndefined()
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(2);
|
|
||||||
expect(window.WailsInvoke.calls).toStrictEqual([['EXa'], ['EXb']]);
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('EventsOffAll', () => {
|
describe('OffAll', () => {
|
||||||
it('should cancel all event listeners', () => {
|
it('should cancel all event listeners', () => {
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('a', () => {})
|
On('a', () => {})
|
||||||
EventsOn('b', () => {})
|
On('b', () => {})
|
||||||
EventsOn('c', () => {})
|
On('c', () => {})
|
||||||
EventsOffAll()
|
OffAll()
|
||||||
expect(eventListeners).toStrictEqual({})
|
expect(eventListeners.size).toBe(0)
|
||||||
expect(window.WailsInvoke).toBeCalledTimes(3);
|
|
||||||
expect(window.WailsInvoke.calls).toStrictEqual([['EXa'], ['EXb'], ['EXc']]);
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
_ __ _ __
|
|
||||||
| | / /___ _(_) /____
|
|
||||||
| | /| / / __ `/ / / ___/
|
|
||||||
| |/ |/ / /_/ / / (__ )
|
|
||||||
|__/|__/\__,_/_/_/____/
|
|
||||||
The electron alternative for Go
|
|
||||||
(c) Lea Anthony 2019-present
|
|
||||||
*/
|
|
||||||
/* jshint esversion: 9 */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let postMessage = null;
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
// Credit: https://stackoverflow.com/a/2631521
|
|
||||||
let _deeptest = function (s) {
|
|
||||||
var obj = window[s.shift()];
|
|
||||||
while (obj && s.length) obj = obj[s.shift()];
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
let windows = _deeptest(["chrome", "webview", "postMessage"]);
|
|
||||||
let mac_linux = _deeptest(["webkit", "messageHandlers", "external", "postMessage"]);
|
|
||||||
|
|
||||||
if (!windows && !mac_linux) {
|
|
||||||
console.error("Unsupported Platform");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (windows) {
|
|
||||||
postMessage = (message) => window.chrome.webview.postMessage(message);
|
|
||||||
}
|
|
||||||
if (mac_linux) {
|
|
||||||
postMessage = (message) => window.webkit.messageHandlers.external.postMessage(message);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
export function invoke(message, id) {
|
|
||||||
if( id && id !== -1) {
|
|
||||||
postMessage("WINDOWID:"+ id + ":" + message);
|
|
||||||
} else {
|
|
||||||
postMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
_ __ _ __
|
_ __ _ __
|
||||||
| | / /___ _(_) /____
|
| | / /___ _(_) /____
|
||||||
| | /| / / __ `/ / / ___/
|
| | /| / / __ `/ / / ___/
|
||||||
| |/ |/ / /_/ / / (__ )
|
| |/ |/ / /_/ / / (__ )
|
||||||
|__/|__/\__,_/_/_/____/
|
|__/|__/\__,_/_/_/____/
|
||||||
@ -8,106 +8,16 @@ The electron alternative for Go
|
|||||||
(c) Lea Anthony 2019-present
|
(c) Lea Anthony 2019-present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
|
|
||||||
|
let call = newRuntimeCaller("log");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a log message to the backend with the given level + message
|
* Logs a message.
|
||||||
*
|
* @param {message} Message to log
|
||||||
* @param {string} level
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
*/
|
||||||
function sendLogMessage(level, message) {
|
export function Log(message) {
|
||||||
|
return call("Log", message);
|
||||||
// Log Message format:
|
|
||||||
// l[type][message]
|
|
||||||
window.WailsInvoke('L' + level + message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given trace message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogTrace(message) {
|
|
||||||
sendLogMessage('T', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogPrint(message) {
|
|
||||||
sendLogMessage('P', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given debug message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogDebug(message) {
|
|
||||||
sendLogMessage('D', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given info message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogInfo(message) {
|
|
||||||
sendLogMessage('I', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given warning message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogWarning(message) {
|
|
||||||
sendLogMessage('W', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given error message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogError(message) {
|
|
||||||
sendLogMessage('E', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log the given fatal message with the backend
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {string} message
|
|
||||||
*/
|
|
||||||
export function LogFatal(message) {
|
|
||||||
sendLogMessage('F', message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the Log level to the given log level
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {number} loglevel
|
|
||||||
*/
|
|
||||||
export function SetLogLevel(loglevel) {
|
|
||||||
sendLogMessage('S', loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log levels
|
|
||||||
export const LogLevel = {
|
|
||||||
TRACE: 1,
|
|
||||||
DEBUG: 2,
|
|
||||||
INFO: 3,
|
|
||||||
WARNING: 4,
|
|
||||||
ERROR: 5,
|
|
||||||
};
|
|
||||||
|
@ -9,48 +9,56 @@ The electron alternative for Go
|
|||||||
*/
|
*/
|
||||||
/* jshint esversion: 9 */
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
import {invoke} from "./ipc.js";
|
import {dialogCallback, dialogErrorCallback, Error, Info, OpenFile, Question, SaveFile, Warning,} from "./dialogs";
|
||||||
import {Callback, callbacks} from './calls';
|
|
||||||
import {EventsNotify, eventListeners} from "./events";
|
import * as Clipboard from './clipboard';
|
||||||
import {SetBindings} from "./bindings";
|
import * as Application from './application';
|
||||||
|
import * as Log from './log';
|
||||||
|
|
||||||
import {newWindow} from "./window";
|
import {newWindow} from "./window";
|
||||||
|
import {dispatchCustomEvent, Emit, Off, OffAll, On, Once, OnMultiple} from "./events";
|
||||||
// export function Environment() {
|
|
||||||
// return Call(":wails:Environment");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Internal wails endpoints
|
// Internal wails endpoints
|
||||||
window.wails = {
|
window.wails = {
|
||||||
Callback,
|
...newRuntime(-1),
|
||||||
callbacks,
|
|
||||||
EventsNotify,
|
|
||||||
eventListeners,
|
|
||||||
SetBindings,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window._wails = {
|
||||||
|
dialogCallback,
|
||||||
|
dialogErrorCallback,
|
||||||
|
dispatchCustomEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function newRuntime(id) {
|
export function newRuntime(id) {
|
||||||
return {
|
return {
|
||||||
// Log: newLog(id),
|
Clipboard: {
|
||||||
// Browser: newBrowser(id),
|
...Clipboard
|
||||||
// Screen: newScreen(id),
|
},
|
||||||
// Events: newEvents(id),
|
Application: {
|
||||||
|
...Application
|
||||||
|
},
|
||||||
|
Log,
|
||||||
|
Dialog: {
|
||||||
|
Info,
|
||||||
|
Warning,
|
||||||
|
Error,
|
||||||
|
Question,
|
||||||
|
OpenFile,
|
||||||
|
SaveFile,
|
||||||
|
},
|
||||||
|
Events: {
|
||||||
|
Emit,
|
||||||
|
On,
|
||||||
|
Once,
|
||||||
|
OnMultiple,
|
||||||
|
Off,
|
||||||
|
OffAll,
|
||||||
|
},
|
||||||
Window: newWindow(id),
|
Window: newWindow(id),
|
||||||
Show: () => invoke("S"),
|
|
||||||
Hide: () => invoke("H"),
|
|
||||||
Quit: () => invoke("Q"),
|
|
||||||
// GetWindow: function (windowID) {
|
|
||||||
// if (!windowID) {
|
|
||||||
// return this.Window;
|
|
||||||
// }
|
|
||||||
// return newWindow(windowID);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.runtime = newRuntime(-1);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log("Wails v3.0.0 Debug Mode Enabled");
|
console.log("Wails v3.0.0 Debug Mode Enabled");
|
||||||
}
|
}
|
||||||
|
50
v3/internal/runtime/desktop/runtime.js
Normal file
50
v3/internal/runtime/desktop/runtime.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The electron alternative for Go
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
const runtimeURL = window.location.origin + "/wails/runtime";
|
||||||
|
|
||||||
|
function runtimeCall(method, args) {
|
||||||
|
let url = new URL(runtimeURL);
|
||||||
|
url.searchParams.append("method", method);
|
||||||
|
if(args) {
|
||||||
|
url.searchParams.append("args", JSON.stringify(args));
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetch(url)
|
||||||
|
.then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
// check content type
|
||||||
|
if (response.headers.get("content-type") && response.headers.get("content-type").indexOf("application/json") !== -1) {
|
||||||
|
return response.json();
|
||||||
|
} else {
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reject(Error(response.statusText));
|
||||||
|
})
|
||||||
|
.then(data => resolve(data))
|
||||||
|
.catch(error => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newRuntimeCaller(object, id) {
|
||||||
|
if (!id || id === -1) {
|
||||||
|
return function (method, args) {
|
||||||
|
return runtimeCall(object + "." + method, args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return function (method, args) {
|
||||||
|
args = args || {};
|
||||||
|
args["windowID"] = id;
|
||||||
|
return runtimeCall(object + "." + method, args);
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
_ __ _ __
|
|
||||||
| | / /___ _(_) /____
|
|
||||||
| | /| / / __ `/ / / ___/
|
|
||||||
| |/ |/ / /_/ / / (__ )
|
|
||||||
|__/|__/\__,_/_/_/____/
|
|
||||||
The electron alternative for Go
|
|
||||||
(c) Lea Anthony 2019-present
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* jshint esversion: 9 */
|
|
||||||
|
|
||||||
|
|
||||||
import {Call} from "./calls";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
|
||||||
* @export
|
|
||||||
* @typedef {import('../wrapper/runtime').Screen} Screen
|
|
||||||
* @return {Promise<{Screen[]}>} The screens
|
|
||||||
*/
|
|
||||||
export function ScreenGetAll() {
|
|
||||||
return Call(":wails:ScreenGetAll");
|
|
||||||
}
|
|
@ -10,58 +10,39 @@ The electron alternative for Go
|
|||||||
|
|
||||||
/* jshint esversion: 9 */
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
import {newRuntimeCaller} from "./runtime";
|
||||||
import {Call} from "./calls";
|
|
||||||
import {invoke} from "./ipc";
|
|
||||||
|
|
||||||
export function newWindow(id) {
|
export function newWindow(id) {
|
||||||
|
let call = newRuntimeCaller("window", id);
|
||||||
return {
|
return {
|
||||||
// Reload: () => invoke('WR', id),
|
// Reload: () => call('WR'),
|
||||||
// ReloadApp: () => invoke('WR', id),
|
// ReloadApp: () => call('WR'),
|
||||||
// SetSystemDefaultTheme: () => invoke('WASDT', id),
|
// SetSystemDefaultTheme: () => call('WASDT'),
|
||||||
// SetLightTheme: () => invoke('WALT', id),
|
// SetLightTheme: () => call('WALT'),
|
||||||
// SetDarkTheme: () => invoke('WADT', id),
|
// SetDarkTheme: () => call('WADT'),
|
||||||
Center: () => invoke('Wc', id),
|
// IsFullscreen: () => call('WIF'),
|
||||||
SetTitle: (title) => invoke('WT' + title, id),
|
// IsMaximized: () => call('WIM'),
|
||||||
Fullscreen: () => invoke('WF', id),
|
// IsMinimized: () => call('WIMN'),
|
||||||
UnFullscreen: () => invoke('Wf', id),
|
// IsWindowed: () => call('WIF'),
|
||||||
SetSize: (width, height) => invoke('WS' + width + ',' + height, id),
|
Center: () => call('Center'),
|
||||||
GetSize: () => {
|
SetTitle: (title) => call('SetTitle', {title}),
|
||||||
return Call(":wails:WindowGetSize")
|
Fullscreen: () => call('Fullscreen'),
|
||||||
},
|
UnFullscreen: () => call('UnFullscreen'),
|
||||||
SetMaxSize: (width, height) => invoke('WZ:' + width + ':' + height, id),
|
SetSize: (width, height) => call('SetSize', {width,height}),
|
||||||
SetMinSize: (width, height) => invoke('Wz:' + width + ':' + height, id),
|
Size: () => { return call('Size') },
|
||||||
SetAlwaysOnTop: (b) => invoke('WATP:' + (b ? '1' : '0'), id),
|
SetMaxSize: (width, height) => call('SetMaxSize', {width,height}),
|
||||||
SetPosition: (x, y) => invoke('Wp:' + x + ':' + y, id),
|
SetMinSize: (width, height) => call('SetMinSize', {width,height}),
|
||||||
GetPosition: () => {
|
SetAlwaysOnTop: (b) => call('SetAlwaysOnTop', {alwaysOnTop:b}),
|
||||||
return Call(":wails:WindowGetPos")
|
SetPosition: (x, y) => call('SetPosition', {x,y}),
|
||||||
},
|
Position: () => { return call('Position') },
|
||||||
Hide: () => invoke('WH', id),
|
Screen: () => { return call('Screen') },
|
||||||
Maximise: () => invoke('WM', id),
|
Hide: () => call('Hide'),
|
||||||
Show: () => invoke('WS', id),
|
Maximise: () => call('Maximise'),
|
||||||
ToggleMaximise: () => invoke('Wt', id),
|
Show: () => call('Show'),
|
||||||
UnMaximise: () => invoke('WU', id),
|
ToggleMaximise: () => call('ToggleMaximise'),
|
||||||
Minimise: () => invoke('Wm', id),
|
UnMaximise: () => call('UnMaximise'),
|
||||||
UnMinimise: () => invoke('Wu', id),
|
Minimise: () => call('Minimise'),
|
||||||
SetBackgroundColour: (R, G, B, A) =>
|
UnMinimise: () => call('UnMinimise'),
|
||||||
invoke('Wr:' + JSON.stringify({
|
SetBackgroundColour: (r, g, b, a) => call('SetBackgroundColour', {r, g, b, a}),
|
||||||
r: R || 0,
|
|
||||||
g: G || 0,
|
|
||||||
b: B || 0,
|
|
||||||
a: A || 255}, id)
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function IsFullscreen: ()=> // return Call(":wails:WindowIsFullscreen"),
|
|
||||||
//
|
|
||||||
|
|
||||||
// export function IsMaximised: ()=> // return Call(":wails:WindowIsMaximised"),
|
|
||||||
//
|
|
||||||
|
|
||||||
// export function IsMinimised: ()=> // return Call(":wails:WindowIsMinimised"),
|
|
||||||
//
|
|
||||||
|
|
||||||
// export function IsNormal: ()=> // return Call(":wails:WindowIsNormal"),
|
|
||||||
//
|
|
||||||
|
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
<script>
|
|
||||||
|
|
||||||
import {overlayVisible} from './store'
|
|
||||||
import {fade,} from 'svelte/transition';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if $overlayVisible }
|
|
||||||
<div class="wails-reconnect-overlay" transition:fade="{{ duration: 300 }}">
|
|
||||||
<div class="wails-reconnect-overlay-content">
|
|
||||||
<div class="wails-reconnect-overlay-loadingspinner"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.wails-reconnect-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
backdrop-filter: blur(2px) saturate(0%) contrast(50%) brightness(25%);
|
|
||||||
z-index: 999999
|
|
||||||
}
|
|
||||||
|
|
||||||
.wails-reconnect-overlay-content {
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
margin: 0;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAAA7CAMAAAAEsocZAAAC91BMVEUAAACzQ0PjMjLkMjLZLS7XLS+vJCjkMjKlEx6uGyHjMDGiFx7GJyrAISjUKy3mMzPlMjLjMzOsGyDKJirkMjK6HyXmMjLgMDC6IiLcMjLULC3MJyrRKSy+IibmMzPmMjK7ISXlMjLIJimzHSLkMjKtGiHZLC7BIifgMDCpGSDFIivcLy+yHSKoGR+eFBzNKCvlMjKxHSPkMTKxHSLmMjLKJyq5ICXDJCe6ISXdLzDkMjLmMzPFJSm2HyTlMTLhMDGyHSKUEBmhFx24HyTCJCjHJijjMzOiFh7mMjJ6BhDaLDCuGyOKABjnMzPGJinJJiquHCGEChSmGB/pMzOiFh7VKy3OKCu1HiSvHCLjMTLMKCrBIyeICxWxHCLDIyjSKizBIyh+CBO9ISa6ISWDChS9Iie1HyXVLC7FJSrLKCrlMjLiMTGPDhicFRywGyKXFBuhFx1/BxO7IiXkMTGeFBx8BxLkMTGnGR/GJCi4ICWsGyGJDxXSLS2yGiHSKi3CJCfnMzPQKiyECRTKJiq6ISWUERq/Iye0HiPDJCjGJSm6ICaPDxiTEBrdLy+3HyXSKiy0HyOQEBi4ICWhFh1+CBO9IieODhfSKyzWLC2LDhh8BxHKKCq7ISWaFBzkMzPqNDTTLC3EJSiHDBacExyvGyO1HyTPKCy+IieoGSC7ISaVEhrMKCvQKyusGyG0HiKACBPIJSq/JCaABxR5BRLEJCnkMzPJJinEJimPDRZ2BRKqHx/jMjLnMzPgMDHULC3NKSvQKSzsNDTWLS7SKyy3HyTKJyrDJSjbLzDYLC6mGB/GJSnVLC61HiPLKCrHJSm/Iye8Iia6ICWzHSKxHCLaLi/PKSupGR+7ICXpMzPbLi/IJinJJSmsGyGrGiCkFx6PDheJCxaFChXBIyfAIieSDxmBCBPlMjLeLzDdLzC5HySMDRe+ISWvGyGcFBzSKSzPJyvMJyrEJCjDIyefFRyWERriMDHUKiy/ISaZExv0NjbwNTXuNDTrMzMI0c+yAAAAu3RSTlMAA8HR/gwGgAj+MEpGCsC+hGpjQjYnIxgWBfzx7urizMrFqqB1bF83KhsR/fz8+/r5+fXv7unZ1tC+t6mmopqKdW1nYVpVRjUeHhIQBPr59/b28/Hx8ODg3NvUw8O/vKeim5aNioiDgn1vZWNjX1xUU1JPTUVFPT08Mi4qJyIh/Pv7+/n4+Pf39fT08/Du7efn5uXj4uHa19XNwsG/vrq2tbSuramlnpyYkpGNiIZ+enRraGVjVVBKOzghdjzRsAAABJVJREFUWMPtllVQG1EYhTc0ASpoobS0FCulUHd3oUjd3d3d3d3d3d2b7CYhnkBCCHGDEIK7Vh56d0NpOgwkYfLQzvA9ZrLfnPvfc+8uVEst/yheBJup3Nya2MjU6pa/jWLZtxjXpZFtVB4uVNI6m5gIruNkVFebqIb5Ug2ym4TIEM/gtUOGbg613oBzjAzZFrZ+lXu/3TIiMXXS5M6HTvrNHeLpZLEh6suGNW9fzZ9zd/qVi2eOHygqi5cDE5GUrJocONgzyqo0UXNSUlKSEhMztFqtXq9vNxImAmS3g7Y6QlbjdBWVGW36jt4wDGTUXjUsafh5zJWRkdFuZGtWGnCRmg+HasiGMUClTTzW0ZuVgLlGDIPM4Lhi0IrVq+tv2hS21fNrSONQgpM9DsJ4t3fM9PkvJuKj2ZjrZwvILKvaSTgciUSirjt6dOfOpyd169bDb9rMOwF9Hj4OD100gY0YXYb299bjzMrqj9doNByJWlVXFB9DT5dmJuvy+cq83JyuS6ayEYSHulKL8dmFnBkrCeZlHKMrC5XRhXGCZB2Ty1fkleRQaMCFT2DBsEafzRFJu7/2MicbKynPhQUDLiZwMWLJZKNLzoLbJBYVcurSmbmn+rcyJ8vCMgmlmaW6gnwun/+3C96VpAUuET1ZgRR36r2xWlnYSnf3oKABA14uXDDvydxHs6cpTV1p3hlJ2rJCiUjIZCByItXg8sHJijuvT64CuMTABUYvb6NN1Jdp1PH7D7f3bo2eS5KvW4RJr7atWT5w4MBBg9zdBw9+37BS7QIoFS5WnIaj12dr1DEXFgdvr4fh4eFl+u/wz8uf3jjHic8s4DL2Dal0IANyUBeCRCcwOBJV26JsjSpGwHVuSai69jvqD+jr56OgtKy0zAAK5mLTVBKVKL5tNthGAR9JneJQ/bFsHNzy+U7IlCYROxtMpIjR0ceoQVnowracLLpAQWETqV361bPoFo3cEbz2zYLZM7t3HWXcxmiBOgttS1ycWkTXMWh4mGigdug9DFdttqCFgTN6nD0q1XEVSoCxEjyFCi2eNC6Z69MRVIImJ6JQSf5gcFVCuF+aDhCa1F6MJFDaiNBQAh2TMfWBjhmLsAxUjG/fmjs0qjJck8D0GPBcuUuZW1LS/tIsPzqmQt17PvZQknlwnf4tHDBc+7t5VV3QQCkdc+Ur8/hdrz0but0RCumWiYbiKmLJ7EVbRomj4Q7+y5wsaXvfTGFpQcHB7n2WbG4MGdniw2Tm8xl5Yhr7MrSYHQ3uampz10aWyHyuzxvqaW/6W4MjXAUD3QV2aw97ZxhGjxCohYf5TpTHMXU1BbsAuoFnkRygVieIGAbqiF7rrH4rfWpKJouBCtyHJF8ctEyGubBa+C6NsMYEUonJFITHZqWBxXUA12Dv76Tf/PgOBmeNiiLG1pcKo1HAq8jLpY4JU1yWEixVNaOgoRJAKBSZHTZTU+wJOMtUDZvlVITC6FTlksyrEBoPHXpxxbzdaqzigUtVDkJVIOtVQ9UEOR4VGUh/kHWq0edJ6CxnZ+eePXva2bnY/cF/I1RLLf8vvwDANdMSMegxcAAAAABJRU5ErkJggg==);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center
|
|
||||||
}
|
|
||||||
|
|
||||||
.wails-reconnect-overlay-loadingspinner {
|
|
||||||
pointer-events: none;
|
|
||||||
width: 2.5em;
|
|
||||||
height: 2.5em;
|
|
||||||
border: .4em solid transparent;
|
|
||||||
border-color: #f00 #eee0 #f00 #eee0;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: loadingspin 1s linear infinite;
|
|
||||||
margin: auto;
|
|
||||||
padding: 2.5em
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loadingspin {
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
@ -1,15 +0,0 @@
|
|||||||
/* jshint esversion: 8 */
|
|
||||||
const esbuild = require("esbuild");
|
|
||||||
const sveltePlugin = require("esbuild-svelte");
|
|
||||||
|
|
||||||
esbuild
|
|
||||||
.build({
|
|
||||||
entryPoints: ["main.js"],
|
|
||||||
bundle: true,
|
|
||||||
minify: true,
|
|
||||||
outfile: "../ipc_websocket.js",
|
|
||||||
plugins: [sveltePlugin({compileOptions: {css: true}})],
|
|
||||||
logLevel: "info",
|
|
||||||
sourcemap: "inline",
|
|
||||||
})
|
|
||||||
.catch(() => process.exit(1));
|
|
@ -1,8 +0,0 @@
|
|||||||
export function log(message) {
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.log(
|
|
||||||
'%c wails dev %c ' + message + ' ',
|
|
||||||
'background: #aa0000; color: #fff; border-radius: 3px 0px 0px 3px; padding: 1px; font-size: 0.7rem',
|
|
||||||
'background: #009900; color: #fff; border-radius: 0px 3px 3px 0px; padding: 1px; font-size: 0.7rem'
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
_ __ _ __
|
|
||||||
| | / /___ _(_) /____
|
|
||||||
| | /| / / __ `/ / / ___/
|
|
||||||
| |/ |/ / /_/ / / (__ )
|
|
||||||
|__/|__/\__,_/_/_/____/
|
|
||||||
The electron alternative for Go
|
|
||||||
(c) Lea Anthony 2019-present
|
|
||||||
*/
|
|
||||||
/* jshint esversion: 6 */
|
|
||||||
|
|
||||||
import {log} from "./log";
|
|
||||||
import Overlay from "./Overlay.svelte";
|
|
||||||
import {hideOverlay, showOverlay} from "./store";
|
|
||||||
|
|
||||||
let components = {};
|
|
||||||
|
|
||||||
let wailsInvokeInternal = null;
|
|
||||||
let messageQueue = [];
|
|
||||||
|
|
||||||
window.WailsInvoke = (message) => {
|
|
||||||
if (!wailsInvokeInternal) {
|
|
||||||
console.log("Queueing: " + message);
|
|
||||||
messageQueue.push(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wailsInvokeInternal(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
|
||||||
components.overlay = new Overlay({
|
|
||||||
target: document.body,
|
|
||||||
anchor: document.querySelector('#wails-spinner'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let websocket = null;
|
|
||||||
let connectTimer;
|
|
||||||
|
|
||||||
window.onbeforeunload = function () {
|
|
||||||
if (websocket) {
|
|
||||||
websocket.onclose = function () {
|
|
||||||
};
|
|
||||||
websocket.close();
|
|
||||||
websocket = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ...and attempt to connect
|
|
||||||
connect();
|
|
||||||
|
|
||||||
function setupIPCBridge() {
|
|
||||||
wailsInvokeInternal = (message) => {
|
|
||||||
websocket.send(message);
|
|
||||||
};
|
|
||||||
for (let i = 0; i < messageQueue.length; i++) {
|
|
||||||
console.log("sending queued message: " + messageQueue[i]);
|
|
||||||
window.WailsInvoke(messageQueue[i]);
|
|
||||||
}
|
|
||||||
messageQueue = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles incoming websocket connections
|
|
||||||
function handleConnect() {
|
|
||||||
log('Connected to backend');
|
|
||||||
hideOverlay();
|
|
||||||
setupIPCBridge();
|
|
||||||
clearInterval(connectTimer);
|
|
||||||
websocket.onclose = handleDisconnect;
|
|
||||||
websocket.onmessage = handleMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles websocket disconnects
|
|
||||||
function handleDisconnect() {
|
|
||||||
log('Disconnected from backend');
|
|
||||||
websocket = null;
|
|
||||||
showOverlay();
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
function _connect() {
|
|
||||||
if (websocket == null) {
|
|
||||||
websocket = new WebSocket('ws://' + window.location.host + '/wails/ipc');
|
|
||||||
websocket.onopen = handleConnect;
|
|
||||||
websocket.onerror = function (e) {
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
websocket = null;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to connect to the backend every .5s
|
|
||||||
function connect() {
|
|
||||||
_connect();
|
|
||||||
connectTimer = setInterval(_connect, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMessage(message) {
|
|
||||||
|
|
||||||
if (message.data === "reload") {
|
|
||||||
window.runtime.WindowReload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (message.data === "reloadapp") {
|
|
||||||
window.runtime.WindowReloadApp()
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As a bridge we ignore js and css injections
|
|
||||||
switch (message.data[0]) {
|
|
||||||
// Notifications
|
|
||||||
case 'n':
|
|
||||||
window.wails.EventsNotify(message.data.slice(1));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
const callbackData = message.data.slice(1);
|
|
||||||
window.wails.Callback(callbackData);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log('Unknown message: ' + message.data);
|
|
||||||
}
|
|
||||||
}
|
|
1536
v3/internal/runtime/dev/package-lock.json
generated
1536
v3/internal/runtime/dev/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "dev",
|
|
||||||
"version": "3.0.0",
|
|
||||||
"description": "Wails JS Dev",
|
|
||||||
"main": "main.js",
|
|
||||||
"scripts": {
|
|
||||||
"build": "run-p build:*",
|
|
||||||
"build:dev": "node build.js"
|
|
||||||
},
|
|
||||||
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
|
||||||
"license": "ISC",
|
|
||||||
"devDependencies": {
|
|
||||||
"esbuild": "^0.12.17",
|
|
||||||
"esbuild-svelte": "^0.5.6",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"svelte": "^3.49.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import {writable} from 'svelte/store';
|
|
||||||
|
|
||||||
/** Overlay */
|
|
||||||
export const overlayVisible = writable(false);
|
|
||||||
|
|
||||||
export function showOverlay() {
|
|
||||||
overlayVisible.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hideOverlay() {
|
|
||||||
overlayVisible.set(false);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package runtime
|
|
||||||
|
|
||||||
import _ "embed"
|
|
||||||
|
|
||||||
//go:embed ipc_websocket.js
|
|
||||||
var WebsocketIPC []byte
|
|
||||||
|
|
||||||
//go:embed ipc.js
|
|
||||||
var DesktopIPC []byte
|
|
@ -1 +0,0 @@
|
|||||||
(()=>{var o=null;(function(){let s=function(e){for(var n=window[e.shift()];n&&e.length;)n=n[e.shift()];return n},t=s(["chrome","webview","postMessage"]),i=s(["webkit","messageHandlers","external","postMessage"]);if(!t&&!i){console.error("Unsupported Platform");return}t&&(o=e=>window.chrome.webview.postMessage(e)),i&&(o=e=>window.webkit.messageHandlers.external.postMessage(e))})();function r(s,t){o(t&&t!==-1?"WINDOWID:"+t+":"+s:s)}})();
|
|
File diff suppressed because one or more lines are too long
7404
v3/internal/runtime/package-lock.json
generated
7404
v3/internal/runtime/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,10 @@
|
|||||||
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"esbuild": "^0.15.6",
|
"esbuild": "^0.17.5",
|
||||||
"happy-dom": "^8.1.3",
|
"happy-dom": "^8.1.5",
|
||||||
"vitest": "^0.24.3"
|
"nanoid": "^4.0.0",
|
||||||
|
"npm-check-updates": "^16.6.3",
|
||||||
|
"vitest": "^0.28.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
(()=>{var y=Object.defineProperty;var f=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})};var L=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(L);return n.searchParams.append("method",t),e&&n.searchParams.append("args",JSON.stringify(e)),new Promise((i,r)=>{fetch(n).then(o=>{if(o.ok)return o.headers.get("content-type")&&o.headers.get("content-type").indexOf("application/json")!==-1?o.json():o.text();r(Error(o.statusText))}).then(o=>i(o)).catch(o=>r(o))})}function l(t,e){return!e||e===-1?function(n,i){return h(t+"."+n,i)}:function(n,i){return i=i||{},i.windowID=e,h(t+"."+n,i)}}var D="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var g=(t=21)=>{let e="",n=t;for(;n--;)e+=D[Math.random()*64|0];return e};var I=l("dialog"),u=new Map;function Q(){let t;do t=g();while(u.has(t));return t}function S(t,e,n){let i=u.get(t);i&&(n?i.resolve(JSON.parse(e)):i.resolve(e),u.delete(t))}function C(t,e){let n=u.get(t);n&&(n.reject(e),u.delete(t))}function c(t,e){return new Promise((n,i)=>{let r=Q();e=e||{},e["dialog-id"]=r,u.set(r,{resolve:n,reject:i}),I(t,e).catch(o=>{i(o),u.delete(r)})})}function M(t){return c("Info",t)}function O(t){return c("Warning",t)}function b(t){return c("Error",t)}function v(t){return c("Question",t)}function E(t){return c("OpenFile",t)}function R(t){return c("SaveFile",t)}var m={};f(m,{SetText:()=>H,Text:()=>B});var T=l("clipboard");function H(t){return T("SetText",{text:t})}function B(){return T("Text")}var d={};f(d,{Hide:()=>N,Quit:()=>G,Show:()=>J});var p=l("application");function N(){return p("Hide")}function J(){return p("Show")}function G(){return p("Quit")}var x={};f(x,{Log:()=>j});var _=l("log");function j(t){return _("Log",t)}function k(t){let e=l("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,i)=>e("SetSize",{width:n,height:i}),Size:()=>e("Size"),SetMaxSize:(n,i)=>e("SetMaxSize",{width:n,height:i}),SetMinSize:(n,i)=>e("SetMinSize",{width:n,height:i}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,i)=>e("SetPosition",{x:n,y:i}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,i,r,o)=>e("SetBackgroundColour",{r:n,g:i,b:r,a:o})}}var q=l("events"),w=class{constructor(e,n,i){this.eventName=e,this.maxCallbacks=i||-1,this.Callback=r=>(n(r),this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0))}};var a=new Map;function s(t,e,n){let i=a.get(t)||[],r=new w(t,e,n);return i.push(r),a.set(t,i),()=>K(r)}function F(t,e){return s(t,e,-1)}function U(t,e){return s(t,e,1)}function K(t){let e=t.eventName,n=a.get(e).filter(i=>i!==t);n.length===0?a.delete(e):a.set(e,n)}function z(t){console.log("dispatching event: ",{event:t});let e=a.get(t.name);if(e){let n=[];e.forEach(i=>{i.Callback(t)&&n.push(i)}),n.length>0&&(e=e.filter(i=>!n.includes(i)),e.length===0?a.delete(t.name):a.set(t.name,e))}}function A(t,...e){[t,...e].forEach(i=>{a.delete(i)})}function P(){a.clear()}function W(t){return q("Emit",t)}window.wails={...V(-1)};window._wails={dialogCallback:S,dialogErrorCallback:C,dispatchCustomEvent:z};function V(t){return{Clipboard:{...m},Application:{...d},Log:x,Dialog:{Info:M,Warning:O,Error:b,Question:v,OpenFile:E,SaveFile:R},Events:{Emit:W,On:F,Once:U,OnMultiple:s,Off:A,OffAll:P},Window:k(t)}}console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||||
|
@ -1 +1 @@
|
|||||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
(()=>{var y=Object.defineProperty;var f=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})};var L=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(L);return n.searchParams.append("method",t),e&&n.searchParams.append("args",JSON.stringify(e)),new Promise((i,r)=>{fetch(n).then(o=>{if(o.ok)return o.headers.get("content-type")&&o.headers.get("content-type").indexOf("application/json")!==-1?o.json():o.text();r(Error(o.statusText))}).then(o=>i(o)).catch(o=>r(o))})}function l(t,e){return!e||e===-1?function(n,i){return h(t+"."+n,i)}:function(n,i){return i=i||{},i.windowID=e,h(t+"."+n,i)}}var D="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var g=(t=21)=>{let e="",n=t;for(;n--;)e+=D[Math.random()*64|0];return e};var I=l("dialog"),u=new Map;function Q(){let t;do t=g();while(u.has(t));return t}function S(t,e,n){let i=u.get(t);i&&(n?i.resolve(JSON.parse(e)):i.resolve(e),u.delete(t))}function C(t,e){let n=u.get(t);n&&(n.reject(e),u.delete(t))}function c(t,e){return new Promise((n,i)=>{let r=Q();e=e||{},e["dialog-id"]=r,u.set(r,{resolve:n,reject:i}),I(t,e).catch(o=>{i(o),u.delete(r)})})}function M(t){return c("Info",t)}function O(t){return c("Warning",t)}function b(t){return c("Error",t)}function v(t){return c("Question",t)}function E(t){return c("OpenFile",t)}function R(t){return c("SaveFile",t)}var m={};f(m,{SetText:()=>H,Text:()=>B});var T=l("clipboard");function H(t){return T("SetText",{text:t})}function B(){return T("Text")}var d={};f(d,{Hide:()=>N,Quit:()=>G,Show:()=>J});var p=l("application");function N(){return p("Hide")}function J(){return p("Show")}function G(){return p("Quit")}var x={};f(x,{Log:()=>j});var _=l("log");function j(t){return _("Log",t)}function k(t){let e=l("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,i)=>e("SetSize",{width:n,height:i}),Size:()=>e("Size"),SetMaxSize:(n,i)=>e("SetMaxSize",{width:n,height:i}),SetMinSize:(n,i)=>e("SetMinSize",{width:n,height:i}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,i)=>e("SetPosition",{x:n,y:i}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,i,r,o)=>e("SetBackgroundColour",{r:n,g:i,b:r,a:o})}}var q=l("events"),w=class{constructor(e,n,i){this.eventName=e,this.maxCallbacks=i||-1,this.Callback=r=>(n(r),this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0))}};var a=new Map;function s(t,e,n){let i=a.get(t)||[],r=new w(t,e,n);return i.push(r),a.set(t,i),()=>K(r)}function F(t,e){return s(t,e,-1)}function U(t,e){return s(t,e,1)}function K(t){let e=t.eventName,n=a.get(e).filter(i=>i!==t);n.length===0?a.delete(e):a.set(e,n)}function z(t){console.log("dispatching event: ",{event:t});let e=a.get(t.name);if(e){let n=[];e.forEach(i=>{i.Callback(t)&&n.push(i)}),n.length>0&&(e=e.filter(i=>!n.includes(i)),e.length===0?a.delete(t.name):a.set(t.name,e))}}function A(t,...e){[t,...e].forEach(i=>{a.delete(i)})}function P(){a.clear()}function W(t){return q("Emit",t)}window.wails={...V(-1)};window._wails={dialogCallback:S,dialogErrorCallback:C,dispatchCustomEvent:z};function V(t){return{Clipboard:{...m},Application:{...d},Log:x,Dialog:{Info:M,Warning:O,Error:b,Question:v,OpenFile:E,SaveFile:R},Events:{Emit:W,On:F,Once:U,OnMultiple:s,Off:A,OffAll:P},Window:k(t)}}console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||||
|
@ -1 +1 @@
|
|||||||
(()=>{var u=null;(function(){let e=function(n){for(var r=window[n.shift()];r&&n.length;)r=r[n.shift()];return r},t=e(["chrome","webview","postMessage"]),o=e(["webkit","messageHandlers","external","postMessage"]);if(!t&&!o){console.error("Unsupported Platform");return}t&&(u=n=>window.chrome.webview.postMessage(n)),o&&(u=n=>window.webkit.messageHandlers.external.postMessage(n))})();function i(e,t){u(t&&t!==-1?"WINDOWID:"+t+":"+e:e)}var c={};function y(){var e=new Uint32Array(1);return window.crypto.getRandomValues(e)[0]}function W(){return Math.random()*9007199254740991}var d;window.crypto?d=y:d=W;function f(e,t,o){o==null&&(o=0);let n=window.wails.window.ID();return new Promise(function(r,l){var s;do s=e+"-"+d();while(c[s]);var w;o>0&&(w=setTimeout(function(){l(Error("Call to "+e+" timed out. Request ID: "+s))},o)),c[s]={timeoutHandle:w,reject:l,resolve:r};try{let m={name:e,args:t,callbackID:s,windowID:n};window.WailsInvoke("C"+JSON.stringify(m))}catch(m){console.error(m)}})}window.ObfuscatedCall=(e,t,o)=>(o==null&&(o=0),new Promise(function(n,r){var l;do l=e+"-"+d();while(c[l]);var s;o>0&&(s=setTimeout(function(){r(Error("Call to method "+e+" timed out. Request ID: "+l))},o)),c[l]={timeoutHandle:s,reject:r,resolve:n};try{let w={id:e,args:t,callbackID:l,windowID:window.wails.window.ID()};window.WailsInvoke("c"+JSON.stringify(w))}catch(w){console.error(w)}}));function p(e){let t;try{t=JSON.parse(e)}catch(r){let l=`Invalid JSON passed to callback: ${r.message}. Message: ${e}`;throw runtime.LogDebug(l),new Error(l)}let o=t.callbackid,n=c[o];if(!n){let r=`Callback '${o}' not registered!!!`;throw console.error(r),new Error(r)}clearTimeout(n.timeoutHandle),delete c[o],t.error?n.reject(t.error):n.resolve(t.result)}var a={};function b(e){let t=e.name;if(a[t]){let o=a[t].slice();for(let n=0;n<a[t].length;n+=1){let r=a[t][n],l=e.data;r.Callback(l)&&o.splice(n,1)}o.length===0?S(t):a[t]=o}}function h(e){let t;try{t=JSON.parse(e)}catch{let n="Invalid JSON passed to Notify: "+e;throw new Error(n)}b(t)}function S(e){delete a[e],window.WailsInvoke("EX"+e)}window.go={};function v(e){try{e=JSON.parse(e)}catch(t){console.error(t)}window.go=window.go||{},Object.keys(e).forEach(t=>{window.go[t]=window.go[t]||{},Object.keys(e[t]).forEach(o=>{window.go[t][o]=window.go[t][o]||{},Object.keys(e[t][o]).forEach(n=>{window.go[t][o][n]=function(){let r=0;function l(){let s=[].slice.call(arguments);return f([t,o,n].join("."),s,r)}return l.setTimeout=function(s){r=s},l.getTimeout=function(){return r},l}()})})})}function g(e){return{Center:()=>i("Wc",e),SetTitle:t=>i("WT"+t,e),Fullscreen:()=>i("WF",e),UnFullscreen:()=>i("Wf",e),SetSize:(t,o)=>i("WS"+t+","+o,e),GetSize:()=>f(":wails:WindowGetSize"),SetMaxSize:(t,o)=>i("WZ:"+t+":"+o,e),SetMinSize:(t,o)=>i("Wz:"+t+":"+o,e),SetAlwaysOnTop:t=>i("WATP:"+(t?"1":"0"),e),SetPosition:(t,o)=>i("Wp:"+t+":"+o,e),GetPosition:()=>f(":wails:WindowGetPos"),Hide:()=>i("WH",e),Maximise:()=>i("WM",e),Show:()=>i("WS",e),ToggleMaximise:()=>i("Wt",e),UnMaximise:()=>i("WU",e),Minimise:()=>i("Wm",e),UnMinimise:()=>i("Wu",e),SetBackgroundColour:(t,o,n,r)=>i("Wr:"+JSON.stringify({r:t||0,g:o||0,b:n||0,a:r||255},e))}}window.wails={Callback:p,callbacks:c,EventsNotify:h,eventListeners:a,SetBindings:v};function x(e){return{Window:g(e),Show:()=>i("S"),Hide:()=>i("H"),Quit:()=>i("Q")}}window.runtime=x(-1);console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
(()=>{var y=Object.defineProperty;var f=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})};var L=window.location.origin+"/wails/runtime";function h(t,e){let n=new URL(L);return n.searchParams.append("method",t),e&&n.searchParams.append("args",JSON.stringify(e)),new Promise((i,r)=>{fetch(n).then(o=>{if(o.ok)return o.headers.get("content-type")&&o.headers.get("content-type").indexOf("application/json")!==-1?o.json():o.text();r(Error(o.statusText))}).then(o=>i(o)).catch(o=>r(o))})}function l(t,e){return!e||e===-1?function(n,i){return h(t+"."+n,i)}:function(n,i){return i=i||{},i.windowID=e,h(t+"."+n,i)}}var D="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";var g=(t=21)=>{let e="",n=t;for(;n--;)e+=D[Math.random()*64|0];return e};var I=l("dialog"),u=new Map;function Q(){let t;do t=g();while(u.has(t));return t}function S(t,e,n){let i=u.get(t);i&&(n?i.resolve(JSON.parse(e)):i.resolve(e),u.delete(t))}function C(t,e){let n=u.get(t);n&&(n.reject(e),u.delete(t))}function c(t,e){return new Promise((n,i)=>{let r=Q();e=e||{},e["dialog-id"]=r,u.set(r,{resolve:n,reject:i}),I(t,e).catch(o=>{i(o),u.delete(r)})})}function M(t){return c("Info",t)}function O(t){return c("Warning",t)}function b(t){return c("Error",t)}function v(t){return c("Question",t)}function E(t){return c("OpenFile",t)}function R(t){return c("SaveFile",t)}var m={};f(m,{SetText:()=>H,Text:()=>B});var T=l("clipboard");function H(t){return T("SetText",{text:t})}function B(){return T("Text")}var d={};f(d,{Hide:()=>N,Quit:()=>G,Show:()=>J});var p=l("application");function N(){return p("Hide")}function J(){return p("Show")}function G(){return p("Quit")}var x={};f(x,{Log:()=>j});var _=l("log");function j(t){return _("Log",t)}function k(t){let e=l("window",t);return{Center:()=>e("Center"),SetTitle:n=>e("SetTitle",{title:n}),Fullscreen:()=>e("Fullscreen"),UnFullscreen:()=>e("UnFullscreen"),SetSize:(n,i)=>e("SetSize",{width:n,height:i}),Size:()=>e("Size"),SetMaxSize:(n,i)=>e("SetMaxSize",{width:n,height:i}),SetMinSize:(n,i)=>e("SetMinSize",{width:n,height:i}),SetAlwaysOnTop:n=>e("SetAlwaysOnTop",{alwaysOnTop:n}),SetPosition:(n,i)=>e("SetPosition",{x:n,y:i}),Position:()=>e("Position"),Screen:()=>e("Screen"),Hide:()=>e("Hide"),Maximise:()=>e("Maximise"),Show:()=>e("Show"),ToggleMaximise:()=>e("ToggleMaximise"),UnMaximise:()=>e("UnMaximise"),Minimise:()=>e("Minimise"),UnMinimise:()=>e("UnMinimise"),SetBackgroundColour:(n,i,r,o)=>e("SetBackgroundColour",{r:n,g:i,b:r,a:o})}}var q=l("events"),w=class{constructor(e,n,i){this.eventName=e,this.maxCallbacks=i||-1,this.Callback=r=>(n(r),this.maxCallbacks===-1?!1:(this.maxCallbacks-=1,this.maxCallbacks===0))}};var a=new Map;function s(t,e,n){let i=a.get(t)||[],r=new w(t,e,n);return i.push(r),a.set(t,i),()=>K(r)}function F(t,e){return s(t,e,-1)}function U(t,e){return s(t,e,1)}function K(t){let e=t.eventName,n=a.get(e).filter(i=>i!==t);n.length===0?a.delete(e):a.set(e,n)}function z(t){console.log("dispatching event: ",{event:t});let e=a.get(t.name);if(e){let n=[];e.forEach(i=>{i.Callback(t)&&n.push(i)}),n.length>0&&(e=e.filter(i=>!n.includes(i)),e.length===0?a.delete(t.name):a.set(t.name,e))}}function A(t,...e){[t,...e].forEach(i=>{a.delete(i)})}function P(){a.clear()}function W(t){return q("Emit",t)}window.wails={...V(-1)};window._wails={dialogCallback:S,dialogErrorCallback:C,dispatchCustomEvent:z};function V(t){return{Clipboard:{...m},Application:{...d},Log:x,Dialog:{Info:M,Warning:O,Error:b,Question:v,OpenFile:E,SaveFile:R},Events:{Emit:W,On:F,Once:U,OnMultiple:s,Off:A,OffAll:P},Window:k(t)}}console.log("Wails v3.0.0 Debug Mode Enabled");})();
|
||||||
|
@ -3,9 +3,12 @@ package application
|
|||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/logger"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/events"
|
"github.com/wailsapp/wails/v3/pkg/events"
|
||||||
"github.com/wailsapp/wails/v3/pkg/options"
|
"github.com/wailsapp/wails/v3/pkg/options"
|
||||||
|
|
||||||
@ -29,7 +32,14 @@ func New(appOptions options.Application) *App {
|
|||||||
options: appOptions,
|
options: appOptions,
|
||||||
applicationEventListeners: make(map[uint][]func()),
|
applicationEventListeners: make(map[uint][]func()),
|
||||||
systemTrays: make(map[uint]*SystemTray),
|
systemTrays: make(map[uint]*SystemTray),
|
||||||
|
log: logger.New(appOptions.Logger.CustomLoggers...),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !appOptions.Logger.Silent {
|
||||||
|
result.log.AddOutput(&logger.Console{})
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Events = NewCustomEventProcessor(result.dispatchEventToWindows)
|
||||||
globalApplication = result
|
globalApplication = result
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -57,6 +67,8 @@ type platformApp interface {
|
|||||||
setIcon(icon []byte)
|
setIcon(icon []byte)
|
||||||
on(id uint)
|
on(id uint)
|
||||||
dispatchOnMainThread(id uint)
|
dispatchOnMainThread(id uint)
|
||||||
|
hide()
|
||||||
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages sent from javascript get routed here
|
// Messages sent from javascript get routed here
|
||||||
@ -80,10 +92,8 @@ type App struct {
|
|||||||
applicationEventListenersLock sync.RWMutex
|
applicationEventListenersLock sync.RWMutex
|
||||||
|
|
||||||
// Windows
|
// Windows
|
||||||
windows map[uint]*WebviewWindow
|
windows map[uint]*WebviewWindow
|
||||||
windowsLock sync.Mutex
|
windowsLock sync.Mutex
|
||||||
windowAliases map[string]uint
|
|
||||||
windowAliasesLock sync.Mutex
|
|
||||||
|
|
||||||
// System Trays
|
// System Trays
|
||||||
systemTrays map[uint]*SystemTray
|
systemTrays map[uint]*SystemTray
|
||||||
@ -104,8 +114,9 @@ type App struct {
|
|||||||
// The main application menu
|
// The main application menu
|
||||||
ApplicationMenu *Menu
|
ApplicationMenu *Menu
|
||||||
|
|
||||||
// About MessageDialog
|
|
||||||
clipboard *Clipboard
|
clipboard *Clipboard
|
||||||
|
Events *EventProcessor
|
||||||
|
log *logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) getSystemTrayID() uint {
|
func (a *App) getSystemTrayID() uint {
|
||||||
@ -114,19 +125,60 @@ func (a *App) getSystemTrayID() uint {
|
|||||||
a.systemTrayID++
|
a.systemTrayID++
|
||||||
return a.systemTrayID
|
return a.systemTrayID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) getWindowForID(id uint) *WebviewWindow {
|
||||||
|
a.windowsLock.Lock()
|
||||||
|
defer a.windowsLock.Unlock()
|
||||||
|
return a.windows[id]
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) On(eventType events.ApplicationEventType, callback func()) {
|
func (a *App) On(eventType events.ApplicationEventType, callback func()) {
|
||||||
eventID := uint(eventType)
|
eventID := uint(eventType)
|
||||||
a.applicationEventListenersLock.Lock()
|
a.applicationEventListenersLock.Lock()
|
||||||
defer a.applicationEventListenersLock.Unlock()
|
defer a.applicationEventListenersLock.Unlock()
|
||||||
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], callback)
|
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], callback)
|
||||||
if a.impl != nil {
|
if a.impl != nil {
|
||||||
a.impl.on(eventID)
|
go a.impl.on(eventID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (a *App) NewWebviewWindow() *WebviewWindow {
|
func (a *App) NewWebviewWindow() *WebviewWindow {
|
||||||
return a.NewWebviewWindowWithOptions(nil)
|
return a.NewWebviewWindowWithOptions(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) info(message string, args ...any) {
|
||||||
|
a.Log(&logger.Message{
|
||||||
|
Level: "INFO",
|
||||||
|
Message: message,
|
||||||
|
Data: args,
|
||||||
|
Sender: "Wails",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) fatal(message string, args ...any) {
|
||||||
|
msg := "************** FATAL **************\n"
|
||||||
|
msg += message
|
||||||
|
msg += "***********************************\n"
|
||||||
|
|
||||||
|
a.Log(&logger.Message{
|
||||||
|
Level: "FATAL",
|
||||||
|
Message: msg,
|
||||||
|
Data: args,
|
||||||
|
Sender: "Wails",
|
||||||
|
})
|
||||||
|
|
||||||
|
a.log.Flush()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) error(message string, args ...any) {
|
||||||
|
a.Log(&logger.Message{
|
||||||
|
Level: "ERROR",
|
||||||
|
Message: message,
|
||||||
|
Data: args,
|
||||||
|
Sender: "Wails",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) NewWebviewWindowWithOptions(windowOptions *options.WebviewWindow) *WebviewWindow {
|
func (a *App) NewWebviewWindowWithOptions(windowOptions *options.WebviewWindow) *WebviewWindow {
|
||||||
// Ensure we have sane defaults
|
// Ensure we have sane defaults
|
||||||
if windowOptions == nil {
|
if windowOptions == nil {
|
||||||
@ -142,14 +194,6 @@ func (a *App) NewWebviewWindowWithOptions(windowOptions *options.WebviewWindow)
|
|||||||
a.windows[id] = newWindow
|
a.windows[id] = newWindow
|
||||||
a.windowsLock.Unlock()
|
a.windowsLock.Unlock()
|
||||||
|
|
||||||
if windowOptions.Alias != "" {
|
|
||||||
if a.windowAliases == nil {
|
|
||||||
a.windowAliases = make(map[string]uint)
|
|
||||||
}
|
|
||||||
a.windowAliasesLock.Lock()
|
|
||||||
a.windowAliases[windowOptions.Alias] = id
|
|
||||||
a.windowAliasesLock.Unlock()
|
|
||||||
}
|
|
||||||
if a.running {
|
if a.running {
|
||||||
newWindow.run()
|
newWindow.run()
|
||||||
}
|
}
|
||||||
@ -158,7 +202,6 @@ func (a *App) NewWebviewWindowWithOptions(windowOptions *options.WebviewWindow)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) NewSystemTray() *SystemTray {
|
func (a *App) NewSystemTray() *SystemTray {
|
||||||
|
|
||||||
id := a.getSystemTrayID()
|
id := a.getSystemTrayID()
|
||||||
newSystemTray := NewSystemTray(id)
|
newSystemTray := NewSystemTray(id)
|
||||||
a.systemTraysLock.Lock()
|
a.systemTraysLock.Lock()
|
||||||
@ -172,6 +215,7 @@ func (a *App) NewSystemTray() *SystemTray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
|
a.info("Starting application")
|
||||||
a.impl = newPlatformApp(a)
|
a.impl = newPlatformApp(a)
|
||||||
|
|
||||||
a.running = true
|
a.running = true
|
||||||
@ -191,7 +235,10 @@ func (a *App) Run() error {
|
|||||||
for {
|
for {
|
||||||
event := <-webviewRequests
|
event := <-webviewRequests
|
||||||
a.handleWebViewRequest(event)
|
a.handleWebViewRequest(event)
|
||||||
event.request.Release()
|
err := event.request.Release()
|
||||||
|
if err != nil {
|
||||||
|
a.error("Failed to release webview request: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
go func() {
|
go func() {
|
||||||
@ -221,7 +268,7 @@ func (a *App) Run() error {
|
|||||||
// set the application menu
|
// set the application menu
|
||||||
a.impl.setApplicationMenu(a.ApplicationMenu)
|
a.impl.setApplicationMenu(a.ApplicationMenu)
|
||||||
|
|
||||||
// set the application icon
|
// set the application Icon
|
||||||
a.impl.setIcon(a.options.Icon)
|
a.impl.setIcon(a.options.Icon)
|
||||||
|
|
||||||
return a.impl.run()
|
return a.impl.run()
|
||||||
@ -382,3 +429,37 @@ func (a *App) dispatchOnMainThread(fn func()) {
|
|||||||
// Call platform specific dispatch function
|
// Call platform specific dispatch function
|
||||||
a.impl.dispatchOnMainThread(id)
|
a.impl.dispatchOnMainThread(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) OpenFileDialogWithOptions(options *OpenFileDialogOptions) *OpenFileDialog {
|
||||||
|
result := a.OpenFileDialog()
|
||||||
|
result.SetOptions(options)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SaveFileDialogWithOptions(s *SaveFileDialogOptions) *SaveFileDialog {
|
||||||
|
result := a.SaveFileDialog()
|
||||||
|
result.SetOptions(s)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) dispatchEventToWindows(event *CustomEvent) {
|
||||||
|
for _, window := range a.windows {
|
||||||
|
window.dispatchCustomEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Hide() {
|
||||||
|
if a.impl != nil {
|
||||||
|
a.impl.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Show() {
|
||||||
|
if a.impl != nil {
|
||||||
|
a.impl.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Log(message *logger.Message) {
|
||||||
|
a.log.Log(message)
|
||||||
|
}
|
||||||
|
@ -113,6 +113,16 @@ static void setApplicationIcon(void *icon, int length) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide the application
|
||||||
|
static void hide(void) {
|
||||||
|
[NSApp hide:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the application
|
||||||
|
static void show(void) {
|
||||||
|
[NSApp unhide:nil];
|
||||||
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
@ -127,6 +137,14 @@ type macosApp struct {
|
|||||||
parent *App
|
parent *App
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *macosApp) hide() {
|
||||||
|
C.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *macosApp) show() {
|
||||||
|
C.show()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *macosApp) on(eventID uint) {
|
func (m *macosApp) on(eventID uint) {
|
||||||
C.registerListener(C.uint(eventID))
|
C.registerListener(C.uint(eventID))
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Button struct {
|
type Button struct {
|
||||||
label string
|
Label string
|
||||||
isCancel bool
|
IsCancel bool
|
||||||
isDefault bool
|
IsDefault bool
|
||||||
callback func()
|
callback func()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,12 +60,16 @@ type messageDialogImpl interface {
|
|||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessageDialogOptions struct {
|
||||||
|
DialogType DialogType
|
||||||
|
Title string
|
||||||
|
Message string
|
||||||
|
Buttons []*Button
|
||||||
|
Icon []byte
|
||||||
|
}
|
||||||
|
|
||||||
type MessageDialog struct {
|
type MessageDialog struct {
|
||||||
dialogType DialogType
|
MessageDialogOptions
|
||||||
title string
|
|
||||||
message string
|
|
||||||
buttons []*Button
|
|
||||||
icon []byte
|
|
||||||
|
|
||||||
// platform independent
|
// platform independent
|
||||||
impl messageDialogImpl
|
impl messageDialogImpl
|
||||||
@ -80,13 +84,16 @@ var defaultTitles = map[DialogType]string{
|
|||||||
|
|
||||||
func newMessageDialog(dialogType DialogType) *MessageDialog {
|
func newMessageDialog(dialogType DialogType) *MessageDialog {
|
||||||
return &MessageDialog{
|
return &MessageDialog{
|
||||||
dialogType: dialogType,
|
MessageDialogOptions: MessageDialogOptions{
|
||||||
title: defaultTitles[dialogType],
|
DialogType: dialogType,
|
||||||
|
Title: defaultTitles[dialogType],
|
||||||
|
},
|
||||||
|
impl: nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
|
func (d *MessageDialog) SetTitle(title string) *MessageDialog {
|
||||||
d.title = title
|
d.Title = title
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,36 +105,41 @@ func (d *MessageDialog) Show() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
|
func (d *MessageDialog) SetIcon(icon []byte) *MessageDialog {
|
||||||
d.icon = icon
|
d.Icon = icon
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) AddButton(s string) *Button {
|
func (d *MessageDialog) AddButton(s string) *Button {
|
||||||
result := &Button{
|
result := &Button{
|
||||||
label: s,
|
Label: s,
|
||||||
}
|
}
|
||||||
d.buttons = append(d.buttons, result)
|
d.Buttons = append(d.Buttons, result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *MessageDialog) AddButtons(buttons []*Button) *MessageDialog {
|
||||||
|
d.Buttons = buttons
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
|
func (d *MessageDialog) SetDefaultButton(button *Button) *MessageDialog {
|
||||||
for _, b := range d.buttons {
|
for _, b := range d.Buttons {
|
||||||
b.isDefault = false
|
b.IsDefault = false
|
||||||
}
|
}
|
||||||
button.isDefault = true
|
button.IsDefault = true
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
|
func (d *MessageDialog) SetCancelButton(button *Button) *MessageDialog {
|
||||||
for _, b := range d.buttons {
|
for _, b := range d.Buttons {
|
||||||
b.isCancel = false
|
b.IsCancel = false
|
||||||
}
|
}
|
||||||
button.isCancel = true
|
button.IsCancel = true
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MessageDialog) SetMessage(title string) *MessageDialog {
|
func (d *MessageDialog) SetMessage(message string) *MessageDialog {
|
||||||
d.title = title
|
d.Message = message
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,9 +147,28 @@ type openFileDialogImpl interface {
|
|||||||
show() ([]string, error)
|
show() ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileFilter struct {
|
type FileFilter struct {
|
||||||
displayName string // Filter information EG: "Image Files (*.jpg, *.png)"
|
DisplayName string // Filter information EG: "Image Files (*.jpg, *.png)"
|
||||||
pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png"
|
Pattern string // semicolon separated list of extensions, EG: "*.jpg;*.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenFileDialogOptions struct {
|
||||||
|
CanChooseDirectories bool
|
||||||
|
CanChooseFiles bool
|
||||||
|
CanCreateDirectories bool
|
||||||
|
ShowHiddenFiles bool
|
||||||
|
ResolvesAliases bool
|
||||||
|
AllowsMultipleSelection bool
|
||||||
|
HideExtension bool
|
||||||
|
CanSelectHiddenExtension bool
|
||||||
|
TreatsFilePackagesAsDirectories bool
|
||||||
|
AllowsOtherFileTypes bool
|
||||||
|
Filters []FileFilter
|
||||||
|
|
||||||
|
Title string
|
||||||
|
Message string
|
||||||
|
ButtonText string
|
||||||
|
Directory string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OpenFileDialog struct {
|
type OpenFileDialog struct {
|
||||||
@ -152,7 +183,7 @@ type OpenFileDialog struct {
|
|||||||
canSelectHiddenExtension bool
|
canSelectHiddenExtension bool
|
||||||
treatsFilePackagesAsDirectories bool
|
treatsFilePackagesAsDirectories bool
|
||||||
allowsOtherFileTypes bool
|
allowsOtherFileTypes bool
|
||||||
filters []fileFilter
|
filters []FileFilter
|
||||||
|
|
||||||
title string
|
title string
|
||||||
message string
|
message string
|
||||||
@ -230,9 +261,9 @@ func (d *OpenFileDialog) PromptForSingleSelection() (string, error) {
|
|||||||
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
|
// AddFilter adds a filter to the dialog. The filter is a display name and a semicolon separated list of extensions.
|
||||||
// EG: AddFilter("Image Files", "*.jpg;*.png")
|
// EG: AddFilter("Image Files", "*.jpg;*.png")
|
||||||
func (d *OpenFileDialog) AddFilter(displayName, pattern string) *OpenFileDialog {
|
func (d *OpenFileDialog) AddFilter(displayName, pattern string) *OpenFileDialog {
|
||||||
d.filters = append(d.filters, fileFilter{
|
d.filters = append(d.filters, FileFilter{
|
||||||
displayName: strings.TrimSpace(displayName),
|
DisplayName: strings.TrimSpace(displayName),
|
||||||
pattern: strings.TrimSpace(pattern),
|
Pattern: strings.TrimSpace(pattern),
|
||||||
})
|
})
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
@ -265,6 +296,24 @@ func (d *OpenFileDialog) CanSelectHiddenExtension(canSelectHiddenExtension bool)
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *OpenFileDialog) SetOptions(options *OpenFileDialogOptions) {
|
||||||
|
d.title = options.Title
|
||||||
|
d.message = options.Message
|
||||||
|
d.buttonText = options.ButtonText
|
||||||
|
d.directory = options.Directory
|
||||||
|
d.canChooseDirectories = options.CanChooseDirectories
|
||||||
|
d.canChooseFiles = options.CanChooseFiles
|
||||||
|
d.canCreateDirectories = options.CanCreateDirectories
|
||||||
|
d.showHiddenFiles = options.ShowHiddenFiles
|
||||||
|
d.resolvesAliases = options.ResolvesAliases
|
||||||
|
d.allowsMultipleSelection = options.AllowsMultipleSelection
|
||||||
|
d.hideExtension = options.HideExtension
|
||||||
|
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
|
||||||
|
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
|
||||||
|
d.allowsOtherFileTypes = options.AllowsOtherFileTypes
|
||||||
|
d.filters = options.Filters
|
||||||
|
}
|
||||||
|
|
||||||
func newOpenFileDialog() *OpenFileDialog {
|
func newOpenFileDialog() *OpenFileDialog {
|
||||||
return &OpenFileDialog{
|
return &OpenFileDialog{
|
||||||
id: getDialogID(),
|
id: getDialogID(),
|
||||||
@ -282,6 +331,19 @@ func newSaveFileDialog() *SaveFileDialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SaveFileDialogOptions struct {
|
||||||
|
CanCreateDirectories bool
|
||||||
|
ShowHiddenFiles bool
|
||||||
|
CanSelectHiddenExtension bool
|
||||||
|
AllowOtherFileTypes bool
|
||||||
|
HideExtension bool
|
||||||
|
TreatsFilePackagesAsDirectories bool
|
||||||
|
Message string
|
||||||
|
Directory string
|
||||||
|
Filename string
|
||||||
|
ButtonText string
|
||||||
|
}
|
||||||
|
|
||||||
type SaveFileDialog struct {
|
type SaveFileDialog struct {
|
||||||
id uint
|
id uint
|
||||||
canCreateDirectories bool
|
canCreateDirectories bool
|
||||||
@ -304,6 +366,19 @@ type saveFileDialogImpl interface {
|
|||||||
show() (string, error)
|
show() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *SaveFileDialog) SetOptions(options *SaveFileDialogOptions) {
|
||||||
|
d.canCreateDirectories = options.CanCreateDirectories
|
||||||
|
d.showHiddenFiles = options.ShowHiddenFiles
|
||||||
|
d.canSelectHiddenExtension = options.CanSelectHiddenExtension
|
||||||
|
d.allowOtherFileTypes = options.AllowOtherFileTypes
|
||||||
|
d.hideExtension = options.HideExtension
|
||||||
|
d.treatsFilePackagesAsDirectories = options.TreatsFilePackagesAsDirectories
|
||||||
|
d.message = options.Message
|
||||||
|
d.directory = options.Directory
|
||||||
|
d.filename = options.Filename
|
||||||
|
d.buttonText = options.ButtonText
|
||||||
|
}
|
||||||
|
|
||||||
func (d *SaveFileDialog) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialog {
|
func (d *SaveFileDialog) CanCreateDirectories(canCreateDirectories bool) *SaveFileDialog {
|
||||||
d.canCreateDirectories = canCreateDirectories
|
d.canCreateDirectories = canCreateDirectories
|
||||||
return d
|
return d
|
||||||
|
@ -61,7 +61,7 @@ static void* createAlert(int alertType, char* title, char *message, void *icon,
|
|||||||
NSImage *image = [NSImage imageNamed:NSImageNameInfo];
|
NSImage *image = [NSImage imageNamed:NSImageNameInfo];
|
||||||
[alert setIcon:image];
|
[alert setIcon:image];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return alert;
|
return alert;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -315,54 +315,54 @@ type macosDialog struct {
|
|||||||
func (m *macosDialog) show() {
|
func (m *macosDialog) show() {
|
||||||
globalApplication.dispatchOnMainThread(func() {
|
globalApplication.dispatchOnMainThread(func() {
|
||||||
|
|
||||||
// Mac can only have 4 buttons on a dialog
|
// Mac can only have 4 Buttons on a dialog
|
||||||
if len(m.dialog.buttons) > 4 {
|
if len(m.dialog.Buttons) > 4 {
|
||||||
m.dialog.buttons = m.dialog.buttons[:4]
|
m.dialog.Buttons = m.dialog.Buttons[:4]
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.nsDialog != nil {
|
if m.nsDialog != nil {
|
||||||
C.releaseDialog(m.nsDialog)
|
C.releaseDialog(m.nsDialog)
|
||||||
}
|
}
|
||||||
var title *C.char
|
var title *C.char
|
||||||
if m.dialog.title != "" {
|
if m.dialog.Title != "" {
|
||||||
title = C.CString(m.dialog.title)
|
title = C.CString(m.dialog.Title)
|
||||||
}
|
}
|
||||||
var message *C.char
|
var message *C.char
|
||||||
if m.dialog.message != "" {
|
if m.dialog.Message != "" {
|
||||||
message = C.CString(m.dialog.message)
|
message = C.CString(m.dialog.Message)
|
||||||
}
|
}
|
||||||
var iconData unsafe.Pointer
|
var iconData unsafe.Pointer
|
||||||
var iconLength C.int
|
var iconLength C.int
|
||||||
if m.dialog.icon != nil {
|
if m.dialog.Icon != nil {
|
||||||
iconData = unsafe.Pointer(&m.dialog.icon[0])
|
iconData = unsafe.Pointer(&m.dialog.Icon[0])
|
||||||
iconLength = C.int(len(m.dialog.icon))
|
iconLength = C.int(len(m.dialog.Icon))
|
||||||
} else {
|
} else {
|
||||||
// if it's an error, use the application icon
|
// if it's an error, use the application Icon
|
||||||
if m.dialog.dialogType == ErrorDialog {
|
if m.dialog.DialogType == ErrorDialog {
|
||||||
iconData = unsafe.Pointer(&globalApplication.options.Icon[0])
|
iconData = unsafe.Pointer(&globalApplication.options.Icon[0])
|
||||||
iconLength = C.int(len(globalApplication.options.Icon))
|
iconLength = C.int(len(globalApplication.options.Icon))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
alertType, ok := alertTypeMap[m.dialog.dialogType]
|
alertType, ok := alertTypeMap[m.dialog.DialogType]
|
||||||
if !ok {
|
if !ok {
|
||||||
alertType = C.NSAlertStyleInformational
|
alertType = C.NSAlertStyleInformational
|
||||||
}
|
}
|
||||||
|
|
||||||
m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength)
|
m.nsDialog = C.createAlert(alertType, title, message, iconData, iconLength)
|
||||||
|
|
||||||
// Reverse the buttons so that the default is on the right
|
// Reverse the Buttons so that the default is on the right
|
||||||
reversedButtons := make([]*Button, len(m.dialog.buttons))
|
reversedButtons := make([]*Button, len(m.dialog.Buttons))
|
||||||
var count = 0
|
var count = 0
|
||||||
for i := len(m.dialog.buttons) - 1; i >= 0; i-- {
|
for i := len(m.dialog.Buttons) - 1; i >= 0; i-- {
|
||||||
button := m.dialog.buttons[i]
|
button := m.dialog.Buttons[i]
|
||||||
C.alertAddButton(m.nsDialog, C.CString(button.label), C.bool(button.isDefault), C.bool(button.isCancel))
|
C.alertAddButton(m.nsDialog, C.CString(button.Label), C.bool(button.IsDefault), C.bool(button.IsCancel))
|
||||||
reversedButtons[count] = m.dialog.buttons[i]
|
reversedButtons[count] = m.dialog.Buttons[i]
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonPressed := int(C.dialogRunModal(m.nsDialog))
|
buttonPressed := int(C.dialogRunModal(m.nsDialog))
|
||||||
if len(m.dialog.buttons) > buttonPressed {
|
if len(m.dialog.Buttons) > buttonPressed {
|
||||||
button := reversedButtons[buttonPressed]
|
button := reversedButtons[buttonPressed]
|
||||||
if button.callback != nil {
|
if button.callback != nil {
|
||||||
button.callback()
|
button.callback()
|
||||||
@ -410,7 +410,7 @@ func (m *macosOpenFileDialog) show() ([]string, error) {
|
|||||||
if len(m.dialog.filters) > 0 {
|
if len(m.dialog.filters) > 0 {
|
||||||
var allPatterns []string
|
var allPatterns []string
|
||||||
for _, filter := range m.dialog.filters {
|
for _, filter := range m.dialog.filters {
|
||||||
patternComponents := strings.Split(filter.pattern, ";")
|
patternComponents := strings.Split(filter.Pattern, ";")
|
||||||
for i, component := range patternComponents {
|
for i, component := range patternComponents {
|
||||||
filterPattern := strings.TrimSpace(component)
|
filterPattern := strings.TrimSpace(component)
|
||||||
filterPattern = strings.TrimPrefix(filterPattern, "*.")
|
filterPattern = strings.TrimPrefix(filterPattern, "*.")
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
var applicationEvents = make(chan uint)
|
var applicationEvents = make(chan uint)
|
||||||
|
|
||||||
type WindowEvent struct {
|
type WindowEvent struct {
|
||||||
@ -10,3 +17,144 @@ type WindowEvent struct {
|
|||||||
var windowEvents = make(chan *WindowEvent)
|
var windowEvents = make(chan *WindowEvent)
|
||||||
|
|
||||||
var menuItemClicked = make(chan uint)
|
var menuItemClicked = make(chan uint)
|
||||||
|
|
||||||
|
type CustomEvent struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Data any `json:"data"`
|
||||||
|
Sender string `json:"sender"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e CustomEvent) ToJSON() string {
|
||||||
|
marshal, err := json.Marshal(&e)
|
||||||
|
if err != nil {
|
||||||
|
// TODO: Fatal error? log?
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(marshal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// eventListener holds a callback function which is invoked when
|
||||||
|
// the event listened for is emitted. It has a counter which indicates
|
||||||
|
// how the total number of events it is interested in. A value of zero
|
||||||
|
// means it does not expire (default).
|
||||||
|
type eventListener struct {
|
||||||
|
callback func(*CustomEvent) // Function to call with emitted event data
|
||||||
|
counter int // The number of times this callback may be called. -1 = infinite
|
||||||
|
delete bool // Flag to indicate that this listener should be deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventProcessor handles custom events
|
||||||
|
type EventProcessor struct {
|
||||||
|
// Go event listeners
|
||||||
|
listeners map[string][]*eventListener
|
||||||
|
notifyLock sync.RWMutex
|
||||||
|
dispatchEventToWindows func(*CustomEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomEventProcessor(dispatchEventToWindows func(*CustomEvent)) *EventProcessor {
|
||||||
|
return &EventProcessor{
|
||||||
|
listeners: make(map[string][]*eventListener),
|
||||||
|
dispatchEventToWindows: dispatchEventToWindows,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// On is the equivalent of Javascript's `addEventListener`
|
||||||
|
func (e *EventProcessor) On(eventName string, callback func(event *CustomEvent)) func() {
|
||||||
|
return e.registerListener(eventName, callback, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnMultiple is the same as `On` but will unregister after `count` events
|
||||||
|
func (e *EventProcessor) OnMultiple(eventName string, callback func(event *CustomEvent), counter int) func() {
|
||||||
|
return e.registerListener(eventName, callback, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once is the same as `On` but will unregister after the first event
|
||||||
|
func (e *EventProcessor) Once(eventName string, callback func(event *CustomEvent)) func() {
|
||||||
|
return e.registerListener(eventName, callback, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit sends an event to all listeners
|
||||||
|
func (e *EventProcessor) Emit(thisEvent *CustomEvent) {
|
||||||
|
if thisEvent == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go e.dispatchEventToListeners(thisEvent)
|
||||||
|
go e.dispatchEventToWindows(thisEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventProcessor) Off(eventName string) {
|
||||||
|
e.unRegisterListener(eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EventProcessor) OffAll() {
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
defer e.notifyLock.Unlock()
|
||||||
|
e.listeners = make(map[string][]*eventListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerListener provides a means of subscribing to events of type "eventName"
|
||||||
|
func (e *EventProcessor) registerListener(eventName string, callback func(*CustomEvent), counter int) func() {
|
||||||
|
// Create new eventListener
|
||||||
|
thisListener := &eventListener{
|
||||||
|
callback: callback,
|
||||||
|
counter: counter,
|
||||||
|
delete: false,
|
||||||
|
}
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
// Append the new listener to the listeners slice
|
||||||
|
e.listeners[eventName] = append(e.listeners[eventName], thisListener)
|
||||||
|
e.notifyLock.Unlock()
|
||||||
|
return func() {
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
defer e.notifyLock.Unlock()
|
||||||
|
|
||||||
|
if _, ok := e.listeners[eventName]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.listeners[eventName] = lo.Filter(e.listeners[eventName], func(l *eventListener, i int) bool {
|
||||||
|
return l != thisListener
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unRegisterListener provides a means of unsubscribing to events of type "eventName"
|
||||||
|
func (e *EventProcessor) unRegisterListener(eventName string) {
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
defer e.notifyLock.Unlock()
|
||||||
|
delete(e.listeners, eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatchEventToListeners calls all registered listeners event name
|
||||||
|
func (e *EventProcessor) dispatchEventToListeners(event *CustomEvent) {
|
||||||
|
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
defer e.notifyLock.Unlock()
|
||||||
|
|
||||||
|
listeners := e.listeners[event.Name]
|
||||||
|
if listeners == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a dirty flag to indicate that there are items to delete
|
||||||
|
itemsToDelete := false
|
||||||
|
|
||||||
|
// Callback in goroutine
|
||||||
|
for _, listener := range listeners {
|
||||||
|
if listener.counter > 0 {
|
||||||
|
listener.counter--
|
||||||
|
}
|
||||||
|
go listener.callback(event)
|
||||||
|
|
||||||
|
if listener.counter == 0 {
|
||||||
|
listener.delete = true
|
||||||
|
itemsToDelete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have items to delete?
|
||||||
|
if itemsToDelete == true {
|
||||||
|
e.listeners[event.Name] = lo.Filter(listeners, func(l *eventListener, i int) bool {
|
||||||
|
return l.delete == false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
135
v3/pkg/application/events_test.go
Normal file
135
v3/pkg/application/events_test.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package application_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/application"
|
||||||
|
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockNotifier struct {
|
||||||
|
Events []*application.CustomEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockNotifier) dispatchEventToWindows(event *application.CustomEvent) {
|
||||||
|
m.Events = append(m.Events, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockNotifier) Reset() {
|
||||||
|
m.Events = []*application.CustomEvent{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_EventsOn(t *testing.T) {
|
||||||
|
i := is.New(t)
|
||||||
|
notifier := &mockNotifier{}
|
||||||
|
eventProcessor := application.NewCustomEventProcessor(notifier.dispatchEventToWindows)
|
||||||
|
|
||||||
|
// Test On
|
||||||
|
eventName := "test"
|
||||||
|
counter := 0
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
unregisterFn := eventProcessor.On(eventName, func(event *application.CustomEvent) {
|
||||||
|
// This is called in a goroutine
|
||||||
|
counter++
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
i.Equal(1, counter)
|
||||||
|
|
||||||
|
// Unregister
|
||||||
|
notifier.Reset()
|
||||||
|
unregisterFn()
|
||||||
|
counter = 0
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
i.Equal(0, counter)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_EventsOnce(t *testing.T) {
|
||||||
|
i := is.New(t)
|
||||||
|
notifier := &mockNotifier{}
|
||||||
|
eventProcessor := application.NewCustomEventProcessor(notifier.dispatchEventToWindows)
|
||||||
|
|
||||||
|
// Test On
|
||||||
|
eventName := "test"
|
||||||
|
counter := 0
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
unregisterFn := eventProcessor.Once(eventName, func(event *application.CustomEvent) {
|
||||||
|
// This is called in a goroutine
|
||||||
|
counter++
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
i.Equal(1, counter)
|
||||||
|
|
||||||
|
// Unregister
|
||||||
|
notifier.Reset()
|
||||||
|
unregisterFn()
|
||||||
|
counter = 0
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
i.Equal(0, counter)
|
||||||
|
|
||||||
|
}
|
||||||
|
func Test_EventsOnMultiple(t *testing.T) {
|
||||||
|
i := is.New(t)
|
||||||
|
notifier := &mockNotifier{}
|
||||||
|
eventProcessor := application.NewCustomEventProcessor(notifier.dispatchEventToWindows)
|
||||||
|
|
||||||
|
// Test On
|
||||||
|
eventName := "test"
|
||||||
|
counter := 0
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
unregisterFn := eventProcessor.OnMultiple(eventName, func(event *application.CustomEvent) {
|
||||||
|
// This is called in a goroutine
|
||||||
|
counter++
|
||||||
|
wg.Done()
|
||||||
|
}, 2)
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
i.Equal(2, counter)
|
||||||
|
|
||||||
|
// Unregister
|
||||||
|
notifier.Reset()
|
||||||
|
unregisterFn()
|
||||||
|
counter = 0
|
||||||
|
eventProcessor.Emit(&application.CustomEvent{
|
||||||
|
Name: "test",
|
||||||
|
Data: "test payload",
|
||||||
|
})
|
||||||
|
i.Equal(0, counter)
|
||||||
|
|
||||||
|
}
|
@ -29,7 +29,7 @@ func dispatchOnMainThreadCallback(callbackID C.uint) {
|
|||||||
id := uint(callbackID)
|
id := uint(callbackID)
|
||||||
fn := mainThreadFunctionStore[id]
|
fn := mainThreadFunctionStore[id]
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
Fatal("dispatchCallback called with invalid id: ", id)
|
Fatal("dispatchCallback called with invalid id: %v", id)
|
||||||
}
|
}
|
||||||
delete(mainThreadFunctionStore, id)
|
delete(mainThreadFunctionStore, id)
|
||||||
mainThreadFunctionStoreLock.RUnlock()
|
mainThreadFunctionStoreLock.RUnlock()
|
||||||
|
@ -163,7 +163,7 @@ func newRole(role Role) *MenuItem {
|
|||||||
case ToggleDevTools:
|
case ToggleDevTools:
|
||||||
return newToggleDevToolsMenuItem()
|
return newToggleDevToolsMenuItem()
|
||||||
case ResetZoom:
|
case ResetZoom:
|
||||||
return newResetZoomMenuItem()
|
return newZoomResetMenuItem()
|
||||||
case ZoomIn:
|
case ZoomIn:
|
||||||
return newZoomInMenuItem()
|
return newZoomInMenuItem()
|
||||||
case ZoomOut:
|
case ZoomOut:
|
||||||
|
@ -564,14 +564,14 @@ func newToggleDevToolsMenuItem() *MenuItem {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResetZoomMenuItem() *MenuItem {
|
func newZoomResetMenuItem() *MenuItem {
|
||||||
// reset zoom menu item
|
// reset zoom menu item
|
||||||
return newMenuItem("Actual Size").
|
return newMenuItem("Actual Size").
|
||||||
SetAccelerator("CmdOrCtrl+0").
|
SetAccelerator("CmdOrCtrl+0").
|
||||||
OnClick(func(ctx *Context) {
|
OnClick(func(ctx *Context) {
|
||||||
currentWindow := globalApplication.CurrentWindow()
|
currentWindow := globalApplication.CurrentWindow()
|
||||||
if currentWindow != nil {
|
if currentWindow != nil {
|
||||||
currentWindow.ResetZoom()
|
currentWindow.ZoomReset()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package application
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MessageProcessor struct {
|
type MessageProcessor struct {
|
||||||
@ -15,46 +18,63 @@ func NewMessageProcessor(w *WebviewWindow) *MessageProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) ProcessMessage(message string) {
|
func (m *MessageProcessor) httpError(rw http.ResponseWriter, message string, args ...any) {
|
||||||
|
m.Error(message, args...)
|
||||||
|
rw.WriteHeader(http.StatusBadRequest)
|
||||||
|
rw.Write([]byte(fmt.Sprintf(message, args...)))
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Implement calls to other windows
|
func (m *MessageProcessor) HandleRuntimeCall(rw http.ResponseWriter, r *http.Request) {
|
||||||
// Check for prefix "WINDOWID"
|
// Read "method" from query string
|
||||||
// If prefix exists, get window ID by parsing: "WINDOWID:12:MESSAGE"
|
method := r.URL.Query().Get("method")
|
||||||
|
if method == "" {
|
||||||
if strings.HasPrefix(message, "WINDOWID") {
|
m.httpError(rw, "No method specified")
|
||||||
m.Error("Window ID prefix not yet implemented")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
splitMethod := strings.Split(method, ".")
|
||||||
window := m.window
|
if len(splitMethod) != 2 {
|
||||||
|
m.httpError(rw, "Invalid method format")
|
||||||
if message == "" {
|
|
||||||
m.Error("Blank message received")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.Info("Processing message: %s", message)
|
// Get the object
|
||||||
switch message[0] {
|
object := splitMethod[0]
|
||||||
//case 'L':
|
// Get the method
|
||||||
// m.processLogMessage(message)
|
method = splitMethod[1]
|
||||||
//case 'E':
|
|
||||||
// return m.processEventMessage(message)
|
params := QueryParams(r.URL.Query())
|
||||||
//case 'C':
|
|
||||||
// return m.processCallMessage(message)
|
var targetWindow = m.window
|
||||||
//case 'c':
|
windowID := params.UInt("windowID")
|
||||||
// return m.processSecureCallMessage(message)
|
if windowID != nil {
|
||||||
case 'W':
|
// Get window for ID
|
||||||
m.processWindowMessage(message, window)
|
targetWindow = globalApplication.getWindowForID(*windowID)
|
||||||
//case 'B':
|
if targetWindow == nil {
|
||||||
// return m.processBrowserMessage(message)
|
m.Error("Window ID %s not found", *windowID)
|
||||||
case 'Q':
|
return
|
||||||
globalApplication.Quit()
|
}
|
||||||
case 'S':
|
}
|
||||||
//globalApplication.Show()
|
|
||||||
case 'H':
|
switch object {
|
||||||
//globalApplication.Hide()
|
case "window":
|
||||||
|
m.processWindowMethod(method, rw, r, targetWindow, params)
|
||||||
|
case "clipboard":
|
||||||
|
m.processClipboardMethod(method, rw, r, targetWindow, params)
|
||||||
|
case "dialog":
|
||||||
|
m.processDialogMethod(method, rw, r, targetWindow, params)
|
||||||
|
case "events":
|
||||||
|
m.processEventsMethod(method, rw, r, targetWindow, params)
|
||||||
|
case "application":
|
||||||
|
m.processApplicationMethod(method, rw, r, targetWindow, params)
|
||||||
|
case "log":
|
||||||
|
m.processLogMethod(method, rw, r, targetWindow, params)
|
||||||
default:
|
default:
|
||||||
m.Error("Unknown message from front end:", message)
|
m.httpError(rw, "Unknown runtime call: %s", object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) ProcessMessage(message string) {
|
||||||
|
m.Info("ProcessMessage from front end:", message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MessageProcessor) Error(message string, args ...any) {
|
func (m *MessageProcessor) Error(message string, args ...any) {
|
||||||
@ -64,3 +84,37 @@ func (m *MessageProcessor) Error(message string, args ...any) {
|
|||||||
func (m *MessageProcessor) Info(message string, args ...any) {
|
func (m *MessageProcessor) Info(message string, args ...any) {
|
||||||
fmt.Printf("[MessageProcessor] Info: "+message, args...)
|
fmt.Printf("[MessageProcessor] Info: "+message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) json(rw http.ResponseWriter, data any) {
|
||||||
|
// convert data to json
|
||||||
|
var jsonPayload = []byte("{}")
|
||||||
|
var err error
|
||||||
|
if data != nil {
|
||||||
|
jsonPayload, err = jsoniter.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
m.Error("Unable to convert data to JSON. Please report this to the Wails team! Error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = rw.Write(jsonPayload)
|
||||||
|
if err != nil {
|
||||||
|
m.Error("Unable to write json payload. Please report this to the Wails team! Error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw.Header().Set("Content-Type", "application/json")
|
||||||
|
m.ok(rw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) text(rw http.ResponseWriter, data string) {
|
||||||
|
_, err := rw.Write([]byte(data))
|
||||||
|
if err != nil {
|
||||||
|
m.Error("Unable to write json payload. Please report this to the Wails team! Error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rw.Header().Set("Content-Type", "text/plain")
|
||||||
|
m.ok(rw)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) ok(rw http.ResponseWriter) {
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
23
v3/pkg/application/messageprocessor_application.go
Normal file
23
v3/pkg/application/messageprocessor_application.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MessageProcessor) processApplicationMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "Quit":
|
||||||
|
globalApplication.Quit()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Hide":
|
||||||
|
globalApplication.Hide()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Show":
|
||||||
|
globalApplication.Show()
|
||||||
|
m.ok(rw)
|
||||||
|
default:
|
||||||
|
m.httpError(rw, "Unknown event method: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
v3/pkg/application/messageprocessor_clipboard.go
Normal file
25
v3/pkg/application/messageprocessor_clipboard.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MessageProcessor) processClipboardMethod(method string, rw http.ResponseWriter, _ *http.Request, _ *WebviewWindow, params QueryParams) {
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "SetText":
|
||||||
|
title := params.String("text")
|
||||||
|
if title == nil {
|
||||||
|
m.Error("SetText: text is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
globalApplication.Clipboard().SetText(*title)
|
||||||
|
m.ok(rw)
|
||||||
|
case "Text":
|
||||||
|
text := globalApplication.Clipboard().Text()
|
||||||
|
m.text(rw, text)
|
||||||
|
default:
|
||||||
|
m.httpError(rw, "Unknown clipboard method: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
128
v3/pkg/application/messageprocessor_dialog.go
Normal file
128
v3/pkg/application/messageprocessor_dialog.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MessageProcessor) dialogErrorCallback(message string, dialogID *string, err error) {
|
||||||
|
errorMsg := fmt.Sprintf(message, err)
|
||||||
|
m.Error(errorMsg)
|
||||||
|
msg := "_wails.dialogErrorCallback('" + *dialogID + "', " + strconv.Quote(errorMsg) + ");"
|
||||||
|
m.window.ExecJS(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) dialogCallback(dialogID *string, result string, isJSON bool) {
|
||||||
|
msg := fmt.Sprintf("_wails.dialogCallback('%s', %s, %v);", *dialogID, strconv.Quote(result), isJSON)
|
||||||
|
m.window.ExecJS(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MessageProcessor) processDialogMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
|
|
||||||
|
args, err := params.Args()
|
||||||
|
if err != nil {
|
||||||
|
m.httpError(rw, "Unable to parse arguments: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialogID := args.String("dialog-id")
|
||||||
|
if dialogID == nil {
|
||||||
|
m.Error("dialog-id is required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch method {
|
||||||
|
case "Info", "Warning", "Error", "Question":
|
||||||
|
var options MessageDialogOptions
|
||||||
|
err := params.ToStruct(&options)
|
||||||
|
if err != nil {
|
||||||
|
m.dialogErrorCallback("Error parsing dialog options: %s", dialogID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(options.Buttons) == 0 {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
options.Buttons = []*Button{{Label: "OK", IsDefault: true}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var dialog *MessageDialog
|
||||||
|
switch method {
|
||||||
|
case "Info":
|
||||||
|
dialog = globalApplication.InfoDialog()
|
||||||
|
case "Warning":
|
||||||
|
dialog = globalApplication.WarningDialog()
|
||||||
|
case "Error":
|
||||||
|
dialog = globalApplication.ErrorDialog()
|
||||||
|
case "Question":
|
||||||
|
dialog = globalApplication.QuestionDialog()
|
||||||
|
}
|
||||||
|
// TODO: Add support for attaching Message dialogs to windows
|
||||||
|
dialog.SetTitle(options.Title)
|
||||||
|
dialog.SetMessage(options.Message)
|
||||||
|
for _, button := range options.Buttons {
|
||||||
|
label := button.Label
|
||||||
|
button.OnClick(func() {
|
||||||
|
m.dialogCallback(dialogID, label, false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
dialog.AddButtons(options.Buttons)
|
||||||
|
dialog.Show()
|
||||||
|
m.ok(rw)
|
||||||
|
case "OpenFile":
|
||||||
|
var options OpenFileDialogOptions
|
||||||
|
err := params.ToStruct(&options)
|
||||||
|
if err != nil {
|
||||||
|
m.httpError(rw, "Error parsing dialog options: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialog := globalApplication.OpenFileDialogWithOptions(&options)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if options.AllowsMultipleSelection {
|
||||||
|
files, err := dialog.PromptForMultipleSelection()
|
||||||
|
if err != nil {
|
||||||
|
m.dialogErrorCallback("Error getting selection: %s", dialogID, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
result, err := json.Marshal(files)
|
||||||
|
if err != nil {
|
||||||
|
m.dialogErrorCallback("Error marshalling files: %s", dialogID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.dialogCallback(dialogID, string(result), true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
file, err := dialog.PromptForSingleSelection()
|
||||||
|
if err != nil {
|
||||||
|
m.dialogErrorCallback("Error getting selection: %s", dialogID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.dialogCallback(dialogID, file, false)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
m.ok(rw)
|
||||||
|
case "SaveFile":
|
||||||
|
var options SaveFileDialogOptions
|
||||||
|
err := params.ToStruct(&options)
|
||||||
|
if err != nil {
|
||||||
|
m.httpError(rw, "Error parsing dialog options: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialog := globalApplication.SaveFileDialogWithOptions(&options)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
file, err := dialog.PromptForSingleSelection()
|
||||||
|
if err != nil {
|
||||||
|
m.dialogErrorCallback("Error getting selection: %s", dialogID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.dialogCallback(dialogID, file, false)
|
||||||
|
}()
|
||||||
|
m.ok(rw)
|
||||||
|
|
||||||
|
default:
|
||||||
|
m.httpError(rw, "Unknown dialog method: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
v3/pkg/application/messageprocessor_events.go
Normal file
28
v3/pkg/application/messageprocessor_events.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MessageProcessor) processEventsMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "Emit":
|
||||||
|
var event CustomEvent
|
||||||
|
err := params.ToStruct(&event)
|
||||||
|
if err != nil {
|
||||||
|
m.httpError(rw, "Error parsing event: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if event.Name == "" {
|
||||||
|
m.httpError(rw, "Event name must be specified")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.Sender = window.Name()
|
||||||
|
globalApplication.Events.Emit(&event)
|
||||||
|
m.ok(rw)
|
||||||
|
default:
|
||||||
|
m.httpError(rw, "Unknown event method: %s", method)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,47 +1,25 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
//
|
import (
|
||||||
//import "errors"
|
"net/http"
|
||||||
//
|
|
||||||
////var logLevelMap = map[byte]logger.LogLevel{
|
"github.com/wailsapp/wails/v3/pkg/logger"
|
||||||
//// '1': pkgLogger.TRACE,
|
)
|
||||||
//// '2': pkgLogger.DEBUG,
|
|
||||||
//// '3': pkgLogger.INFO,
|
func (m *MessageProcessor) processLogMethod(method string, rw http.ResponseWriter, _ *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
//// '4': pkgLogger.WARNING,
|
switch method {
|
||||||
//// '5': pkgLogger.ERROR,
|
case "Log":
|
||||||
////}
|
var msg logger.Message
|
||||||
//
|
err := params.ToStruct(&msg)
|
||||||
//func (m *MessageProcessor) processLogMessage(message string) {
|
if err != nil {
|
||||||
// if len(message) < 3 {
|
m.httpError(rw, "error parsing log message: %s", err.Error())
|
||||||
// m.Error("Invalid Log Message: " + message)
|
return
|
||||||
// return
|
}
|
||||||
// }
|
msg.Sender = window.Name()
|
||||||
//
|
globalApplication.Log(&msg)
|
||||||
// messageText := message[2:]
|
m.ok(rw)
|
||||||
//
|
default:
|
||||||
// switch message[1] {
|
m.httpError(rw, "Unknown log method: %s", method)
|
||||||
// case 'T':
|
}
|
||||||
// d.log.Trace(messageText)
|
|
||||||
// case 'P':
|
}
|
||||||
// d.log.Print(messageText)
|
|
||||||
// case 'D':
|
|
||||||
// d.log.Debug(messageText)
|
|
||||||
// case 'I':
|
|
||||||
// d.log.Info(messageText)
|
|
||||||
// case 'W':
|
|
||||||
// d.log.Warning(messageText)
|
|
||||||
// case 'E':
|
|
||||||
// d.log.Error(messageText)
|
|
||||||
// case 'F':
|
|
||||||
// d.log.Fatal(messageText)
|
|
||||||
// case 'S':
|
|
||||||
// loglevel, exists := logLevelMap[message[2]]
|
|
||||||
// if !exists {
|
|
||||||
// return "", errors.New("Invalid Set Log Level Message: " + message)
|
|
||||||
// }
|
|
||||||
// d.log.SetLogLevel(loglevel)
|
|
||||||
// default:
|
|
||||||
// return "", errors.New("Invalid Log Message: " + message)
|
|
||||||
// }
|
|
||||||
// return "", nil
|
|
||||||
//}
|
|
||||||
|
190
v3/pkg/application/messageprocessor_params.go
Normal file
190
v3/pkg/application/messageprocessor_params.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryParams map[string][]string
|
||||||
|
|
||||||
|
func (qp QueryParams) String(key string) *string {
|
||||||
|
if qp == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
values := qp[key]
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &values[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) Int(key string) *int {
|
||||||
|
val := qp.String(key)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
result, err := strconv.Atoi(*val)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) UInt8(key string) *uint8 {
|
||||||
|
val := qp.String(key)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
intResult, err := strconv.Atoi(*val)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if intResult < 0 {
|
||||||
|
intResult = 0
|
||||||
|
}
|
||||||
|
if intResult > 255 {
|
||||||
|
intResult = 255
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = uint8(intResult)
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
func (qp QueryParams) UInt(key string) *uint {
|
||||||
|
val := qp.String(key)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
intResult, err := strconv.Atoi(*val)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if intResult < 0 {
|
||||||
|
intResult = 0
|
||||||
|
}
|
||||||
|
if intResult > 255 {
|
||||||
|
intResult = 255
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = uint(intResult)
|
||||||
|
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) Bool(key string) *bool {
|
||||||
|
val := qp.String(key)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
result, err := strconv.ParseBool(*val)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) Float64(key string) *float64 {
|
||||||
|
val := qp.String(key)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
result, err := strconv.ParseFloat(*val, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) ToStruct(str any) error {
|
||||||
|
args := qp["args"]
|
||||||
|
if len(args) == 1 {
|
||||||
|
return json.Unmarshal([]byte(args[0]), &str)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Args struct {
|
||||||
|
data map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Args) String(key string) *string {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[key]; val != nil {
|
||||||
|
result := fmt.Sprintf("%v", val)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Args) Int(s string) *int {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[s]; val != nil {
|
||||||
|
result := val.(int)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Args) UInt8(s string) *uint8 {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[s]; val != nil {
|
||||||
|
result := val.(uint8)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (a *Args) UInt(s string) *uint {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[s]; val != nil {
|
||||||
|
result := val.(uint)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Args) Float64(s string) *float64 {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[s]; val != nil {
|
||||||
|
result := val.(float64)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Args) Bool(s string) *bool {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if val := a.data[s]; val != nil {
|
||||||
|
result := val.(bool)
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp QueryParams) Args() (*Args, error) {
|
||||||
|
argData := qp["args"]
|
||||||
|
var result = &Args{
|
||||||
|
data: make(map[string]any),
|
||||||
|
}
|
||||||
|
if len(argData) == 1 {
|
||||||
|
err := json.Unmarshal([]byte(argData[0]), &result.data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
@ -1,95 +1,191 @@
|
|||||||
package application
|
package application
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v3/pkg/options"
|
"github.com/wailsapp/wails/v3/pkg/options"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *MessageProcessor) mustAtoI(input string) int {
|
func (m *MessageProcessor) processWindowMethod(method string, rw http.ResponseWriter, r *http.Request, window *WebviewWindow, params QueryParams) {
|
||||||
result, err := strconv.Atoi(input)
|
|
||||||
|
args, err := params.Args()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.Error("cannot convert %s to integer!", input)
|
m.httpError(rw, "Unable to parse arguments: %s", err)
|
||||||
}
|
return
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MessageProcessor) processWindowMessage(message string, window *WebviewWindow) {
|
|
||||||
if len(message) < 2 {
|
|
||||||
m.Error("Invalid Window Message: " + message)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch message[1] {
|
switch method {
|
||||||
case 'A':
|
case "SetTitle":
|
||||||
switch message[2:] {
|
title := args.String("title")
|
||||||
//case "SDT":
|
if title == nil {
|
||||||
// go window.WindowSetSystemDefaultTheme()
|
m.Error("SetTitle: title is required")
|
||||||
//case "LT":
|
return
|
||||||
// go window.SetLightTheme()
|
|
||||||
//case "DT":
|
|
||||||
// go window.SetDarkTheme()
|
|
||||||
case "TP:0", "TP:1":
|
|
||||||
if message[2:] == "TP:0" {
|
|
||||||
go window.SetAlwaysOnTop(false)
|
|
||||||
} else if message[2:] == "TP:1" {
|
|
||||||
go window.SetAlwaysOnTop(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 'c':
|
window.SetTitle(*title)
|
||||||
go window.Center()
|
m.ok(rw)
|
||||||
case 'T':
|
case "SetSize":
|
||||||
title := message[2:]
|
width := args.Int("width")
|
||||||
go window.SetTitle(title)
|
height := args.Int("height")
|
||||||
case 'F':
|
if width == nil || height == nil {
|
||||||
go window.Fullscreen()
|
m.Error("Invalid SetSize Message")
|
||||||
case 'f':
|
return
|
||||||
go window.UnFullscreen()
|
}
|
||||||
case 's':
|
window.SetSize(*width, *height)
|
||||||
parts := strings.Split(message[3:], ":")
|
m.ok(rw)
|
||||||
w := m.mustAtoI(parts[0])
|
case "SetPosition":
|
||||||
h := m.mustAtoI(parts[1])
|
x := args.Int("x")
|
||||||
go window.SetSize(w, h)
|
y := args.Int("y")
|
||||||
case 'p':
|
if x == nil || y == nil {
|
||||||
parts := strings.Split(message[3:], ":")
|
m.Error("Invalid SetPosition Message")
|
||||||
x := m.mustAtoI(parts[0])
|
return
|
||||||
y := m.mustAtoI(parts[1])
|
}
|
||||||
go window.SetPosition(x, y)
|
window.SetPosition(*x, *y)
|
||||||
case 'H':
|
m.ok(rw)
|
||||||
go window.Hide()
|
case "Fullscreen":
|
||||||
case 'S':
|
window.Fullscreen()
|
||||||
go window.Show()
|
m.ok(rw)
|
||||||
//case 'R':
|
case "UnFullscreen":
|
||||||
// go window.ReloadApp()
|
window.UnFullscreen()
|
||||||
case 'r':
|
m.ok(rw)
|
||||||
var rgba options.RGBA
|
case "Minimise":
|
||||||
err := json.Unmarshal([]byte(message[3:]), &rgba)
|
window.Minimize()
|
||||||
|
m.ok(rw)
|
||||||
|
case "UnMinimise":
|
||||||
|
window.UnMinimise()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Maximise":
|
||||||
|
window.Maximise()
|
||||||
|
m.ok(rw)
|
||||||
|
case "UnMaximise":
|
||||||
|
window.UnMaximise()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Show":
|
||||||
|
window.Show()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Hide":
|
||||||
|
window.Hide()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Close":
|
||||||
|
window.Close()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Center":
|
||||||
|
window.Center()
|
||||||
|
m.ok(rw)
|
||||||
|
case "Size":
|
||||||
|
width, height := window.Size()
|
||||||
|
m.json(rw, map[string]interface{}{
|
||||||
|
"width": width,
|
||||||
|
"height": height,
|
||||||
|
})
|
||||||
|
case "Position":
|
||||||
|
x, y := window.Position()
|
||||||
|
m.json(rw, map[string]interface{}{
|
||||||
|
"x": x,
|
||||||
|
"y": y,
|
||||||
|
})
|
||||||
|
case "SetBackgroundColour":
|
||||||
|
r := args.UInt8("r")
|
||||||
|
if r == nil {
|
||||||
|
m.Error("Invalid SetBackgroundColour Message: 'r' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g := args.UInt8("g")
|
||||||
|
if g == nil {
|
||||||
|
m.Error("Invalid SetBackgroundColour Message: 'g' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b := args.UInt8("b")
|
||||||
|
if b == nil {
|
||||||
|
m.Error("Invalid SetBackgroundColour Message: 'b' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a := args.UInt8("a")
|
||||||
|
if a == nil {
|
||||||
|
m.Error("Invalid SetBackgroundColour Message: 'a' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.SetBackgroundColour(&options.RGBA{
|
||||||
|
Red: *r,
|
||||||
|
Green: *g,
|
||||||
|
Blue: *b,
|
||||||
|
Alpha: *a,
|
||||||
|
})
|
||||||
|
m.ok(rw)
|
||||||
|
case "SetAlwaysOnTop":
|
||||||
|
alwaysOnTop := args.Bool("alwaysOnTop")
|
||||||
|
if alwaysOnTop == nil {
|
||||||
|
m.Error("Invalid SetAlwaysOnTop Message: 'alwaysOnTop' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.SetAlwaysOnTop(*alwaysOnTop)
|
||||||
|
m.ok(rw)
|
||||||
|
case "SetResizable":
|
||||||
|
resizable := args.Bool("resizable")
|
||||||
|
if resizable == nil {
|
||||||
|
m.Error("Invalid SetResizable Message: 'resizable' value required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.SetResizable(*resizable)
|
||||||
|
m.ok(rw)
|
||||||
|
case "SetMinSize":
|
||||||
|
width := args.Int("width")
|
||||||
|
height := args.Int("height")
|
||||||
|
if width == nil || height == nil {
|
||||||
|
m.Error("Invalid SetMinSize Message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.SetMinSize(*width, *height)
|
||||||
|
m.ok(rw)
|
||||||
|
case "SetMaxSize":
|
||||||
|
width := args.Int("width")
|
||||||
|
height := args.Int("height")
|
||||||
|
if width == nil || height == nil {
|
||||||
|
m.Error("Invalid SetMaxSize Message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.SetMaxSize(*width, *height)
|
||||||
|
m.ok(rw)
|
||||||
|
case "Width":
|
||||||
|
width := window.Width()
|
||||||
|
m.json(rw, map[string]interface{}{
|
||||||
|
"width": width,
|
||||||
|
})
|
||||||
|
case "Height":
|
||||||
|
height := window.Height()
|
||||||
|
m.json(rw, map[string]interface{}{
|
||||||
|
"height": height,
|
||||||
|
})
|
||||||
|
case "ZoomIn":
|
||||||
|
window.ZoomIn()
|
||||||
|
m.ok(rw)
|
||||||
|
case "ZoomOut":
|
||||||
|
window.ZoomOut()
|
||||||
|
m.ok(rw)
|
||||||
|
case "ZoomReset":
|
||||||
|
window.ZoomReset()
|
||||||
|
m.ok(rw)
|
||||||
|
case "GetZoom":
|
||||||
|
zoomLevel := window.GetZoom()
|
||||||
|
m.json(rw, map[string]interface{}{
|
||||||
|
"zoomLevel": zoomLevel,
|
||||||
|
})
|
||||||
|
case "Screen":
|
||||||
|
screen, err := window.GetScreen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.Error("Invalid RGBA Message: %s", err.Error())
|
m.httpError(rw, err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
go window.SetBackgroundColour(&rgba)
|
m.json(rw, screen)
|
||||||
case 'M':
|
case "SetZoom":
|
||||||
go window.Maximise()
|
zoomLevel := args.Float64("zoomLevel")
|
||||||
//case 't':
|
if zoomLevel == nil {
|
||||||
// go window.ToggleMaximise()
|
m.Error("Invalid SetZoom Message: invalid 'zoomLevel' value")
|
||||||
case 'U':
|
return
|
||||||
go window.UnMaximise()
|
}
|
||||||
case 'm':
|
window.SetZoom(*zoomLevel)
|
||||||
go window.Minimise()
|
m.ok(rw)
|
||||||
case 'u':
|
|
||||||
go window.UnMinimise()
|
|
||||||
case 'Z':
|
|
||||||
parts := strings.Split(message[3:], ":")
|
|
||||||
w := m.mustAtoI(parts[0])
|
|
||||||
h := m.mustAtoI(parts[1])
|
|
||||||
go window.SetMaxSize(w, h)
|
|
||||||
case 'z':
|
|
||||||
parts := strings.Split(message[3:], ":")
|
|
||||||
w := m.mustAtoI(parts[0])
|
|
||||||
h := m.mustAtoI(parts[1])
|
|
||||||
go window.SetMinSize(w, h)
|
|
||||||
default:
|
default:
|
||||||
m.Error("unknown Window message: %s", message)
|
m.httpError(rw, "Unknown window method: %s", method)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ package application
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v3/pkg/logger"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
"github.com/wailsapp/wails/v2/pkg/assetserver"
|
||||||
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
"github.com/wailsapp/wails/v2/pkg/assetserver/webview"
|
||||||
@ -34,9 +37,11 @@ type (
|
|||||||
reload()
|
reload()
|
||||||
forceReload()
|
forceReload()
|
||||||
toggleDevTools()
|
toggleDevTools()
|
||||||
resetZoom()
|
zoomReset()
|
||||||
zoomIn()
|
zoomIn()
|
||||||
zoomOut()
|
zoomOut()
|
||||||
|
getZoom() float64
|
||||||
|
setZoom(zoom float64)
|
||||||
close()
|
close()
|
||||||
zoom()
|
zoom()
|
||||||
minimize()
|
minimize()
|
||||||
@ -96,11 +101,10 @@ func NewWindow(options *options.WebviewWindow) *WebviewWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware}
|
opts := assetserveroptions.Options{Assets: options.Assets.FS, Handler: options.Assets.Handler, Middleware: options.Assets.Middleware}
|
||||||
// TODO Bindings, Logger, ServingFrom disk?
|
// TODO Bindings, ServingFrom disk?
|
||||||
srv, err := assetserver.NewAssetServer("", opts, false, nil, runtime.RuntimeAssetsBundle)
|
srv, err := assetserver.NewAssetServer("", opts, false, nil, runtime.RuntimeAssetsBundle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO handle errors
|
globalApplication.fatal(err.Error())
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &WebviewWindow{
|
result := &WebviewWindow{
|
||||||
@ -112,6 +116,7 @@ func NewWindow(options *options.WebviewWindow) *WebviewWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.messageProcessor = NewMessageProcessor(result)
|
result.messageProcessor = NewMessageProcessor(result)
|
||||||
|
srv.UseRuntimeHandler(result.messageProcessor)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -126,6 +131,13 @@ func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebviewWindow) Name() string {
|
||||||
|
if w.options.Name != "" {
|
||||||
|
return w.options.Name
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Window %d", w.id)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WebviewWindow) SetSize(width, height int) *WebviewWindow {
|
func (w *WebviewWindow) SetSize(width, height int) *WebviewWindow {
|
||||||
// Don't set size if fullscreen
|
// Don't set size if fullscreen
|
||||||
if w.IsFullscreen() {
|
if w.IsFullscreen() {
|
||||||
@ -211,6 +223,21 @@ func (w *WebviewWindow) SetURL(s string) *WebviewWindow {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebviewWindow) SetZoom(magnification float64) *WebviewWindow {
|
||||||
|
w.options.Zoom = magnification
|
||||||
|
if w.impl != nil {
|
||||||
|
w.impl.setZoom(magnification)
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WebviewWindow) GetZoom() float64 {
|
||||||
|
if w.impl != nil {
|
||||||
|
return w.impl.getZoom()
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WebviewWindow) SetResizable(b bool) *WebviewWindow {
|
func (w *WebviewWindow) SetResizable(b bool) *WebviewWindow {
|
||||||
w.options.DisableResize = !b
|
w.options.DisableResize = !b
|
||||||
if w.impl != nil {
|
if w.impl != nil {
|
||||||
@ -347,7 +374,7 @@ func (w *WebviewWindow) SetBackgroundColour(colour *options.RGBA) *WebviewWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebviewWindow) handleMessage(message string) {
|
func (w *WebviewWindow) handleMessage(message string) {
|
||||||
fmt.Printf("[window %d] %s\n", w.id, message)
|
w.info(message)
|
||||||
// Check for special messages
|
// Check for special messages
|
||||||
if message == "test" {
|
if message == "test" {
|
||||||
w.SetTitle("Hello World")
|
w.SetTitle("Hello World")
|
||||||
@ -358,7 +385,7 @@ func (w *WebviewWindow) handleMessage(message string) {
|
|||||||
|
|
||||||
func (w *WebviewWindow) handleWebViewRequest(request webview.Request) {
|
func (w *WebviewWindow) handleWebViewRequest(request webview.Request) {
|
||||||
url, _ := request.URL()
|
url, _ := request.URL()
|
||||||
fmt.Printf("[window %d] Request %s\n", w.id, url)
|
w.info("Request: %s", url)
|
||||||
w.assets.ServeWebViewRequest(request)
|
w.assets.ServeWebViewRequest(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,9 +476,9 @@ func (w *WebviewWindow) ToggleDevTools() {
|
|||||||
w.impl.toggleDevTools()
|
w.impl.toggleDevTools()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebviewWindow) ResetZoom() *WebviewWindow {
|
func (w *WebviewWindow) ZoomReset() *WebviewWindow {
|
||||||
if w.impl != nil {
|
if w.impl != nil {
|
||||||
w.impl.resetZoom()
|
w.impl.zoomReset()
|
||||||
}
|
}
|
||||||
return w
|
return w
|
||||||
|
|
||||||
@ -598,3 +625,19 @@ func (w *WebviewWindow) SetFrameless(frameless bool) *WebviewWindow {
|
|||||||
}
|
}
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *WebviewWindow) dispatchCustomEvent(event *CustomEvent) {
|
||||||
|
msg := fmt.Sprintf("_wails.dispatchCustomEvent(%s);", event.ToJSON())
|
||||||
|
w.ExecJS(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WebviewWindow) info(message string, args ...any) {
|
||||||
|
|
||||||
|
globalApplication.Log(&logger.Message{
|
||||||
|
Level: "INFO",
|
||||||
|
Message: message,
|
||||||
|
Data: args,
|
||||||
|
Sender: w.Name(),
|
||||||
|
Time: time.Now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -251,8 +251,8 @@ void windowEnableDevTools(void* nsWindow) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// windowResetZoom
|
// windowZoomReset
|
||||||
void windowResetZoom(void* nsWindow) {
|
void windowZoomReset(void* nsWindow) {
|
||||||
// Reset zoom on main thread
|
// Reset zoom on main thread
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
// Get window delegate
|
// Get window delegate
|
||||||
@ -262,6 +262,24 @@ void windowResetZoom(void* nsWindow) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// windowZoomSet
|
||||||
|
void windowZoomSet(void* nsWindow, double zoom) {
|
||||||
|
// Reset zoom on main thread
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
// Get window delegate
|
||||||
|
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[(WebviewWindow*)nsWindow delegate];
|
||||||
|
// Reset zoom
|
||||||
|
[delegate.webView setMagnification:zoom];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// windowZoomGet
|
||||||
|
float windowZoomGet(void* nsWindow) {
|
||||||
|
// Get zoom
|
||||||
|
WebviewWindowDelegate* delegate = (WebviewWindowDelegate*)[(WebviewWindow*)nsWindow delegate];
|
||||||
|
return [delegate.webView magnification];
|
||||||
|
}
|
||||||
|
|
||||||
// windowZoomIn
|
// windowZoomIn
|
||||||
void windowZoomIn(void* nsWindow) {
|
void windowZoomIn(void* nsWindow) {
|
||||||
// Zoom in on main thread
|
// Zoom in on main thread
|
||||||
@ -762,6 +780,14 @@ type macosWebviewWindow struct {
|
|||||||
parent *WebviewWindow
|
parent *WebviewWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *macosWebviewWindow) getZoom() float64 {
|
||||||
|
return float64(C.windowZoomGet(w.nsWindow))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *macosWebviewWindow) setZoom(zoom float64) {
|
||||||
|
C.windowZoomSet(w.nsWindow, C.double(zoom))
|
||||||
|
}
|
||||||
|
|
||||||
func (w *macosWebviewWindow) setFrameless(frameless bool) {
|
func (w *macosWebviewWindow) setFrameless(frameless bool) {
|
||||||
C.windowSetFrameless(w.nsWindow, C.bool(frameless))
|
C.windowSetFrameless(w.nsWindow, C.bool(frameless))
|
||||||
if frameless {
|
if frameless {
|
||||||
@ -848,8 +874,8 @@ func (w *macosWebviewWindow) zoomOut() {
|
|||||||
C.windowZoomOut(w.nsWindow)
|
C.windowZoomOut(w.nsWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *macosWebviewWindow) resetZoom() {
|
func (w *macosWebviewWindow) zoomReset() {
|
||||||
C.windowResetZoom(w.nsWindow)
|
C.windowZoomReset(w.nsWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *macosWebviewWindow) toggleDevTools() {
|
func (w *macosWebviewWindow) toggleDevTools() {
|
||||||
@ -1017,6 +1043,7 @@ func (w *macosWebviewWindow) run() {
|
|||||||
if w.parent.options.MaxWidth != 0 || w.parent.options.MaxHeight != 0 {
|
if w.parent.options.MaxWidth != 0 || w.parent.options.MaxHeight != 0 {
|
||||||
w.setMaxSize(w.parent.options.MaxWidth, w.parent.options.MaxHeight)
|
w.setMaxSize(w.parent.options.MaxWidth, w.parent.options.MaxHeight)
|
||||||
}
|
}
|
||||||
|
//w.setZoom(w.parent.options.Zoom)
|
||||||
w.enableDevTools()
|
w.enableDevTools()
|
||||||
w.setBackgroundColour(w.parent.options.BackgroundColour)
|
w.setBackgroundColour(w.parent.options.BackgroundColour)
|
||||||
|
|
||||||
@ -1078,7 +1105,6 @@ func (w *macosWebviewWindow) run() {
|
|||||||
if w.parent.options.Hidden == false {
|
if w.parent.options.Hidden == false {
|
||||||
C.windowShow(w.nsWindow)
|
C.windowShow(w.nsWindow)
|
||||||
}
|
}
|
||||||
C.printWindowStyle(w.nsWindow)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
v3/pkg/logger/log.go
Normal file
35
v3/pkg/logger/log.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
output []Output
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(outputs ...Output) *Logger {
|
||||||
|
result := &Logger{}
|
||||||
|
if outputs != nil {
|
||||||
|
result.output = outputs
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) AddOutput(output Output) {
|
||||||
|
l.output = append(l.output, output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Log(message *Message) {
|
||||||
|
for _, o := range l.output {
|
||||||
|
go o.Log(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Logger) Flush() {
|
||||||
|
for _, o := range l.output {
|
||||||
|
if err := o.Flush(); err != nil {
|
||||||
|
fmt.Printf("Error flushing '%s' Logger: %s\n", o.Name(), err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
v3/pkg/logger/log_console.go
Normal file
27
v3/pkg/logger/log_console.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Console struct{}
|
||||||
|
|
||||||
|
func (l *Console) Name() string {
|
||||||
|
return "Console"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Console) Log(message *Message) {
|
||||||
|
msg := fmt.Sprintf(message.Message+"\n", message.Data...)
|
||||||
|
level := ""
|
||||||
|
if message.Level != "" {
|
||||||
|
level = fmt.Sprintf("[%s] ", message.Level)
|
||||||
|
}
|
||||||
|
sender := ""
|
||||||
|
if message.Sender != "" {
|
||||||
|
sender = fmt.Sprintf("%s: ", message.Sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s%s%s", level, sender, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Console) Flush() error {
|
||||||
|
return nil
|
||||||
|
}
|
11
v3/pkg/logger/message.go
Normal file
11
v3/pkg/logger/message.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Level string `json:"log"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data []any `json:"data,omitempty"`
|
||||||
|
Sender string `json:"-"`
|
||||||
|
Time time.Time `json:"-"`
|
||||||
|
}
|
7
v3/pkg/logger/output.go
Normal file
7
v3/pkg/logger/output.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
type Output interface {
|
||||||
|
Name() string
|
||||||
|
Log(message *Message)
|
||||||
|
Flush() error
|
||||||
|
}
|
@ -1,9 +1,15 @@
|
|||||||
package options
|
package options
|
||||||
|
|
||||||
|
import "github.com/wailsapp/wails/v3/pkg/logger"
|
||||||
|
|
||||||
type Application struct {
|
type Application struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
Icon []byte
|
Icon []byte
|
||||||
Mac Mac
|
Mac Mac
|
||||||
Bind []interface{}
|
Bind []interface{}
|
||||||
|
Logger struct {
|
||||||
|
Silent bool
|
||||||
|
CustomLoggers []logger.Output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WebviewWindow struct {
|
type WebviewWindow struct {
|
||||||
// Alias is a human-readable name for the window. This can be used to reference the window in the frontend.
|
Name string
|
||||||
Alias string
|
|
||||||
Title string
|
Title string
|
||||||
Width, Height int
|
Width, Height int
|
||||||
AlwaysOnTop bool
|
AlwaysOnTop bool
|
||||||
@ -39,6 +38,7 @@ type WebviewWindow struct {
|
|||||||
FullscreenButtonEnabled bool
|
FullscreenButtonEnabled bool
|
||||||
Hidden bool
|
Hidden bool
|
||||||
EnableFraudulentWebsiteWarnings bool
|
EnableFraudulentWebsiteWarnings bool
|
||||||
|
Zoom float64
|
||||||
}
|
}
|
||||||
|
|
||||||
var WindowDefaults = &WebviewWindow{
|
var WindowDefaults = &WebviewWindow{
|
||||||
|
Loading…
Reference in New Issue
Block a user