From f02c140709b5296c8172ab92051411affb7c33d2 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Mon, 19 Jul 2021 20:21:41 +1000 Subject: [PATCH] [v2] [broken - WIP] Major refactor of runtime in progress --- v2/internal/app/desktop.go | 13 +- v2/internal/app/dev.go | 3 +- v2/internal/app/server.go | 19 +- v2/internal/runtime/browser.go | 36 --- v2/internal/runtime/dialog.go | 172 ------------ v2/internal/runtime/events.go | 86 ------ v2/internal/runtime/log.go | 69 ----- v2/internal/runtime/menu.go | 46 ---- v2/internal/runtime/runtime.go | 38 --- v2/internal/runtime/store.go | 367 -------------------------- v2/internal/runtime/store_test.go | 165 ------------ v2/internal/runtime/system.go | 60 ----- v2/internal/runtime/system_default.go | 8 - v2/internal/runtime/system_desktop.go | 8 - v2/internal/runtime/system_server.go | 8 - v2/internal/runtime/window.go | 120 --------- v2/internal/servicebus/extract.go | 17 ++ v2/internal/subsystem/binding.go | 7 +- v2/internal/subsystem/call.go | 16 +- v2/internal/subsystem/log.go | 17 +- v2/internal/subsystem/runtime.go | 53 ---- v2/pkg/runtime/Events/events.go | 43 +++ v2/pkg/runtime/dialog/dialog.go | 43 +-- v2/pkg/runtime/log/log.go | 55 ++++ v2/pkg/runtime/menu/menu.go | 32 +++ v2/pkg/runtime/runtime.go | 13 + v2/pkg/runtime/window/window.go | 96 +++++++ v2/wails.go | 4 - 28 files changed, 284 insertions(+), 1330 deletions(-) delete mode 100644 v2/internal/runtime/browser.go delete mode 100644 v2/internal/runtime/dialog.go delete mode 100644 v2/internal/runtime/events.go delete mode 100644 v2/internal/runtime/log.go delete mode 100644 v2/internal/runtime/menu.go delete mode 100644 v2/internal/runtime/runtime.go delete mode 100644 v2/internal/runtime/store.go delete mode 100644 v2/internal/runtime/store_test.go delete mode 100644 v2/internal/runtime/system.go delete mode 100644 v2/internal/runtime/system_default.go delete mode 100644 v2/internal/runtime/system_desktop.go delete mode 100644 v2/internal/runtime/system_server.go delete mode 100644 v2/internal/runtime/window.go create mode 100644 v2/internal/servicebus/extract.go create mode 100644 v2/pkg/runtime/Events/events.go create mode 100644 v2/pkg/runtime/log/log.go create mode 100644 v2/pkg/runtime/menu/menu.go create mode 100644 v2/pkg/runtime/runtime.go create mode 100644 v2/pkg/runtime/window/window.go diff --git a/v2/internal/app/desktop.go b/v2/internal/app/desktop.go index 0bef0fa6a..528383813 100644 --- a/v2/internal/app/desktop.go +++ b/v2/internal/app/desktop.go @@ -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 } diff --git a/v2/internal/app/dev.go b/v2/internal/app/dev.go index c3f5b5c2d..b61fb4bd9 100644 --- a/v2/internal/app/dev.go +++ b/v2/internal/app/dev.go @@ -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 } diff --git a/v2/internal/app/server.go b/v2/internal/app/server.go index 620139c4c..a624f63f7 100644 --- a/v2/internal/app/server.go +++ b/v2/internal/app/server.go @@ -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 } diff --git a/v2/internal/runtime/browser.go b/v2/internal/runtime/browser.go deleted file mode 100644 index 0b1941ba8..000000000 --- a/v2/internal/runtime/browser.go +++ /dev/null @@ -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{} -} diff --git a/v2/internal/runtime/dialog.go b/v2/internal/runtime/dialog.go deleted file mode 100644 index 2f56e36e0..000000000 --- a/v2/internal/runtime/dialog.go +++ /dev/null @@ -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 -//} diff --git a/v2/internal/runtime/events.go b/v2/internal/runtime/events.go deleted file mode 100644 index b36085935..000000000 --- a/v2/internal/runtime/events.go +++ /dev/null @@ -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) - }) -} diff --git a/v2/internal/runtime/log.go b/v2/internal/runtime/log.go deleted file mode 100644 index 16f258eed..000000000 --- a/v2/internal/runtime/log.go +++ /dev/null @@ -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) -} diff --git a/v2/internal/runtime/menu.go b/v2/internal/runtime/menu.go deleted file mode 100644 index f4aa80429..000000000 --- a/v2/internal/runtime/menu.go +++ /dev/null @@ -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) -} diff --git a/v2/internal/runtime/runtime.go b/v2/internal/runtime/runtime.go deleted file mode 100644 index e331dfb80..000000000 --- a/v2/internal/runtime/runtime.go +++ /dev/null @@ -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()") -} diff --git a/v2/internal/runtime/store.go b/v2/internal/runtime/store.go deleted file mode 100644 index 4199154c6..000000000 --- a/v2/internal/runtime/store.go +++ /dev/null @@ -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() -} diff --git a/v2/internal/runtime/store_test.go b/v2/internal/runtime/store_test.go deleted file mode 100644 index 62327c578..000000000 --- a/v2/internal/runtime/store_test.go +++ /dev/null @@ -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() -} diff --git a/v2/internal/runtime/system.go b/v2/internal/runtime/system.go deleted file mode 100644 index 681e0472b..000000000 --- a/v2/internal/runtime/system.go +++ /dev/null @@ -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) -} diff --git a/v2/internal/runtime/system_default.go b/v2/internal/runtime/system_default.go deleted file mode 100644 index 025baf30a..000000000 --- a/v2/internal/runtime/system_default.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build !desktop,!server - -package runtime - -// AppType returns the application type, EG: desktop -func (r *system) AppType() string { - return "default" -} diff --git a/v2/internal/runtime/system_desktop.go b/v2/internal/runtime/system_desktop.go deleted file mode 100644 index e569a8555..000000000 --- a/v2/internal/runtime/system_desktop.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build desktop,!server - -package runtime - -// AppType returns the application type, EG: desktop -func (r *system) AppType() string { - return "desktop" -} diff --git a/v2/internal/runtime/system_server.go b/v2/internal/runtime/system_server.go deleted file mode 100644 index fb17eb08a..000000000 --- a/v2/internal/runtime/system_server.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build server - -package runtime - -// AppType returns the application type, EG: desktop -func (r *system) AppType() string { - return "server" -} diff --git a/v2/internal/runtime/window.go b/v2/internal/runtime/window.go deleted file mode 100644 index ce6efae8c..000000000 --- a/v2/internal/runtime/window.go +++ /dev/null @@ -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", "") -} diff --git a/v2/internal/servicebus/extract.go b/v2/internal/servicebus/extract.go new file mode 100644 index 000000000..bcd46186c --- /dev/null +++ b/v2/internal/servicebus/extract.go @@ -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) +} diff --git a/v2/internal/subsystem/binding.go b/v2/internal/subsystem/binding.go index a51c95b87..cfc4bae4f 100644 --- a/v2/internal/subsystem/binding.go +++ b/v2/internal/subsystem/binding.go @@ -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 diff --git a/v2/internal/subsystem/call.go b/v2/internal/subsystem/call.go index 558143f9d..d67e86f71 100644 --- a/v2/internal/subsystem/call.go +++ b/v2/internal/subsystem/call.go @@ -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) } diff --git a/v2/internal/subsystem/log.go b/v2/internal/subsystem/log.go index 023d2d12e..36f5abfd6 100644 --- a/v2/internal/subsystem/log.go +++ b/v2/internal/subsystem/log.go @@ -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) } diff --git a/v2/internal/subsystem/runtime.go b/v2/internal/subsystem/runtime.go index 1eebf946c..89a5639af 100644 --- a/v2/internal/subsystem/runtime.go +++ b/v2/internal/subsystem/runtime.go @@ -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 -} diff --git a/v2/pkg/runtime/Events/events.go b/v2/pkg/runtime/Events/events.go new file mode 100644 index 000000000..d51a61c85 --- /dev/null +++ b/v2/pkg/runtime/Events/events.go @@ -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) +} diff --git a/v2/pkg/runtime/dialog/dialog.go b/v2/pkg/runtime/dialog/dialog.go index 31a980113..db7797559 100644 --- a/v2/pkg/runtime/dialog/dialog.go +++ b/v2/pkg/runtime/dialog/dialog.go @@ -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() diff --git a/v2/pkg/runtime/log/log.go b/v2/pkg/runtime/log/log.go new file mode 100644 index 000000000..473826feb --- /dev/null +++ b/v2/pkg/runtime/log/log.go @@ -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) +} diff --git a/v2/pkg/runtime/menu/menu.go b/v2/pkg/runtime/menu/menu.go new file mode 100644 index 000000000..7f1fe521a --- /dev/null +++ b/v2/pkg/runtime/menu/menu.go @@ -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) +} diff --git a/v2/pkg/runtime/runtime.go b/v2/pkg/runtime/runtime.go new file mode 100644 index 000000000..ee1531df6 --- /dev/null +++ b/v2/pkg/runtime/runtime.go @@ -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()") +} diff --git a/v2/pkg/runtime/window/window.go b/v2/pkg/runtime/window/window.go new file mode 100644 index 000000000..71b432cdd --- /dev/null +++ b/v2/pkg/runtime/window/window.go @@ -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", "") +} diff --git a/v2/wails.go b/v2/wails.go index 4b90b2338..f2177fe3b 100644 --- a/v2/wails.go +++ b/v2/wails.go @@ -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 {