5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 18:29:53 +08:00

[v2] [broken - WIP] Major refactor of runtime in progress

This commit is contained in:
Lea Anthony 2021-07-19 20:21:41 +10:00
parent b80a64b0ee
commit f02c140709
28 changed files with 284 additions and 1330 deletions

View File

@ -11,7 +11,6 @@ import (
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/menumanager"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/internal/signal"
"github.com/wailsapp/wails/v2/internal/subsystem"
@ -46,10 +45,6 @@ type App struct {
// This is our binding DB
bindings *binding.Bindings
// Application Stores
loglevelStore *runtime.Store
appconfigStore *runtime.Store
// Startup/Shutdown
startupCallback func(ctx context.Context)
shutdownCallback func()
@ -145,12 +140,8 @@ func (a *App) Run() error {
return err
}
// Application Stores
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
// Start the logging subsystem
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
log, err := subsystem.NewLog(a.servicebus, a.logger)
if err != nil {
return err
}
@ -207,7 +198,7 @@ func (a *App) Run() error {
}
// Start the call subsystem
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB())
if err != nil {
return err
}

View File

@ -14,7 +14,6 @@ import (
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/internal/signal"
"github.com/wailsapp/wails/v2/internal/subsystem"
@ -185,7 +184,7 @@ func (a *App) Run() error {
}
// Start the call subsystem
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB(), a.runtime.GoRuntime())
callSubsystem, err := subsystem.NewCall(ctx, a.servicebus, a.logger, a.bindings.DB())
if err != nil {
return err
}

View File

@ -3,6 +3,7 @@
package app
import (
"context"
"os"
"path/filepath"
@ -12,7 +13,6 @@ import (
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/messagedispatcher"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/internal/subsystem"
"github.com/wailsapp/wails/v2/internal/webserver"
@ -26,7 +26,6 @@ type App struct {
call *subsystem.Call
event *subsystem.Event
log *subsystem.Log
runtime *subsystem.Runtime
options *options.App
@ -38,12 +37,8 @@ type App struct {
debug bool
// Application Stores
loglevelStore *runtime.Store
appconfigStore *runtime.Store
// Startup/Shutdown
startupCallback func(*runtime.Runtime)
startupCallback func(ctx context.Context)
shutdownCallback func()
}
@ -109,19 +104,15 @@ func (a *App) Run() error {
}
// Start the runtime
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback, a.shutdownCallback)
runtime, err := subsystem.NewRuntime(a.servicebus, a.logger, a.startupCallback)
if err != nil {
return err
}
a.runtime = runtime
a.runtime.Start()
// Application Stores
a.loglevelStore = a.runtime.GoRuntime().Store.New("wails:loglevel", a.options.LogLevel)
a.appconfigStore = a.runtime.GoRuntime().Store.New("wails:appconfig", a.options)
a.servicebus.Start()
log, err := subsystem.NewLog(a.servicebus, a.logger, a.loglevelStore)
log, err := subsystem.NewLog(a.servicebus, a.logger)
if err != nil {
return err
}
@ -135,7 +126,7 @@ func (a *App) Run() error {
a.dispatcher.Start()
// Start the binding subsystem
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings, runtime.GoRuntime())
binding, err := subsystem.NewBinding(a.servicebus, a.logger, a.bindings)
if err != nil {
return err
}

View File

@ -1,36 +0,0 @@
package runtime
import (
"fmt"
"os/exec"
"runtime"
)
// Browser defines all browser related operations
type Browser interface {
Open(target string) error
}
type browser struct{}
// Open a url / file using the system default application
// Credit: https://gist.github.com/hyg/9c4afcd91fe24316cbf0
func (b *browser) Open(target string) error {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", target).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", target).Start()
case "darwin":
err = exec.Command("open", target).Start()
default:
err = fmt.Errorf("unsupported platform")
}
return err
}
func newBrowser() *browser {
return &browser{}
}

View File

@ -1,172 +0,0 @@
package runtime
//
//import (
// "fmt"
// "github.com/wailsapp/wails/v2/internal/crypto"
// "github.com/wailsapp/wails/v2/internal/servicebus"
// d "github.com/wailsapp/wails/v2/pkg/runtime/dialog"
//)
//
//// Dialog defines all Dialog related operations
//type Dialog interface {
// OpenFile(options d.OpenDialogOptions) (string, error)
// OpenMultipleFiles(options d.OpenDialogOptions) ([]string, error)
// OpenDirectory(options d.OpenDialogOptions) (string, error)
// SaveFile(options d.SaveDialogOptions) (string, error)
// Message(options d.MessageDialogOptions) (string, error)
//}
//
//// dialog exposes the Dialog interface
//type dialog struct {
// bus *servicebus.ServiceBus
//}
//
//// newDialogs creates a new Dialogs struct
//func newDialog(bus *servicebus.ServiceBus) *dialog {
// return &dialog{
// bus: bus,
// }
//}
//
//// processTitleAndFilter return the title and filter from the given params.
//// title is the first string, filter is the second
//func (r *dialog) processTitleAndFilter(params ...string) (string, string) {
//
// var title, filter string
//
// if len(params) > 0 {
// title = params[0]
// }
//
// if len(params) > 1 {
// filter = params[1]
// }
//
// return title, filter
//}
//
//// OpenDirectory prompts the user to select a directory
//func (r *dialog) OpenDirectory(dialogOptions d.OpenDialogOptions) (string, error) {
//
// // Create unique dialog callback
// uniqueCallback := crypto.RandomID()
//
// // Subscribe to the respose channel
// responseTopic := "dialog:opendirectoryselected:" + uniqueCallback
// dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
// if err != nil {
// return "", fmt.Errorf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
// }
//
// message := "dialog:select:directory:" + uniqueCallback
// r.bus.Publish(message, dialogOptions)
//
// // Wait for result
// var result = <-dialogResponseChannel
//
// // Delete subscription to response topic
// r.bus.UnSubscribe(responseTopic)
//
// return result.Data().(string), nil
//}
//
//// OpenFile prompts the user to select a file
//func (r *dialog) OpenFile(dialogOptions d.OpenDialogOptions) (string, error) {
//
// // Create unique dialog callback
// uniqueCallback := crypto.RandomID()
//
// // Subscribe to the respose channel
// responseTopic := "dialog:openselected:" + uniqueCallback
// dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
// if err != nil {
// return "", fmt.Errorf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
// }
//
// message := "dialog:select:open:" + uniqueCallback
// r.bus.Publish(message, dialogOptions)
//
// // Wait for result
// var result = <-dialogResponseChannel
//
// // Delete subscription to response topic
// r.bus.UnSubscribe(responseTopic)
//
// return result.Data().(string), nil
//}
//
//// OpenMultipleFiles prompts the user to select a file
//func (r *dialog) OpenMultipleFiles(dialogOptions d.OpenDialogOptions) ([]string, error) {
//
// // Create unique dialog callback
// uniqueCallback := crypto.RandomID()
//
// // Subscribe to the respose channel
// responseTopic := "dialog:openmultipleselected:" + uniqueCallback
// dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
// if err != nil {
// return nil, fmt.Errorf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
// }
//
// message := "dialog:select:openmultiple:" + uniqueCallback
// r.bus.Publish(message, dialogOptions)
//
// // Wait for result
// var result = <-dialogResponseChannel
//
// // Delete subscription to response topic
// r.bus.UnSubscribe(responseTopic)
//
// return result.Data().([]string), nil
//}
//
//// SaveFile prompts the user to select a file
//func (r *dialog) SaveFile(dialogOptions d.SaveDialogOptions) (string, error) {
//
// // Create unique dialog callback
// uniqueCallback := crypto.RandomID()
//
// // Subscribe to the respose channel
// responseTopic := "dialog:saveselected:" + uniqueCallback
// dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
// if err != nil {
// return "", fmt.Errorf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
// }
//
// message := "dialog:select:save:" + uniqueCallback
// r.bus.Publish(message, dialogOptions)
//
// // Wait for result
// var result = <-dialogResponseChannel
//
// // Delete subscription to response topic
// r.bus.UnSubscribe(responseTopic)
//
// return result.Data().(string), nil
//}
//
//// Message show a message to the user
//func (r *dialog) Message(dialogOptions d.MessageDialogOptions) (string, error) {
//
// // Create unique dialog callback
// uniqueCallback := crypto.RandomID()
//
// // Subscribe to the respose channel
// responseTopic := "dialog:messageselected:" + uniqueCallback
// dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
// if err != nil {
// return "", fmt.Errorf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
// }
//
// message := "dialog:select:message:" + uniqueCallback
// r.bus.Publish(message, dialogOptions)
//
// // Wait for result
// var result = <-dialogResponseChannel
//
// // Delete subscription to response topic
// r.bus.UnSubscribe(responseTopic)
//
// return result.Data().(string), nil
//}

View File

@ -1,86 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// Events defines all events related operations
type Events interface {
On(eventName string, callback func(optionalData ...interface{}))
Once(eventName string, callback func(optionalData ...interface{}))
OnMultiple(eventName string, callback func(optionalData ...interface{}), maxCallbacks int)
Emit(eventName string, optionalData ...interface{})
OnThemeChange(callback func(darkMode bool))
}
// event exposes the events interface
type event struct {
bus *servicebus.ServiceBus
}
// newEvents creates a new Events struct
func newEvents(bus *servicebus.ServiceBus) Events {
return &event{
bus: bus,
}
}
// On registers a listener for a particular event
func (r *event) On(eventName string, callback func(optionalData ...interface{})) {
eventMessage := &message.OnEventMessage{
Name: eventName,
Callback: callback,
Counter: -1,
}
r.bus.Publish("event:on", eventMessage)
}
// Once registers a listener for a particular event. After the first callback, the
// listener is deleted.
func (r *event) Once(eventName string, callback func(optionalData ...interface{})) {
eventMessage := &message.OnEventMessage{
Name: eventName,
Callback: callback,
Counter: 1,
}
r.bus.Publish("event:on", eventMessage)
}
// OnMultiple registers a listener for a particular event, for a given maximum amount of callbacks.
// Once the callback has been run `maxCallbacks` times, the listener is deleted.
func (r *event) OnMultiple(eventName string, callback func(optionalData ...interface{}), maxCallbacks int) {
eventMessage := &message.OnEventMessage{
Name: eventName,
Callback: callback,
Counter: maxCallbacks,
}
r.bus.Publish("event:on", eventMessage)
}
// Emit pass through
func (r *event) Emit(eventName string, optionalData ...interface{}) {
eventMessage := &message.EventMessage{
Name: eventName,
Data: optionalData,
}
r.bus.Publish("event:emit:from:g", eventMessage)
}
// OnThemeChange allows you to register callbacks when the system theme changes
// from light or dark.
func (r *event) OnThemeChange(callback func(darkMode bool)) {
r.On("wails:system:themechange", func(data ...interface{}) {
if len(data) != 1 {
// TODO: Log error
return
}
darkMode, ok := data[0].(bool)
if !ok {
// TODO: Log error
return
}
callback(darkMode)
})
}

View File

@ -1,69 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/logger"
)
// Log defines all Log related operations
type Log interface {
Print(message string)
Trace(message string)
Debug(message string)
Info(message string)
Warning(message string)
Error(message string)
Fatal(message string)
SetLogLevel(level logger.LogLevel)
}
type log struct {
bus *servicebus.ServiceBus
}
// newLog creates a new Log struct
func newLog(bus *servicebus.ServiceBus) Log {
return &log{
bus: bus,
}
}
// Print prints a Print level message
func (r *log) Print(message string) {
r.bus.Publish("log:print", message)
}
// Trace prints a Trace level message
func (r *log) Trace(message string) {
r.bus.Publish("log:trace", message)
}
// Debug prints a Debug level message
func (r *log) Debug(message string) {
r.bus.Publish("log:debug", message)
}
// Info prints a Info level message
func (r *log) Info(message string) {
r.bus.Publish("log:info", message)
}
// Warning prints a Warning level message
func (r *log) Warning(message string) {
r.bus.Publish("log:warning", message)
}
// Error prints a Error level message
func (r *log) Error(message string) {
r.bus.Publish("log:error", message)
}
// Fatal prints a Fatal level message
func (r *log) Fatal(message string) {
r.bus.Publish("log:fatal", message)
}
// Sets the log level
func (r *log) SetLogLevel(level logger.LogLevel) {
r.bus.Publish("log:setlevel", level)
}

