mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 02:50:36 +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/frontend"
|
||||
"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/menumanager"
|
||||
"github.com/wailsapp/wails/v2/internal/signal"
|
||||
@ -26,88 +27,18 @@ type App struct {
|
||||
// Startup/Shutdown
|
||||
startupCallback func(ctx context.Context)
|
||||
shutdownCallback func()
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (a *App) Run() error {
|
||||
|
||||
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()
|
||||
return a.frontend.Run(a.ctx)
|
||||
}
|
||||
|
||||
// CreateApp
|
||||
func CreateApp(appoptions *options.App) (*App, error) {
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Merge default options
|
||||
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
|
||||
bindingExemptions := []interface{}{appoptions.Startup, appoptions.Shutdown}
|
||||
appBindings := binding.NewBindings(myLogger, appoptions.Bind, bindingExemptions)
|
||||
|
||||
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings)
|
||||
eventHandler := runtime.NewEvents(myLogger)
|
||||
ctx = context.WithValue(ctx, "events", eventHandler)
|
||||
messageDispatcher := dispatcher.NewDispatcher(myLogger, appBindings, eventHandler)
|
||||
|
||||
appFrontend := NewFrontend(appoptions, myLogger, appBindings, messageDispatcher)
|
||||
|
||||
result := &App{
|
||||
ctx: ctx,
|
||||
frontend: appFrontend,
|
||||
logger: myLogger,
|
||||
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
|
||||
|
||||
import "github.com/wailsapp/wails/v2/pkg/menu"
|
||||
import (
|
||||
"context"
|
||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||
)
|
||||
|
||||
// FileFilter defines a filter for dialog boxes
|
||||
type FileFilter struct {
|
||||
@ -54,15 +57,9 @@ type MessageDialogOptions struct {
|
||||
}
|
||||
|
||||
type Frontend interface {
|
||||
|
||||
// Main methods
|
||||
Run() error
|
||||
Run(context.Context) error
|
||||
Quit()
|
||||
|
||||
//// Events
|
||||
//NotifyEvent(message string)
|
||||
//CallResult(message string)
|
||||
//
|
||||
// Dialog
|
||||
OpenFileDialog(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
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Trace(message) {
|
||||
export function LogTrace(message) {
|
||||
sendLogMessage('T', message);
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ export function Trace(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Print(message) {
|
||||
export function LogPrint(message) {
|
||||
sendLogMessage('P', message);
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export function Print(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Debug(message) {
|
||||
export function LogDebug(message) {
|
||||
sendLogMessage('D', message);
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ export function Debug(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Info(message) {
|
||||
export function LogInfo(message) {
|
||||
sendLogMessage('I', message);
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ export function Info(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Warning(message) {
|
||||
export function LogWarning(message) {
|
||||
sendLogMessage('W', message);
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ export function Warning(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Error(message) {
|
||||
export function LogError(message) {
|
||||
sendLogMessage('E', message);
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ export function Error(message) {
|
||||
* @export
|
||||
* @param {string} message
|
||||
*/
|
||||
export function Fatal(message) {
|
||||
export function LogFatal(message) {
|
||||
sendLogMessage('F', message);
|
||||
}
|
||||
|
||||
@ -106,7 +106,7 @@ export function SetLogLevel(loglevel) {
|
||||
}
|
||||
|
||||
// Log levels
|
||||
export const Level = {
|
||||
export const LogLevel = {
|
||||
TRACE: 1,
|
||||
DEBUG: 2,
|
||||
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 {
|
||||
|
||||
// Context
|
||||
ctx context.Context
|
||||
|
||||
frontendOptions *options.App
|
||||
logger *logger.Logger
|
||||
chromium *edge.Chromium
|
||||
@ -30,7 +34,7 @@ type Frontend struct {
|
||||
dispatcher frontend.Dispatcher
|
||||
}
|
||||
|
||||
func (f *Frontend) Run() error {
|
||||
func (f *Frontend) Run(ctx context.Context) error {
|
||||
|
||||
mainWindow := NewWindow(nil, f.frontendOptions)
|
||||
f.mainWindow = mainWindow
|
||||
@ -57,7 +61,7 @@ func (f *Frontend) Run() error {
|
||||
|
||||
// TODO: Move this into a callback from frontend
|
||||
go func() {
|
||||
ctx := context.WithValue(context.Background(), "frontend", f)
|
||||
ctx := context.WithValue(ctx, "frontend", f)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
func Quit(ctx context.Context) {
|
||||
frontend := getFrontend(ctx)
|
||||
frontend.Quit()
|
||||
appFrontend := getFrontend(ctx)
|
||||
appFrontend.Quit()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user