mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-04 04:11:56 +08:00
[windows-x] Support events runtime js->go, Refactor events methods, Refactor JS runtime.
This commit is contained in:
parent
244b3dc2b4
commit
619d8cc05e
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/wailsapp/wails/v2/internal/binding"
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
"github.com/wailsapp/wails/v2/internal/frontend"
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
"github.com/wailsapp/wails/v2/internal/frontend/dispatcher"
|
"github.com/wailsapp/wails/v2/internal/frontend/dispatcher"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend/runtime"
|
||||||
"github.com/wailsapp/wails/v2/internal/logger"
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
"github.com/wailsapp/wails/v2/internal/menumanager"
|
"github.com/wailsapp/wails/v2/internal/menumanager"
|
||||||
"github.com/wailsapp/wails/v2/internal/signal"
|
"github.com/wailsapp/wails/v2/internal/signal"
|
||||||
@ -26,88 +27,18 @@ type App struct {
|
|||||||
// Startup/Shutdown
|
// Startup/Shutdown
|
||||||
startupCallback func(ctx context.Context)
|
startupCallback func(ctx context.Context)
|
||||||
shutdownCallback func()
|
shutdownCallback func()
|
||||||
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) Run() error {
|
func (a *App) Run() error {
|
||||||
|
return a.frontend.Run(a.ctx)
|
||||||
go func() {
|
|
||||||
//time.Sleep(3 * time.Second)
|
|
||||||
//println("WindowSetSize(3000,2000)")
|
|
||||||
//a.frontend.WindowSetSize(3000,2000)
|
|
||||||
//x,y := a.frontend.WindowGetSize()
|
|
||||||
//println("X", x, "Y", y)
|
|
||||||
//time.Sleep(3 * time.Second)
|
|
||||||
//println("a.frontend.WindowSetSize(10,10)")
|
|
||||||
//a.frontend.WindowSetSize(10,10)
|
|
||||||
//x,y = a.frontend.WindowGetSize()
|
|
||||||
//println("X", x, "Y", y)
|
|
||||||
//time.Sleep(3 * time.Second)
|
|
||||||
//time.Sleep(3 * time.Second)
|
|
||||||
//println("WindowSetMaxSize(50,50)")
|
|
||||||
//a.frontend.WindowSetMaxSize(200,200)
|
|
||||||
//x,y := a.frontend.WindowGetSize()
|
|
||||||
//println("X", x, "Y", y)
|
|
||||||
//time.Sleep(3 * time.Second)
|
|
||||||
//println("WindowSetMinSize(100,100)")
|
|
||||||
//a.frontend.WindowSetMinSize(600,600)
|
|
||||||
//x,y = a.frontend.WindowGetSize()
|
|
||||||
//println("X", x, "Y", y)
|
|
||||||
//println("fullscreen")
|
|
||||||
//a.frontend.WindowFullscreen()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("unfullscreen")
|
|
||||||
//a.frontend.WindowUnFullscreen()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("hide")
|
|
||||||
//a.frontend.WindowHide()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("show")
|
|
||||||
//a.frontend.WindowShow()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("title 1")
|
|
||||||
//a.frontend.WindowSetTitle("title 1")
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("title 2")
|
|
||||||
//a.frontend.WindowSetTitle("title 2")
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("setsize 1")
|
|
||||||
//a.frontend.WindowSetSize(100,100)
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("setsize 2")
|
|
||||||
//a.frontend.WindowSetSize(500,500)
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("setpos 1")
|
|
||||||
//a.frontend.WindowSetPos(0,0)
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("setpos 2")
|
|
||||||
//a.frontend.WindowSetPos(500,500)
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("Center 1")
|
|
||||||
//a.frontend.WindowCenter()
|
|
||||||
//time.Sleep(5 * time.Second)
|
|
||||||
//println("Center 2")
|
|
||||||
//a.frontend.WindowCenter()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("maximise")
|
|
||||||
//a.frontend.WindowMaximise()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("UnMaximise")
|
|
||||||
//a.frontend.WindowUnmaximise()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("minimise")
|
|
||||||
//a.frontend.WindowMinimise()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
//println("unminimise")
|
|
||||||
//a.frontend.WindowUnminimise()
|
|
||||||
//time.Sleep(1 * time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return a.frontend.Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateApp
|
// CreateApp
|
||||||
func CreateApp(appoptions *options.App) (*App, error) {
|
func CreateApp(appoptions *options.App) (*App, error) {
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
// Merge default options
|
// Merge default options
|
||||||
options.MergeDefaults(appoptions)
|
options.MergeDefaults(appoptions)
|
||||||
|
|
||||||
@ -128,12 +59,14 @@ func CreateApp(appoptions *options.App) (*App, error) {
|
|||||||
// Create binding exemptions - Ugly hack. There must be a better way
|
// Create binding exemptions - Ugly hack. There must be a better way
|
||||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
||||||
|
eventHandler := runtime.NewEvents(myLogger)
|
||||||
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings)
|
ctx = context.WithValue(ctx, "events", eventHandler)
|
||||||
|
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler)
|
||||||
|
|
||||||
appFrontend := NewFrontend(appoptions, myLogger, appBindings, messageDispatcher)
|
appFrontend := NewFrontend(appoptions, myLogger, appBindings, messageDispatcher)
|
||||||
|
|
||||||
result := &App{
|
result := &App{
|
||||||
|
ctx: ctx,
|
||||||
frontend: appFrontend,
|
frontend: appFrontend,
|
||||||
logger: myLogger,
|
logger: myLogger,
|
||||||
menuManager: menuManager,
|
menuManager: menuManager,
|
||||||
|
36
v2/internal/frontend/dispatcher/dispatcher.go
Normal file
36
v2/internal/frontend/dispatcher/dispatcher.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/binding"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dispatcher struct {
|
||||||
|
log *logger.Logger
|
||||||
|
bindings *binding.Bindings
|
||||||
|
events frontend.Events
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDispatcher(log *logger.Logger, bindings *binding.Bindings, events frontend.Events) *Dispatcher {
|
||||||
|
return &Dispatcher{
|
||||||
|
log: log,
|
||||||
|
bindings: bindings,
|
||||||
|
events: events,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) ProcessMessage(message string) error {
|
||||||
|
if message == "" {
|
||||||
|
return errors.New("No message to process")
|
||||||
|
}
|
||||||
|
switch message[0] {
|
||||||
|
case 'L':
|
||||||
|
return d.processLogMessage(message)
|
||||||
|
case 'E':
|
||||||
|
return d.processEventMessage(message)
|
||||||
|
default:
|
||||||
|
return errors.New("Unknown message from front end: " + message)
|
||||||
|
}
|
||||||
|
}
|
32
v2/internal/frontend/dispatcher/events.go
Normal file
32
v2/internal/frontend/dispatcher/events.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EventMessage struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Data []interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) processEventMessage(message string) error {
|
||||||
|
if len(message) < 3 {
|
||||||
|
return errors.New("Invalid Event Message: " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch message[1] {
|
||||||
|
case 'E':
|
||||||
|
var eventMessage EventMessage
|
||||||
|
err := json.Unmarshal([]byte(message[2:]), &eventMessage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go d.events.Notify(eventMessage.Name, eventMessage.Data)
|
||||||
|
case 'X':
|
||||||
|
eventName := message[2:]
|
||||||
|
go d.events.Off(eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
49
v2/internal/frontend/dispatcher/log.go
Normal file
49
v2/internal/frontend/dispatcher/log.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package dispatcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
pkgLogger "github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logLevelMap = map[byte]logger.LogLevel{
|
||||||
|
'1': pkgLogger.TRACE,
|
||||||
|
'2': pkgLogger.DEBUG,
|
||||||
|
'3': pkgLogger.INFO,
|
||||||
|
'4': pkgLogger.WARNING,
|
||||||
|
'5': pkgLogger.ERROR,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) processLogMessage(message string) error {
|
||||||
|
if len(message) < 3 {
|
||||||
|
return errors.New("Invalid Log Message: " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
messageText := message[2:]
|
||||||
|
|
||||||
|
switch message[1] {
|
||||||
|
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
|
||||||
|
}
|
10
v2/internal/frontend/events.go
Normal file
10
v2/internal/frontend/events.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package frontend
|
||||||
|
|
||||||
|
type Events interface {
|
||||||
|
On(eventName string, callback func(...interface{}))
|
||||||
|
OnMultiple(eventName string, callback func(...interface{}), counter int)
|
||||||
|
Once(eventName string, callback func(...interface{}))
|
||||||
|
Emit(eventName string, data ...interface{})
|
||||||
|
Off(eventName string)
|
||||||
|
Notify(name string, data ...interface{})
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package frontend
|
package frontend
|
||||||
|
|
||||||
import "github.com/wailsapp/wails/v2/pkg/menu"
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
|
)
|
||||||
|
|
||||||
// FileFilter defines a filter for dialog boxes
|
// FileFilter defines a filter for dialog boxes
|
||||||
type FileFilter struct {
|
type FileFilter struct {
|
||||||
@ -54,15 +57,9 @@ type MessageDialogOptions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Frontend interface {
|
type Frontend interface {
|
||||||
|
Run(context.Context) error
|
||||||
// Main methods
|
|
||||||
Run() error
|
|
||||||
Quit()
|
Quit()
|
||||||
|
|
||||||
//// Events
|
|
||||||
//NotifyEvent(message string)
|
|
||||||
//CallResult(message string)
|
|
||||||
//
|
|
||||||
// Dialog
|
// Dialog
|
||||||
OpenFileDialog(dialogOptions OpenDialogOptions) (string, error)
|
OpenFileDialog(dialogOptions OpenDialogOptions) (string, error)
|
||||||
OpenMultipleFilesDialog(dialogOptions OpenDialogOptions) ([]string, error)
|
OpenMultipleFilesDialog(dialogOptions OpenDialogOptions) ([]string, error)
|
||||||
|
160
v2/internal/frontend/runtime/desktop/events.js
Normal file
160
v2/internal/frontend/runtime/desktop/events.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 6 */
|
||||||
|
|
||||||
|
import {SendMessage} from './ipc';
|
||||||
|
|
||||||
|
// Defines a single listener with a maximum number of times to callback
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Listener class defines a listener! :-)
|
||||||
|
*
|
||||||
|
* @class Listener
|
||||||
|
*/
|
||||||
|
class Listener {
|
||||||
|
/**
|
||||||
|
* Creates an instance of Listener.
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {number} maxCallbacks
|
||||||
|
* @memberof Listener
|
||||||
|
*/
|
||||||
|
constructor(callback, maxCallbacks) {
|
||||||
|
// Default of -1 means infinite
|
||||||
|
maxCallbacks = maxCallbacks || -1;
|
||||||
|
// Callback invokes the callback with the given data
|
||||||
|
// Returns true if this listener should be destroyed
|
||||||
|
this.Callback = (data) => {
|
||||||
|
callback.apply(null, data);
|
||||||
|
// If maxCallbacks is infinite, return false (do not destroy)
|
||||||
|
if (maxCallbacks === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Decrement maxCallbacks. Return true if now 0, otherwise false
|
||||||
|
maxCallbacks -= 1;
|
||||||
|
return maxCallbacks === 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let eventListeners = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
* @param {number} maxCallbacks
|
||||||
|
*/
|
||||||
|
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
|
||||||
|
eventListeners[eventName] = eventListeners[eventName] || [];
|
||||||
|
const thisListener = new Listener(callback, maxCallbacks);
|
||||||
|
eventListeners[eventName].push(thisListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked every time the event is emitted
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function EventsOn(eventName, callback) {
|
||||||
|
EventsOnMultiple(eventName, callback, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an event listener that will be invoked once then destroyed
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {string} eventName
|
||||||
|
* @param {function} callback
|
||||||
|
*/
|
||||||
|
export function EventsOnce(eventName, callback) {
|
||||||
|
EventsOnMultiple(eventName, callback, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyListeners(eventData) {
|
||||||
|
|
||||||
|
// Get the event name
|
||||||
|
let eventName = eventData.name;
|
||||||
|
|
||||||
|
// Check if we have any listeners for this event
|
||||||
|
if (eventListeners[eventName]) {
|
||||||
|
|
||||||
|
// Keep a list of listener indexes to destroy
|
||||||
|
const newEventListenerList = eventListeners[eventName].slice();
|
||||||
|
|
||||||
|
// Iterate listeners
|
||||||
|
for (let count = 0; count < eventListeners[eventName].length; count += 1) {
|
||||||
|
|
||||||
|
// Get next listener
|
||||||
|
const listener = eventListeners[eventName][count];
|
||||||
|
|
||||||
|
let data = eventData.data;
|
||||||
|
|
||||||
|
// Do the callback
|
||||||
|
const destroy = listener.Callback(data);
|
||||||
|
if (destroy) {
|
||||||
|
// if the listener indicated to destroy itself, add it to the destroy list
|
||||||
|
newEventListenerList.splice(count, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update callbacks with new list of listeners
|
||||||
|
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
|
||||||
|
SendMessage('EE' + JSON.stringify(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EventsOff(eventName) {
|
||||||
|
// Notify Go listeners
|
||||||
|
SendMessage('EX' + eventName);
|
||||||
|
}
|
@ -31,7 +31,7 @@ function sendLogMessage(level, message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Trace(message) {
|
export function LogTrace(message) {
|
||||||
sendLogMessage('T', message);
|
sendLogMessage('T', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export function Trace(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Print(message) {
|
export function LogPrint(message) {
|
||||||
sendLogMessage('P', message);
|
sendLogMessage('P', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export function Print(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Debug(message) {
|
export function LogDebug(message) {
|
||||||
sendLogMessage('D', message);
|
sendLogMessage('D', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ export function Debug(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Info(message) {
|
export function LogInfo(message) {
|
||||||
sendLogMessage('I', message);
|
sendLogMessage('I', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ export function Info(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Warning(message) {
|
export function LogWarning(message) {
|
||||||
sendLogMessage('W', message);
|
sendLogMessage('W', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ export function Warning(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Error(message) {
|
export function LogError(message) {
|
||||||
sendLogMessage('E', message);
|
sendLogMessage('E', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ export function Error(message) {
|
|||||||
* @export
|
* @export
|
||||||
* @param {string} message
|
* @param {string} message
|
||||||
*/
|
*/
|
||||||
export function Fatal(message) {
|
export function LogFatal(message) {
|
||||||
sendLogMessage('F', message);
|
sendLogMessage('F', message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ export function SetLogLevel(loglevel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log levels
|
// Log levels
|
||||||
export const Level = {
|
export const LogLevel = {
|
||||||
TRACE: 1,
|
TRACE: 1,
|
||||||
DEBUG: 2,
|
DEBUG: 2,
|
||||||
INFO: 3,
|
INFO: 3,
|
||||||
|
43
v2/internal/frontend/runtime/desktop/main.js
Normal file
43
v2/internal/frontend/runtime/desktop/main.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The lightweight framework for web-like apps
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
import * as Log from './log';
|
||||||
|
import {EventsEmit, EventsNotify, EventsOff, EventsOn, EventsOnce, EventsOnMultiple} from './events';
|
||||||
|
// import {Callback, SystemCall} from './calls';
|
||||||
|
// import {AddScript, DisableDefaultContextMenu, InjectCSS} from './utils';
|
||||||
|
// import {AddIPCListener, SendMessage} from 'ipc';
|
||||||
|
|
||||||
|
// Backend is where the Go struct wrappers get bound to
|
||||||
|
window.backend = {};
|
||||||
|
|
||||||
|
window.runtime = {
|
||||||
|
...Log,
|
||||||
|
EventsOn,
|
||||||
|
EventsOnce,
|
||||||
|
EventsOnMultiple,
|
||||||
|
EventsEmit,
|
||||||
|
EventsOff,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialise global if not already
|
||||||
|
window.wails = {
|
||||||
|
_: {
|
||||||
|
// Callback,
|
||||||
|
EventsNotify,
|
||||||
|
// AddScript,
|
||||||
|
// InjectCSS,
|
||||||
|
// DisableDefaultContextMenu,
|
||||||
|
// // Init,
|
||||||
|
// AddIPCListener,
|
||||||
|
// SystemCall,
|
||||||
|
// SendMessage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
135
v2/internal/frontend/runtime/events.go
Normal file
135
v2/internal/frontend/runtime/events.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/logger"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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(...interface{}) // 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events handles eventing
|
||||||
|
type Events struct {
|
||||||
|
log *logger.Logger
|
||||||
|
|
||||||
|
// Go event listeners
|
||||||
|
listeners map[string][]*eventListener
|
||||||
|
notifyLock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) Notify(name string, data ...interface{}) {
|
||||||
|
e.notify(name, data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) On(eventName string, callback func(...interface{})) {
|
||||||
|
e.registerListener(eventName, callback, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) OnMultiple(eventName string, callback func(...interface{}), counter int) {
|
||||||
|
e.registerListener(eventName, callback, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) Once(eventName string, callback func(...interface{})) {
|
||||||
|
e.registerListener(eventName, callback, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) Emit(eventName string, data ...interface{}) {
|
||||||
|
e.notify(eventName, data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Events) Off(eventName string) {
|
||||||
|
e.unRegisterListener(eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEvents creates a new log subsystem
|
||||||
|
func NewEvents(log *logger.Logger) *Events {
|
||||||
|
result := &Events{
|
||||||
|
log: log,
|
||||||
|
listeners: make(map[string][]*eventListener),
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// registerListener provides a means of subscribing to events of type "eventName"
|
||||||
|
func (e *Events) registerListener(eventName string, callback func(...interface{}), counter int) {
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// unRegisterListener provides a means of unsubscribing to events of type "eventName"
|
||||||
|
func (e *Events) unRegisterListener(eventName string) {
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
// Clear the listeners
|
||||||
|
delete(e.listeners, eventName)
|
||||||
|
e.notifyLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// notify for the given event name
|
||||||
|
func (e *Events) notify(eventName string, data ...interface{}) {
|
||||||
|
|
||||||
|
// Get list of event listeners
|
||||||
|
listeners := e.listeners[eventName]
|
||||||
|
if listeners == nil {
|
||||||
|
e.log.Trace("No listeners for event '%s'", eventName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock the listeners
|
||||||
|
e.notifyLock.Lock()
|
||||||
|
|
||||||
|
// 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(data...)
|
||||||
|
|
||||||
|
if listener.counter == 0 {
|
||||||
|
listener.delete = true
|
||||||
|
itemsToDelete = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we have items to delete?
|
||||||
|
if itemsToDelete == true {
|
||||||
|
|
||||||
|
// Create a new Listeners slice
|
||||||
|
var newListeners []*eventListener
|
||||||
|
|
||||||
|
// Iterate over current listeners
|
||||||
|
for _, listener := range listeners {
|
||||||
|
// If we aren't deleting the listener, add it to the new list
|
||||||
|
if !listener.delete {
|
||||||
|
newListeners = append(newListeners, listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save new listeners or remove entry
|
||||||
|
if len(newListeners) > 0 {
|
||||||
|
e.listeners[eventName] = newListeners
|
||||||
|
} else {
|
||||||
|
delete(e.listeners, eventName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock
|
||||||
|
e.notifyLock.Unlock()
|
||||||
|
}
|
14
v2/internal/frontend/runtime/package-lock.json
generated
Normal file
14
v2/internal/frontend/runtime/package-lock.json
generated
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "runtime",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": {
|
||||||
|
"version": "0.12.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.17.tgz",
|
||||||
|
"integrity": "sha512-GshKJyVYUnlSXIZj/NheC2O0Kblh42CS7P1wJyTbbIHevTG4jYMS9NNw8EOd8dDWD0dzydYHS01MpZoUcQXB4g==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
v2/internal/frontend/runtime/package.json
Normal file
15
v2/internal/frontend/runtime/package.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "runtime",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Wails JS Runtime",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build:windows": "esbuild desktop/main.js --bundle --minify --outfile=runtime_windows.js --define:PLATFORM='windows'",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "Lea Anthony <lea.anthony@gmail.com>",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"esbuild": "^0.12.17"
|
||||||
|
}
|
||||||
|
}
|
1
v2/internal/frontend/runtime/runtime_windows.js
Normal file
1
v2/internal/frontend/runtime/runtime_windows.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(()=>{var l=Object.defineProperty;var x=t=>l(t,"__esModule",{value:!0});var h=(t,e)=>{x(t);for(var n in e)l(t,n,{get:e[n],enumerable:!0})};var p={};h(p,{LogDebug:()=>M,LogError:()=>R,LogFatal:()=>I,LogInfo:()=>N,LogLevel:()=>A,LogPrint:()=>y,LogTrace:()=>m,LogWarning:()=>S,SetLogLevel:()=>P});var f=[];function s(t){if(window.chrome.webview.postMessage(t),f.length>0)for(let e=0;e<f.length;e++)f[e](t)}function i(t,e){s("L"+t+e)}function m(t){i("T",t)}function y(t){i("P",t)}function M(t){i("D",t)}function N(t){i("I",t)}function S(t){i("W",t)}function R(t){i("E",t)}function I(t){i("F",t)}function P(t){i("S",t)}var A={TRACE:1,DEBUG:2,INFO:3,WARNING:4,ERROR:5};var u=class{constructor(e,n){n=n||-1,this.Callback=o=>(e.apply(null,o),n===-1?!1:(n-=1,n===0))}},r={};function c(t,e,n){r[t]=r[t]||[];let o=new u(e,n);r[t].push(o)}function E(t,e){c(t,e,-1)}function d(t,e){c(t,e,1)}function g(t){let e=t.name;if(r[e]){let n=r[e].slice();for(let o=0;o<r[e].length;o+=1){let v=r[e][o],O=t.data;v.Callback(O)&&n.splice(o,1)}r[e]=n}}function a(t){let e;try{e=JSON.parse(t)}catch(n){let o="Invalid JSON passed to Notify: "+t;throw new Error(o)}g(e)}function L(t){let e={name:t,data:[].slice.apply(arguments).slice(1)};g(e),s("EE"+JSON.stringify(e))}function w(t){s("EX"+t)}window.backend={};window.runtime={...p,EventsOn:E,EventsOnce:d,EventsOnMultiple:c,EventsEmit:L,EventsOff:w};window.wails={_:{EventsNotify:a}};})();
|
@ -16,6 +16,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Frontend struct {
|
type Frontend struct {
|
||||||
|
|
||||||
|
// Context
|
||||||
|
ctx context.Context
|
||||||
|
|
||||||
frontendOptions *options.App
|
frontendOptions *options.App
|
||||||
logger *logger.Logger
|
logger *logger.Logger
|
||||||
chromium *edge.Chromium
|
chromium *edge.Chromium
|
||||||
@ -30,7 +34,7 @@ type Frontend struct {
|
|||||||
dispatcher frontend.Dispatcher
|
dispatcher frontend.Dispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Frontend) Run() error {
|
func (f *Frontend) Run(ctx context.Context) error {
|
||||||
|
|
||||||
mainWindow := NewWindow(nil, f.frontendOptions)
|
mainWindow := NewWindow(nil, f.frontendOptions)
|
||||||
f.mainWindow = mainWindow
|
f.mainWindow = mainWindow
|
||||||
@ -57,7 +61,7 @@ func (f *Frontend) Run() error {
|
|||||||
|
|
||||||
// TODO: Move this into a callback from frontend
|
// TODO: Move this into a callback from frontend
|
||||||
go func() {
|
go func() {
|
||||||
ctx := context.WithValue(context.Background(), "frontend", f)
|
ctx := context.WithValue(ctx, "frontend", f)
|
||||||
f.frontendOptions.Startup(ctx)
|
f.frontendOptions.Startup(ctx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
38
v2/pkg/runtime/events-x.go
Normal file
38
v2/pkg/runtime/events-x.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// +build experimental
|
||||||
|
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EventsOn registers a listener for the given event name
|
||||||
|
func EventsOn(ctx context.Context, eventName string, callback func(optionalData ...interface{})) {
|
||||||
|
events := getEvents(ctx)
|
||||||
|
events.On(eventName, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOff unregisters a listener for the given event name
|
||||||
|
func EventsOff(ctx context.Context, eventName string) {
|
||||||
|
events := getEvents(ctx)
|
||||||
|
events.Off(eventName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOnce registers a listener for the given event name. After the first callback, the
|
||||||
|
// listener is deleted.
|
||||||
|
func EventsOnce(ctx context.Context, eventName string, callback func(optionalData ...interface{})) {
|
||||||
|
events := getEvents(ctx)
|
||||||
|
events.Once(eventName, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsOnMultiple registers a listener for the given event name, that may be called a maximum of 'counter' times
|
||||||
|
func EventsOnMultiple(ctx context.Context, eventName string, callback func(optionalData ...interface{}), counter int) {
|
||||||
|
events := getEvents(ctx)
|
||||||
|
events.OnMultiple(eventName, callback, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventsEmit pass through
|
||||||
|
func EventsEmit(ctx context.Context, eventName string, optionalData ...interface{}) {
|
||||||
|
events := getEvents(ctx)
|
||||||
|
events.Emit(eventName, optionalData...)
|
||||||
|
}
|
@ -20,8 +20,19 @@ func getFrontend(ctx context.Context) frontend.Frontend {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getEvents(ctx context.Context) frontend.Events {
|
||||||
|
result := ctx.Value("events")
|
||||||
|
if result != nil {
|
||||||
|
return result.(frontend.Events)
|
||||||
|
}
|
||||||
|
pc, _, _, _ := goruntime.Caller(1)
|
||||||
|
funcName := goruntime.FuncForPC(pc).Name()
|
||||||
|
log.Fatalf("cannot call '%s': Application not initialised", funcName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Quit the application
|
// Quit the application
|
||||||
func Quit(ctx context.Context) {
|
func Quit(ctx context.Context) {
|
||||||
frontend := getFrontend(ctx)
|
appFrontend := getFrontend(ctx)
|
||||||
frontend.Quit()
|
appFrontend.Quit()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user