View File

@ -1,46 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
// Menu defines all Menu related operations
type Menu interface {
UpdateApplicationMenu()
UpdateContextMenu(contextMenu *menu.ContextMenu)
SetTrayMenu(trayMenu *menu.TrayMenu)
DeleteTrayMenu(trayMenu *menu.TrayMenu)
UpdateTrayMenuLabel(trayMenu *menu.TrayMenu)
}
type menuRuntime struct {
bus *servicebus.ServiceBus
}
// newMenu creates a new Menu struct
func newMenu(bus *servicebus.ServiceBus) Menu {
return &menuRuntime{
bus: bus,
}
}
func (m *menuRuntime) UpdateApplicationMenu() {
m.bus.Publish("menu:updateappmenu", nil)
}
func (m *menuRuntime) UpdateContextMenu(contextMenu *menu.ContextMenu) {
m.bus.Publish("menu:updatecontextmenu", contextMenu)
}
func (m *menuRuntime) SetTrayMenu(trayMenu *menu.TrayMenu) {
m.bus.Publish("menu:settraymenu", trayMenu)
}
func (m *menuRuntime) UpdateTrayMenuLabel(trayMenu *menu.TrayMenu) {
m.bus.Publish("menu:updatetraymenulabel", trayMenu)
}
func (m *menuRuntime) DeleteTrayMenu(trayMenu *menu.TrayMenu) {
m.bus.Publish("menu:deletetraymenu", trayMenu)
}

View File

@ -1,38 +0,0 @@
package runtime
import (
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// Runtime is a means for the user to interact with the application at runtime
type Runtime struct {
Browser Browser
Events Events
Window Window
System System
Menu Menu
Store *StoreProvider
Log Log
bus *servicebus.ServiceBus
}
// New creates a new runtime
func New(serviceBus *servicebus.ServiceBus) *Runtime {
result := &Runtime{
Browser: newBrowser(),
Events: newEvents(serviceBus),
Window: newWindow(serviceBus),
System: newSystem(serviceBus),
Menu: newMenu(serviceBus),
Log: newLog(serviceBus),
bus: serviceBus,
}
result.Store = newStore(result)
return result
}
// Quit the application
func (r *Runtime) Quit() {
// Start shutdown of Wails
r.bus.Publish("quit", "runtime.Quit()")
}

View File

@ -1,367 +0,0 @@
// package runtime contains all the methods and data structures related to the
// runtime library of Wails. This includes both Go and JS runtimes.
package runtime
import (
"bytes"
"encoding/json"
"fmt"
golog "log"
"os"
"reflect"
"sync"
"github.com/wailsapp/wails/v2/internal/deepcopy"
)
// Options defines the optional data that may be used
// when creating a Store
type Options struct {
// The name of the store
Name string
// The runtime to attach the store to
Runtime *Runtime
// Indicates if notifying Go listeners should be notified of updates
// synchronously (on the current thread) or asynchronously using
// goroutines
NotifySynchronously bool
}
// StoreProvider is a struct that creates Stores
type StoreProvider struct {
runtime *Runtime
}
// newStore creates new stores using the provided Runtime reference.
func newStore(runtime *Runtime) *StoreProvider {
return &StoreProvider{
runtime: runtime,
}
}
// Store is where we keep named data
type Store struct {
name string
data reflect.Value
dataType reflect.Type
eventPrefix string
callbacks []reflect.Value
runtime *Runtime
notifySynchronously bool
// Lock
mux sync.Mutex
// Error handler
errorHandler func(error)
}
func fatal(err error) {
println(err.Error())
os.Exit(1)
}
// New creates a new store
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
if defaultValue == nil {
golog.Fatal("Cannot initialise a store with nil")
}
result := Store{
name: name,
runtime: p.runtime,
}
// Setup the sync listener
result.setupListener()
result.Set(defaultValue)
return &result
}
func (s *Store) lock() {
s.mux.Lock()
}
func (s *Store) unlock() {
s.mux.Unlock()
}
// OnError takes a function that will be called
// whenever an error occurs
func (s *Store) OnError(callback func(error)) {
s.errorHandler = callback
}
// Processes the updates sent by the front end
func (s *Store) processUpdatedData(data string) error {
// Decode incoming data
var rawdata json.RawMessage
d := json.NewDecoder(bytes.NewBufferString(data))
err := d.Decode(&rawdata)
if err != nil {
return err
}
// Create a new instance of our data and unmarshal
// the received value into it
newData := reflect.New(s.dataType).Interface()
err = json.Unmarshal(rawdata, &newData)
if err != nil {
return err
}
// Lock mutex for writing
s.lock()
// Handle nulls
if newData == nil {
s.data = reflect.Zero(s.dataType)
} else {
// Store the resultant value in the data store
s.data = reflect.ValueOf(newData).Elem()
}
// Unlock mutex
s.unlock()
return nil
}
// Setup listener for front end changes
func (s *Store) setupListener() {
// Listen for updates from the front end
s.runtime.Events.On("wails:sync:store:updatedbyfrontend:"+s.name, func(data ...interface{}) {
// Process the incoming data
err := s.processUpdatedData(data[0].(string))
if err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
return
}
}
// Notify listeners
s.notify()
})
// Listen for resync events
s.runtime.Events.On("wails:sync:store:resync:"+s.name, func(data ...interface{}) {
// Resetting the curent data will resync
s.resync()
})
// Do initial resync
s.resync()
}
func (s *Store) resync() {
// Lock
s.lock()
defer s.unlock()
var result string
if s.data.IsValid() {
rawdata, err := json.Marshal(s.data.Interface())
if err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
return
}
}
result = string(rawdata)
} else {
result = "{}"
}
// Emit event to front end
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, result)
// Notify subscribers
s.notify()
}
// notify the listeners of the current data state
func (s *Store) notify() {
// Execute callbacks
for _, callback := range s.callbacks {
// Build args
s.lock()
args := []reflect.Value{s.data}
s.unlock()
if s.notifySynchronously {
callback.Call(args)
} else {
go callback.Call(args)
}
}
}
// Set will update the data held by the store
// and notify listeners of the change
func (s *Store) Set(data interface{}) error {
if data == nil {
return fmt.Errorf("cannot set store to nil")
}
s.lock()
dataCopy := deepcopy.Copy(data)
if dataCopy != nil {
inType := reflect.TypeOf(dataCopy)
if inType != s.dataType && s.data.IsValid() {
s.unlock()
return fmt.Errorf("invalid data given in Store.Set(). Expected %s, got %s", s.dataType.String(), inType.String())
}
}
if s.dataType == nil {
s.dataType = reflect.TypeOf(dataCopy)
}
// Save data
s.data = reflect.ValueOf(dataCopy)
s.unlock()
// Resync with subscribers
s.resync()
return nil
}
// callbackCheck ensures the given function to Subscribe() is
// of the correct signature. Absolutely cannot wait for
// generics to land rather than writing this nonsense.
func (s *Store) callbackCheck(callback interface{}) error {
// Get type
callbackType := reflect.TypeOf(callback)
// Check callback is a function
if callbackType.Kind() != reflect.Func {
return fmt.Errorf("invalid value given to store.Subscribe(). Expected 'func(%s)'", s.dataType.String())
}
// Check input param
if callbackType.NumIn() != 1 {
return fmt.Errorf("invalid number of parameters given in callback function. Expected 1")
}
// Check input data type
if callbackType.In(0) != s.dataType {
return fmt.Errorf("invalid type for input parameter given in callback function. Expected %s, got %s", s.dataType.String(), callbackType.In(0))
}
// Check output param
if callbackType.NumOut() != 0 {
return fmt.Errorf("invalid number of return parameters given in callback function. Expected 0")
}
return nil
}
// Subscribe will subscribe to updates to the store by
// providing a callback. Any updates to the store are sent
// to the callback
func (s *Store) Subscribe(callback interface{}) {
err := s.callbackCheck(callback)
if err != nil {
fatal(err)
}
callbackFunc := reflect.ValueOf(callback)
s.lock()
s.callbacks = append(s.callbacks, callbackFunc)
s.unlock()
}
// updaterCheck ensures the given function to Update() is
// of the correct signature. Absolutely cannot wait for
// generics to land rather than writing this nonsense.
func (s *Store) updaterCheck(updater interface{}) error {
// Get type
updaterType := reflect.TypeOf(updater)
// Check updater is a function
if updaterType.Kind() != reflect.Func {
return fmt.Errorf("invalid value given to store.Update(). Expected 'func(%s) %s'", s.dataType.String(), s.dataType.String())
}
// Check input param
if updaterType.NumIn() != 1 {
return fmt.Errorf("invalid number of parameters given in updater function. Expected 1")
}
// Check input data type
if updaterType.In(0) != s.dataType {
return fmt.Errorf("invalid type for input parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.In(0))
}
// Check output param
if updaterType.NumOut() != 1 {
return fmt.Errorf("invalid number of return parameters given in updater function. Expected 1")
}
// Check output data type
if updaterType.Out(0) != s.dataType {
return fmt.Errorf("invalid type for return parameter given in updater function. Expected %s, got %s", s.dataType.String(), updaterType.Out(0))
}
return nil
}
// Update takes a function that is passed the current state.
// The result of that function is then set as the new state
// of the store. This will notify listeners of the change
func (s *Store) Update(updater interface{}) {
err := s.updaterCheck(updater)
if err != nil {
fatal(err)
}
// Build args
s.lock()
args := []reflect.Value{s.data}
s.unlock()
// Make call
results := reflect.ValueOf(updater).Call(args)
// We will only have 1 result. Set the store to it
s.Set(results[0].Interface())
}
// Get returns the value of the data that's kept in the current state / Store
func (s *Store) Get() interface{} {
s.lock()
defer s.unlock()
if !s.data.IsValid() {
return nil
}
return s.data.Interface()
}

View File

@ -1,165 +0,0 @@
package runtime
import (
"context"
"math/rand"
"sync"
"testing"
"time"
internallogger "github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/logger"
is2 "github.com/matryer/is"
)
func TestStoreProvider_NewWithNilDefault(t *testing.T) {
is := is2.New(t)
defaultLogger := logger.NewDefaultLogger()
testLogger := internallogger.New(defaultLogger)
//testLogger.SetLogLevel(logger.TRACE)
serviceBus := servicebus.New(testLogger)
err := serviceBus.Start()
is.NoErr(err)
defer serviceBus.Stop()
testRuntime := New(serviceBus)
storeProvider := newStore(testRuntime)
testStore := storeProvider.New("test", 0)
// You should be able to write a new value into a
// store initialised with nil
err = testStore.Set(100)
is.NoErr(err)
// You shouldn't be able to write different types to the
// store
err = testStore.Set(false)
is.True(err != nil)
}
func TestStoreProvider_NewWithScalarDefault(t *testing.T) {
is := is2.New(t)
defaultLogger := logger.NewDefaultLogger()
testLogger := internallogger.New(defaultLogger)
//testLogger.SetLogLevel(logger.TRACE)
serviceBus := servicebus.New(testLogger)
err := serviceBus.Start()
is.NoErr(err)
defer serviceBus.Stop()
testRuntime := New(serviceBus)
storeProvider := newStore(testRuntime)
testStore := storeProvider.New("test", 100)
value := testStore.Get()
is.Equal(value, 100)
testStore.resync()
value = testStore.Get()
is.Equal(value, 100)
}
func TestStoreProvider_NewWithStructDefault(t *testing.T) {
is := is2.New(t)
defaultLogger := logger.NewDefaultLogger()
testLogger := internallogger.New(defaultLogger)
//testLogger.SetLogLevel(logger.TRACE)
serviceBus := servicebus.New(testLogger)
err := serviceBus.Start()
is.NoErr(err)
defer serviceBus.Stop()
testRuntime := New(serviceBus)
storeProvider := newStore(testRuntime)
type TestValue struct {
Name string
}
testValue := &TestValue{
Name: "hi",
}
testStore := storeProvider.New("test", testValue)
err = testStore.Set(testValue)
is.NoErr(err)
testStore.resync()
value := testStore.Get()
is.Equal(value, testValue)
is.Equal(value.(*TestValue).Name, "hi")
testValue = &TestValue{
Name: "there",
}
err = testStore.Set(testValue)
is.NoErr(err)
testStore.resync()
value = testStore.Get()
is.Equal(value, testValue)
is.Equal(value.(*TestValue).Name, "there")
}
func TestStoreProvider_RapidReadWrite(t *testing.T) {
is := is2.New(t)
defaultLogger := logger.NewDefaultLogger()
testLogger := internallogger.New(defaultLogger)
//testLogger.SetLogLevel(logger.TRACE)
serviceBus := servicebus.New(testLogger)
err := serviceBus.Start()
is.NoErr(err)
defer serviceBus.Stop()
testRuntime := New(serviceBus)
storeProvider := newStore(testRuntime)
testStore := storeProvider.New("test", 1)
ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)
var wg sync.WaitGroup
readers := 100
writers := 100
wg.Add(readers + writers)
// Setup readers
go func(testStore *Store, ctx context.Context) {
for readerCount := 0; readerCount < readers; readerCount++ {
go func(store *Store, ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
wg.Done()
return
default:
store.Get()
}
}
}(testStore, ctx, readerCount)
}
}(testStore, ctx)
// Setup writers
go func(testStore *Store, ctx context.Context) {
for writerCount := 0; writerCount < writers; writerCount++ {
go func(store *Store, ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
wg.Done()
return
default:
err := store.Set(rand.Int())
is.NoErr(err)
}
}
}(testStore, ctx, writerCount)
}
}(testStore, ctx)
wg.Wait()
}

View File

@ -1,60 +0,0 @@
package runtime
import (
"fmt"
"runtime"
"github.com/wailsapp/wails/v2/internal/crypto"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// System defines all System related operations
type System interface {
IsDarkMode() bool
Platform() string
AppType() string
}
// system exposes the System interface
type system struct {
bus *servicebus.ServiceBus
}
// newSystem creates a new System struct
func newSystem(bus *servicebus.ServiceBus) System {
return &system{
bus: bus,
}
}
// Platform returns the platform name the application
// is running on
func (r *system) Platform() string {
return runtime.GOOS
}
// On pass through
func (r *system) IsDarkMode() bool {
// Create unique system callback
uniqueCallback := crypto.RandomID()
// Subscribe to the respose channel
responseTopic := "systemresponse:" + uniqueCallback
systemResponseChannel, err := r.bus.Subscribe(responseTopic)
if err != nil {
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
return false
}
message := "system:isdarkmode:" + uniqueCallback
r.bus.Publish(message, nil)
// Wait for result
var result *servicebus.Message = <-systemResponseChannel
// Delete subscription to response topic
r.bus.UnSubscribe(responseTopic)
return result.Data().(bool)
}

View File

@ -1,8 +0,0 @@
// +build !desktop,!server
package runtime
// AppType returns the application type, EG: desktop
func (r *system) AppType() string {
return "default"
}

View File

@ -1,8 +0,0 @@
// +build desktop,!server
package runtime
// AppType returns the application type, EG: desktop
func (r *system) AppType() string {
return "desktop"
}

View File

@ -1,8 +0,0 @@
// +build server
package runtime
// AppType returns the application type, EG: desktop
func (r *system) AppType() string {
return "server"
}

View File

@ -1,120 +0,0 @@
package runtime
import (
"fmt"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// Window defines all Window related operations
type Window interface {
Close()
Center()
Show()
Hide()
Maximise()
Unmaximise()
Minimise()
Unminimise()
SetTitle(title string)
SetSize(width int, height int)
SetMinSize(width int, height int)
SetMaxSize(width int, height int)
SetPosition(x int, y int)
Fullscreen()
UnFullscreen()
}
// Window exposes the Windows interface
type window struct {
bus *servicebus.ServiceBus
}
// newWindow creates a new window struct
func newWindow(bus *servicebus.ServiceBus) Window {
return &window{
bus: bus,
}
}
// Close the Window
// DISCUSSION:
// Should we even be doing this now we have a server build?
// Runtime.Quit() makes more sense than closing a window...
func (w *window) Close() {
w.bus.Publish("quit", "runtime.Close()")
}
// SetTitle sets the title of the window
func (w *window) SetTitle(title string) {
w.bus.Publish("window:settitle", title)
}
// Fullscreen makes the window fullscreen
func (w *window) Fullscreen() {
w.bus.Publish("window:fullscreen", "")
}
// UnFullscreen makes the window UnFullscreen
func (w *window) UnFullscreen() {
w.bus.Publish("window:unfullscreen", "")
}
// Center the window on the current screen
func (w *window) Center() {
w.bus.Publish("window:center", "")
}
// Show shows the window if hidden
func (w *window) Show() {
w.bus.Publish("window:show", "")
}
// Hide the window
func (w *window) Hide() {
w.bus.Publish("window:hide", "")
}
// SetSize sets the size of the window
func (w *window) SetSize(width int, height int) {
message := fmt.Sprintf("window:size:%d:%d", width, height)
w.bus.Publish(message, "")
}
// SetSize sets the size of the window
func (w *window) SetMinSize(width int, height int) {
message := fmt.Sprintf("window:minsize:%d:%d", width, height)
w.bus.Publish(message, "")
}
// SetSize sets the size of the window
func (w *window) SetMaxSize(width int, height int) {
message := fmt.Sprintf("window:maxsize:%d:%d", width, height)
w.bus.Publish(message, "")
}
// SetPosition sets the position of the window
func (w *window) SetPosition(x int, y int) {
message := fmt.Sprintf("window:position:%d:%d", x, y)
w.bus.Publish(message, "")
}
// Maximise the window
func (w *window) Maximise() {
w.bus.Publish("window:maximise", "")
}
// Unmaximise the window
func (w *window) Unmaximise() {
w.bus.Publish("window:unmaximise", "")
}
// Minimise the window
func (w *window) Minimise() {
w.bus.Publish("window:minimise", "")
}
// Unminimise the window
func (w *window) Unminimise() {
w.bus.Publish("window:unminimise", "")
}

View File

@ -0,0 +1,17 @@
package servicebus
import (
"context"
"log"
"runtime"
)
func ExtractBus(ctx context.Context) *ServiceBus {
bus := ctx.Value("bus")
if bus == nil {
pc, _, _, _ := runtime.Caller(1)
funcName := runtime.FuncForPC(pc).Name()
log.Fatalf("cannot call '%s': Application not initialised", funcName)
}
return bus.(*ServiceBus)
}

View File

@ -3,7 +3,6 @@ package subsystem
import (
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
@ -19,13 +18,10 @@ type Binding struct {
// logger
logger logger.CustomLogger
// runtime
runtime *runtime.Runtime
}
// NewBinding creates a new binding subsystem. Uses the given bindings db for reference.
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings, runtime *runtime.Runtime) (*Binding, error) {
func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *binding.Bindings) (*Binding, error) {
// Subscribe to event messages
bindingChannel, err := bus.Subscribe("binding")
@ -37,7 +33,6 @@ func NewBinding(bus *servicebus.ServiceBus, logger *logger.Logger, bindings *bin
bindingChannel: bindingChannel,
logger: logger.CustomLogger("Binding Subsystem"),
bindings: bindings,
runtime: runtime,
}
return result, nil

View File

@ -11,7 +11,6 @@ import (
"github.com/wailsapp/wails/v2/internal/binding"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
@ -32,9 +31,6 @@ type Call struct {
// logger
logger logger.CustomLogger
// runtime
runtime *runtime.Runtime
// context
ctx context.Context
@ -43,7 +39,7 @@ type Call struct {
}
// NewCall creates a new call subsystem
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB, runtime *runtime.Runtime) (*Call, error) {
func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Logger, DB *binding.DB) (*Call, error) {
// Subscribe to event messages
callChannel, err := bus.Subscribe("call:invoke")
@ -56,7 +52,6 @@ func NewCall(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.Log
logger: logger.CustomLogger("Call Subsystem"),
DB: DB,
bus: bus,
runtime: runtime,
ctx: ctx,
wg: ctx.Value("waitgroup").(*sync.WaitGroup),
}
@ -130,12 +125,9 @@ func (c *Call) processSystemCall(payload *message.CallMessage, clientID string)
c.logger.Trace("Got internal System call: %+v", payload)
callName := strings.TrimPrefix(payload.Name, ".wails.")
switch callName {
case "IsDarkMode":
darkModeEnabled := c.runtime.System.IsDarkMode()
c.sendResult(darkModeEnabled, payload, clientID)
case "Dialog.Open":
var dialogOptions dialog.OpenDialogOptions
err := json.Unmarshal(payload.Args[0], dialogOptions)
err := json.Unmarshal(payload.Args[0], &dialogOptions)
if err != nil {
c.logger.Error("Error decoding: %s", err)
}
@ -146,7 +138,7 @@ func (c *Call) processSystemCall(payload *message.CallMessage, clientID string)
c.sendResult(result, payload, clientID)
case "Dialog.Save":
var dialogOptions dialog.SaveDialogOptions
err := json.Unmarshal(payload.Args[0], dialogOptions)
err := json.Unmarshal(payload.Args[0], &dialogOptions)
if err != nil {
c.logger.Error("Error decoding: %s", err)
}
@ -157,7 +149,7 @@ func (c *Call) processSystemCall(payload *message.CallMessage, clientID string)
c.sendResult(result, payload, clientID)
case "Dialog.Message":
var dialogOptions dialog.MessageDialogOptions
err := json.Unmarshal(payload.Args[0], dialogOptions)
err := json.Unmarshal(payload.Args[0], &dialogOptions)
if err != nil {
c.logger.Error("Error decoding: %s", err)
}

View File

@ -7,7 +7,6 @@ import (
"sync"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
@ -22,9 +21,6 @@ type Log struct {
// Logger!
logger *logger.Logger
// Loglevel store
logLevelStore *runtime.Store
// Context for shutdown
ctx context.Context
cancel context.CancelFunc
@ -34,7 +30,7 @@ type Log struct {
}
// NewLog creates a new log subsystem
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *runtime.Store) (*Log, error) {
func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger) (*Log, error) {
// Subscribe to log messages
logChannel, err := bus.Subscribe("log")
@ -45,11 +41,10 @@ func NewLog(bus *servicebus.ServiceBus, logger *logger.Logger, logLevelStore *ru
ctx, cancel := context.WithCancel(context.Background())
result := &Log{
logChannel: logChannel,
logger: logger,
logLevelStore: logLevelStore,
ctx: ctx,
cancel: cancel,
logChannel: logChannel,
logger: logger,
ctx: ctx,
cancel: cancel,
}
return result, nil
@ -90,7 +85,6 @@ func (l *Log) Start() error {
switch inLevel := logMessage.Data().(type) {
case logger.LogLevel:
l.logger.SetLogLevel(inLevel)
l.logLevelStore.Set(inLevel)
case string:
uint64level, err := strconv.ParseUint(inLevel, 10, 8)
if err != nil {
@ -98,7 +92,6 @@ func (l *Log) Start() error {
continue
}
level := logger.LogLevel(uint64level)
l.logLevelStore.Set(level)
l.logger.SetLogLevel(level)
}

View File

@ -4,7 +4,6 @@ import (
"context"
"fmt"
"github.com/wailsapp/wails/v2/internal/logger"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/internal/servicebus"
"strings"
"sync"
@ -25,9 +24,6 @@ type Runtime struct {
logger logger.CustomLogger
// Runtime library
runtime *runtime.Runtime
//ctx
ctx context.Context
@ -57,7 +53,6 @@ func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.
runtimeChannel: runtimeChannel,
hooksChannel: hooksChannel,
logger: logger.CustomLogger("Runtime Subsystem"),
runtime: runtime.New(bus),
startupCallback: startupCallback,
bus: bus,
}
@ -98,30 +93,6 @@ func (r *Runtime) Start() error {
r.logger.Error("unknown hook message: %+v", hooksMessage)
continue
}
case runtimeMessage := <-r.runtimeChannel:
r.logger.Trace(fmt.Sprintf("Received message: %+v", runtimeMessage))
// Topics have the format: "runtime:category:call"
messageSlice := strings.Split(runtimeMessage.Topic(), ":")
if len(messageSlice) != 3 {
r.logger.Error("Invalid runtime message: %#v\n", runtimeMessage)
continue
}
category := messageSlice[1]
method := messageSlice[2]
var err error
switch category {
case "browser":
err = r.processBrowserMessage(method, runtimeMessage.Data())
default:
err = fmt.Errorf("unknown runtime message: %+v",
runtimeMessage)
}
// If we had an error, log it
if err != nil {
r.logger.Error(err.Error())
}
case <-r.ctx.Done():
return
}
@ -130,27 +101,3 @@ func (r *Runtime) Start() error {
return nil
}
// GoRuntime returns the Go Runtime object
func (r *Runtime) GoRuntime() *runtime.Runtime {
return r.runtime
}
func (r *Runtime) processBrowserMessage(method string, data interface{}) error {
switch method {
case "open":
target, ok := data.(string)
if !ok {
return fmt.Errorf("expected 1 string parameter for runtime:browser:open")
}
go func() {
err := r.runtime.Browser.Open(target)
if err != nil {
r.logger.Error(err.Error())
}
}()
default:
return fmt.Errorf("unknown method runtime:browser:%s", method)
}
return nil
}

View File

@ -0,0 +1,43 @@
package events
import (
"context"
"github.com/wailsapp/wails/v2/internal/messagedispatcher/message"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// On registers a listener for a particular event
func On(ctx context.Context, eventName string, callback func(optionalData ...interface{})) {
bus := servicebus.ExtractBus(ctx)
eventMessage := &message.OnEventMessage{
Name: eventName,
Callback: callback,
Counter: -1,
}
bus.Publish("event:on", eventMessage)
}
// Once registers a listener for a particular event. After the first callback, the
// listener is deleted.
func Once(ctx context.Context, eventName string, callback func(optionalData ...interface{})) {
bus := servicebus.ExtractBus(ctx)
eventMessage := &message.OnEventMessage{
Name: eventName,
Callback: callback,
Counter: 1,
}
bus.Publish("event:on", eventMessage)
}
// Emit pass through
func Emit(ctx context.Context, eventName string, optionalData ...interface{}) {
bus := servicebus.ExtractBus(ctx)
eventMessage := &message.EventMessage{
Name: eventName,
Data: optionalData,
}
bus.Publish("event:emit:from:g", eventMessage)
}

View File

@ -3,9 +3,9 @@ package dialog
import (
"context"
"fmt"
"github.com/pkg/errors"
"github.com/wailsapp/wails/v2/internal/crypto"
"github.com/wailsapp/wails/v2/internal/servicebus"
"log"
)
// FileFilter defines a filter for dialog boxes
@ -59,14 +59,6 @@ type MessageDialogOptions struct {
Icon string
}
func extractBus(ctx context.Context) (*servicebus.ServiceBus, error) {
bus := ctx.Value("bus")
if bus == nil {
return nil, errors.New("wails runtime has not been initialised correctly")
}
return bus.(*servicebus.ServiceBus), nil
}
// processTitleAndFilter return the title and filter from the given params.
// title is the first string, filter is the second
func processTitleAndFilter(params ...string) (string, string) {
@ -84,13 +76,14 @@ func processTitleAndFilter(params ...string) (string, string) {
return title, filter
}
func fatal(caller string) {
log.Fatalf("cannot call '%s': Application not initialised", caller)
}
// OpenDirectory prompts the user to select a directory
func OpenDirectory(ctx context.Context, dialogOptions OpenDialogOptions) (string, error) {
bus, err := extractBus(ctx)
if err != nil {
return "", errors.Wrap(err, "OpenDirectory")
}
bus := servicebus.ExtractBus(ctx)
// Create unique dialog callback
uniqueCallback := crypto.RandomID()
@ -117,10 +110,7 @@ func OpenDirectory(ctx context.Context, dialogOptions OpenDialogOptions) (string
// OpenFile prompts the user to select a file
func OpenFile(ctx context.Context, dialogOptions OpenDialogOptions) (string, error) {
bus, err := extractBus(ctx)
if err != nil {
return "", errors.Wrap(err, "OpenFile")
}
bus := servicebus.ExtractBus(ctx)
// Create unique dialog callback
uniqueCallback := crypto.RandomID()
@ -147,12 +137,7 @@ func OpenFile(ctx context.Context, dialogOptions OpenDialogOptions) (string, err
// OpenMultipleFiles prompts the user to select a file
func OpenMultipleFiles(ctx context.Context, dialogOptions OpenDialogOptions) ([]string, error) {
bus, err := extractBus(ctx)
if err != nil {
return nil, errors.Wrap(err, "OpenMultipleFiles")
}
// Create unique dialog callback
bus := servicebus.ExtractBus(ctx)
uniqueCallback := crypto.RandomID()
// Subscribe to the respose channel
@ -177,12 +162,7 @@ func OpenMultipleFiles(ctx context.Context, dialogOptions OpenDialogOptions) ([]
// SaveFile prompts the user to select a file
func SaveFile(ctx context.Context, dialogOptions SaveDialogOptions) (string, error) {
bus, err := extractBus(ctx)
if err != nil {
return "", errors.Wrap(err, "SaveFile")
}
// Create unique dialog callback
bus := servicebus.ExtractBus(ctx)
uniqueCallback := crypto.RandomID()
// Subscribe to the respose channel
@ -207,10 +187,7 @@ func SaveFile(ctx context.Context, dialogOptions SaveDialogOptions) (string, err
// Message show a message to the user
func Message(ctx context.Context, dialogOptions MessageDialogOptions) (string, error) {
bus, err := extractBus(ctx)
if err != nil {
return "", errors.Wrap(err, "Message")
}
bus := servicebus.ExtractBus(ctx)
// Create unique dialog callback
uniqueCallback := crypto.RandomID()

55
v2/pkg/runtime/log/log.go Normal file
View File

@ -0,0 +1,55 @@
package log
import (
"context"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/logger"
)
// Print prints a Print level message
func Print(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:print", message)
}
// Trace prints a Trace level message
func Trace(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:trace", message)
}
// Debug prints a Debug level message
func Debug(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:debug", message)
}
// Info prints a Info level message
func Info(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:info", message)
}
// Warning prints a Warning level message
func Warning(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:warning", message)
}
// Error prints a Error level message
func Error(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:error", message)
}
// Fatal prints a Fatal level message
func Fatal(ctx context.Context, message string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:fatal", message)
}
// SetLogLevel sets the log level
func SetLogLevel(ctx context.Context, level logger.LogLevel) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("log:setlevel", level)
}

View File

@ -0,0 +1,32 @@
package menu
import (
"context"
"github.com/wailsapp/wails/v2/internal/servicebus"
"github.com/wailsapp/wails/v2/pkg/menu"
)
func UpdateApplicationMenu(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("menu:updateappmenu", nil)
}
func UpdateContextMenu(ctx context.Context, contextMenu *menu.ContextMenu) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("menu:updatecontextmenu", contextMenu)
}
func SetTrayMenu(ctx context.Context, trayMenu *menu.TrayMenu) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("menu:settraymenu", trayMenu)
}
func UpdateTrayMenuLabel(ctx context.Context, trayMenu *menu.TrayMenu) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("menu:updatetraymenulabel", trayMenu)
}
func DeleteTrayMenu(ctx context.Context, trayMenu *menu.TrayMenu) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("menu:deletetraymenu", trayMenu)
}

13
v2/pkg/runtime/runtime.go Normal file
View File

@ -0,0 +1,13 @@
package runtime
import (
"context"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// Quit the application
func Quit(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
// Start shutdown of Wails
bus.Publish("quit", "runtime.Quit()")
}

View File

@ -0,0 +1,96 @@
package window
import (
"context"
"fmt"
"github.com/wailsapp/wails/v2/internal/servicebus"
)
// SetTitle sets the title of the window
func SetTitle(ctx context.Context, title string) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:settitle", title)
}
// Fullscreen makes the window fullscreen
func Fullscreen(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:fullscreen", "")
}
// UnFullscreen makes the window UnFullscreen
func UnFullscreen(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:unfullscreen", "")
}
// Center the window on the current screen
func Center(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:center", "")
}
// Show shows the window if hidden
func Show(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:show", "")
}
// Hide the window
func Hide(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:hide", "")
}
// SetSize sets the size of the window
func SetSize(ctx context.Context, width int, height int) {
bus := servicebus.ExtractBus(ctx)
message := fmt.Sprintf("window:size:%d:%d", width, height)
bus.Publish(message, "")
}
// SetSize sets the size of the window
func SetMinSize(ctx context.Context, width int, height int) {
bus := servicebus.ExtractBus(ctx)
message := fmt.Sprintf("window:minsize:%d:%d", width, height)
bus.Publish(message, "")
}
// SetSize sets the size of the window
func SetMaxSize(ctx context.Context, width int, height int) {
bus := servicebus.ExtractBus(ctx)
message := fmt.Sprintf("window:maxsize:%d:%d", width, height)
bus.Publish(message, "")
}
// SetPosition sets the position of the window
func SetPosition(ctx context.Context, x int, y int) {
bus := servicebus.ExtractBus(ctx)
message := fmt.Sprintf("window:position:%d:%d", x, y)
bus.Publish(message, "")
}
// Maximise the window
func Maximise(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:maximise", "")
}
// Unmaximise the window
func Unmaximise(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:unmaximise", "")
}
// Minimise the window
func Minimise(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:minimise", "")
}
// Unminimise the window
func Unminimise(ctx context.Context) {
bus := servicebus.ExtractBus(ctx)
bus.Publish("window:unminimise", "")
}

View File

@ -4,13 +4,9 @@ package wails
import (
"github.com/wailsapp/wails/v2/internal/app"
"github.com/wailsapp/wails/v2/internal/runtime"
"github.com/wailsapp/wails/v2/pkg/options"
)
// Store is an alias for the Store object
type Store = runtime.Store
// Run creates an application based on the given config and executes it
func Run(options *options.App) error {