Files

922 lines
20 KiB
Go
Raw Permalink Normal View History

2023-01-18 21:42:49 +11:00
package application
import (
"embed"
"encoding/json"
"io"
2023-01-18 21:42:49 +11:00
"log"
"log/slog"
"net/http"
2023-02-06 20:50:11 +11:00
"os"
2023-01-18 21:42:49 +11:00
"runtime"
"strconv"
2023-01-18 21:42:49 +11:00
"sync"
2024-03-20 10:30:14 +01:00
"github.com/wailsapp/wails/v3/internal/operatingsystem"
"github.com/pkg/browser"
"github.com/samber/lo"
"github.com/wailsapp/wails/v3/internal/signal"
2023-08-14 08:31:55 +10:00
"github.com/wailsapp/wails/v3/internal/assetserver"
"github.com/wailsapp/wails/v3/internal/assetserver/webview"
2023-11-01 10:53:45 -05:00
"github.com/wailsapp/wails/v3/internal/capabilities"
2023-02-12 13:36:58 +11:00
"github.com/wailsapp/wails/v3/pkg/events"
2023-11-01 10:53:45 -05:00
"github.com/wailsapp/wails/v3/pkg/icons"
2023-01-18 21:42:49 +11:00
)
//go:embed assets/*
var alphaAssets embed.FS
2023-01-18 21:42:49 +11:00
var globalApplication *App
// AlphaAssets is the default assets for the alpha application
var AlphaAssets = AssetOptions{
Handler: BundledAssetFileServer(alphaAssets),
}
2023-01-18 21:42:49 +11:00
func init() {
runtime.LockOSThread()
}
type EventListener struct {
2023-09-15 17:12:35 +10:00
callback func(app *Event)
}
2023-07-08 22:51:33 +10:00
func Get() *App {
return globalApplication
}
2023-02-12 13:36:58 +11:00
func New(appOptions Options) *App {
2023-01-18 21:42:49 +11:00
if globalApplication != nil {
return globalApplication
}
mergeApplicationDefaults(&appOptions)
2023-08-15 20:18:43 +10:00
result := newApplication(appOptions)
2023-03-24 08:43:00 +11:00
globalApplication = result
2023-02-06 20:50:11 +11:00
if result.Logger == nil {
if result.isDebugMode {
result.Logger = DefaultLogger(result.options.LogLevel)
} else {
result.Logger = slog.New(slog.NewTextHandler(io.Discard, nil))
}
2023-02-06 20:50:11 +11:00
}
2024-01-19 21:34:49 +11:00
if !appOptions.DisableDefaultSignalHandler {
result.signalHandler = signal.NewSignalHandler(result.Quit)
result.signalHandler.Logger = result.Logger
result.signalHandler.ExitMessage = func(sig os.Signal) string {
return "Quitting application..."
}
}
2023-08-19 17:15:15 +10:00
result.logStartup()
result.logPlatformInfo()
result.Events = NewWailsEventProcessor(result.dispatchEventToListeners)
messageProc := NewMessageProcessor(result.Logger)
2023-08-12 14:08:22 +10:00
opts := &assetserver.Options{
Handler: appOptions.Assets.Handler,
Middleware: assetserver.ChainMiddleware(
func(next http.Handler) http.Handler {
if m := appOptions.Assets.Middleware; m != nil {
return m(next)
}
return next
},
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
path := req.URL.Path
switch path {
case "/wails/runtime":
messageProc.ServeHTTP(rw, req)
case "/wails/capabilities":
assetserver.ServeFile(rw, path, globalApplication.capabilities.AsBytes())
case "/wails/flags":
updatedOptions := result.impl.GetFlags(appOptions)
flags, err := json.Marshal(updatedOptions)
if err != nil {
log.Fatal("Invalid flags provided to application: ", err.Error())
}
assetserver.ServeFile(rw, path, flags)
default:
next.ServeHTTP(rw, req)
}
})
},
),
Logger: result.Logger,
2023-03-15 10:05:52 +01:00
}
if appOptions.Assets.DisableLogging {
2024-01-19 21:34:49 +11:00
opts.Logger = slog.New(slog.NewTextHandler(io.Discard, nil))
}
2024-01-19 21:34:49 +11:00
srv, err := assetserver.NewAssetServer(opts)
if err != nil {
2023-11-27 21:24:58 +11:00
result.Logger.Error("Fatal error in application initialisation: " + err.Error())
}
result.assets = srv
2023-08-19 17:15:15 +10:00
result.assets.LogDetails()
result.bindings, err = NewBindings(appOptions.Bind, appOptions.BindAliases)
2023-03-24 08:43:00 +11:00
if err != nil {
2023-08-28 20:30:40 +10:00
globalApplication.fatal("Fatal error in application initialisation: " + err.Error())
2023-03-24 08:43:00 +11:00
}
result.plugins = NewPluginManager(appOptions.Plugins, srv)
2024-03-26 20:56:58 +11:00
errors := result.plugins.Init()
if len(errors) > 0 {
for _, err := range errors {
globalApplication.error("Error initialising plugin: " + err.Error())
}
globalApplication.fatal("Fatal error in plugins initialisation")
2023-03-24 08:43:00 +11:00
}
2023-04-05 21:22:02 +10:00
err = result.bindings.AddPlugins(appOptions.Plugins)
if err != nil {
2023-08-28 20:30:40 +10:00
globalApplication.fatal("Fatal error in application initialisation: " + err.Error())
2023-04-05 21:22:02 +10:00
}
2023-09-17 20:48:30 +10:00
// Process keybindings
if result.options.KeyBindings != nil {
result.keyBindings = processKeyBindingOptions(result.options.KeyBindings)
}
if appOptions.OnShutdown != nil {
result.OnShutdown(appOptions.OnShutdown)
}
2023-01-18 21:42:49 +11:00
return result
}
2023-02-12 13:36:58 +11:00
func mergeApplicationDefaults(o *Options) {
2023-01-18 21:42:49 +11:00
if o.Name == "" {
o.Name = "My Wails Application"
}
if o.Description == "" {
o.Description = "An application written using Wails"
}
if o.Icon == nil {
o.Icon = icons.ApplicationLightMode256
2023-01-18 21:42:49 +11:00
}
}
2023-04-18 21:27:09 +10:00
type (
platformApp interface {
run() error
destroy()
setApplicationMenu(menu *Menu)
name() string
getCurrentWindowID() uint
showAboutDialog(name string, description string, icon []byte)
setIcon(icon []byte)
on(id uint)
dispatchOnMainThread(id uint)
hide()
show()
getPrimaryScreen() (*Screen, error)
getScreens() ([]*Screen, error)
GetFlags(options Options) map[string]any
2023-06-16 20:48:57 +10:00
isOnMainThread() bool
isDarkMode() bool
2023-04-18 21:27:09 +10:00
}
runnable interface {
2023-09-28 11:38:59 -05:00
Run()
}
2023-04-18 21:27:09 +10:00
)
2023-01-18 21:42:49 +11:00
func processPanicHandlerRecover() {
h := globalApplication.options.PanicHandler
if h == nil {
2023-07-03 19:52:50 +10:00
return
}
if err := recover(); err != nil {
h(err)
}
2023-07-03 19:52:50 +10:00
}
2023-01-18 21:42:49 +11:00
// Messages sent from javascript get routed here
type windowMessage struct {
windowId uint
message string
}
var windowMessageBuffer = make(chan *windowMessage, 5)
2023-01-18 21:42:49 +11:00
2023-02-12 13:36:58 +11:00
type dragAndDropMessage struct {
windowId uint
filenames []string
}
var windowDragAndDropBuffer = make(chan *dragAndDropMessage, 5)
2023-02-12 13:36:58 +11:00
2023-10-21 11:21:10 +11:00
func addDragAndDropMessage(windowId uint, filenames []string) {
windowDragAndDropBuffer <- &dragAndDropMessage{
windowId: windowId,
filenames: filenames,
}
}
var _ webview.Request = &webViewAssetRequest{}
const webViewRequestHeaderWindowId = "x-wails-window-id"
2023-04-07 19:47:01 +10:00
const webViewRequestHeaderWindowName = "x-wails-window-name"
2023-01-18 21:42:49 +11:00
type webViewAssetRequest struct {
webview.Request
2023-04-07 19:47:01 +10:00
windowId uint
windowName string
}
var windowKeyEvents = make(chan *windowKeyEvent, 5)
2023-09-20 20:35:35 +10:00
type windowKeyEvent struct {
windowId uint
acceleratorString string
}
func (r *webViewAssetRequest) Header() (http.Header, error) {
h, err := r.Request.Header()
if err != nil {
return nil, err
}
hh := h.Clone()
hh.Set(webViewRequestHeaderWindowId, strconv.FormatUint(uint64(r.windowId), 10))
return hh, nil
2023-01-18 21:42:49 +11:00
}
var webviewRequests = make(chan *webViewAssetRequest, 5)
2023-01-18 21:42:49 +11:00
2023-07-12 20:31:13 +10:00
type eventHook struct {
2023-09-15 17:12:35 +10:00
callback func(event *Event)
2023-07-12 20:31:13 +10:00
}
2023-01-18 21:42:49 +11:00
type App struct {
2023-02-12 13:36:58 +11:00
options Options
applicationEventListeners map[uint][]*EventListener
2023-01-18 21:42:49 +11:00
applicationEventListenersLock sync.RWMutex
2023-07-12 20:31:13 +10:00
applicationEventHooks map[uint][]*eventHook
applicationEventHooksLock sync.RWMutex
2023-01-18 21:42:49 +11:00
// Windows
2023-09-28 11:38:59 -05:00
windows map[uint]Window
2023-10-14 09:34:00 +00:00
windowsLock sync.RWMutex
2023-01-18 21:42:49 +11:00
// System Trays
systemTrays map[uint]*SystemTray
systemTraysLock sync.Mutex
systemTrayID uint
systemTrayIDLock sync.RWMutex
// MenuItems
menuItems map[uint]*MenuItem
menuItemsLock sync.Mutex
// Running
running bool
runLock sync.Mutex
pendingRun []runnable
2023-02-26 15:06:05 +11:00
bindings *Bindings
2023-03-18 09:12:44 +11:00
plugins *PluginManager
2023-01-18 21:42:49 +11:00
// platform app
impl platformApp
// The main application menu
ApplicationMenu *Menu
clipboard *Clipboard
2023-02-06 20:50:11 +11:00
Events *EventProcessor
2023-08-12 16:36:08 +10:00
Logger *slog.Logger
2023-02-12 13:36:58 +11:00
contextMenus map[string]*Menu
contextMenusLock sync.Mutex
2023-05-26 21:20:11 +10:00
assets *assetserver.AssetServer
startURL string
2023-03-23 06:51:24 +11:00
// Hooks
2023-09-28 11:38:59 -05:00
windowCreatedCallbacks []func(window Window)
2023-04-01 20:36:55 +11:00
pid int
// Capabilities
capabilities capabilities.Capabilities
isDebugMode bool
2023-09-17 20:48:30 +10:00
// Keybindings
keyBindings map[string]func(window *WebviewWindow)
2024-01-09 07:45:56 +11:00
// Shutdown
performingShutdown bool
2024-01-17 20:45:30 +11:00
// Shutdown tasks are run when the application is shutting down.
// They are run in the order they are added and run on the main thread.
// The application option `OnShutdown` is run first.
shutdownTasks []func()
2024-01-19 21:34:49 +11:00
// signalHandler is used to handle signals
signalHandler *signal.SignalHandler
// Wails Event Listener related
wailsEventListenerLock sync.Mutex
wailsEventListeners []WailsEventListener
}
func (a *App) init() {
a.applicationEventHooks = make(map[uint][]*eventHook)
a.applicationEventListeners = make(map[uint][]*EventListener)
2023-09-28 11:38:59 -05:00
a.windows = make(map[uint]Window)
a.systemTrays = make(map[uint]*SystemTray)
a.contextMenus = make(map[string]*Menu)
2023-09-17 20:48:30 +10:00
a.keyBindings = make(map[string]func(window *WebviewWindow))
a.Logger = a.options.Logger
a.pid = os.Getpid()
a.wailsEventListeners = make([]WailsEventListener, 0)
2023-01-18 21:42:49 +11:00
}
func (a *App) getSystemTrayID() uint {
a.systemTrayIDLock.Lock()
defer a.systemTrayIDLock.Unlock()
a.systemTrayID++
return a.systemTrayID
}
2023-02-06 20:50:11 +11:00
2023-09-28 11:38:59 -05:00
func (a *App) getWindowForID(id uint) Window {
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
defer a.windowsLock.RUnlock()
2023-02-06 20:50:11 +11:00
return a.windows[id]
}
func (a *App) deleteWindowByID(id uint) {
a.windowsLock.Lock()
defer a.windowsLock.Unlock()
delete(a.windows, id)
}
func (a *App) Capabilities() capabilities.Capabilities {
return a.capabilities
}
2023-07-12 20:31:13 +10:00
func (a *App) On(eventType events.ApplicationEventType, callback func(event *Event)) func() {
2023-01-18 21:42:49 +11:00
eventID := uint(eventType)
a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock()
listener := &EventListener{
callback: callback,
}
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], listener)
2023-01-18 21:42:49 +11:00
if a.impl != nil {
2023-02-06 20:50:11 +11:00
go a.impl.on(eventID)
2023-01-18 21:42:49 +11:00
}
return func() {
// lock the map
a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock()
// Remove listener
a.applicationEventListeners[eventID] = lo.Without(a.applicationEventListeners[eventID], listener)
}
2023-01-18 21:42:49 +11:00
}
2023-07-12 20:31:13 +10:00
// RegisterHook registers a hook for the given event type. Hooks are called before the event listeners and can cancel the event.
// The returned function can be called to remove the hook.
func (a *App) RegisterHook(eventType events.ApplicationEventType, callback func(event *Event)) func() {
eventID := uint(eventType)
a.applicationEventHooksLock.Lock()
defer a.applicationEventHooksLock.Unlock()
thisHook := &eventHook{
callback: callback,
}
a.applicationEventHooks[eventID] = append(a.applicationEventHooks[eventID], thisHook)
return func() {
a.applicationEventHooksLock.Lock()
a.applicationEventHooks[eventID] = lo.Without(a.applicationEventHooks[eventID], thisHook)
a.applicationEventHooksLock.Unlock()
}
}
func (a *App) RegisterListener(listener WailsEventListener) {
a.wailsEventListenerLock.Lock()
a.wailsEventListeners = append(a.wailsEventListeners, listener)
a.wailsEventListenerLock.Unlock()
}
func (a *App) NewWebviewWindow() *WebviewWindow {
return a.NewWebviewWindowWithOptions(WebviewWindowOptions{})
2023-01-18 21:42:49 +11:00
}
2023-04-01 20:36:55 +11:00
func (a *App) GetPID() int {
return a.pid
}
2023-02-06 20:50:11 +11:00
func (a *App) info(message string, args ...any) {
2023-08-13 16:52:39 +10:00
if a.Logger != nil {
2023-09-13 16:47:14 +10:00
go a.Logger.Info(message, args...)
2023-08-13 16:52:39 +10:00
}
2023-08-12 16:36:08 +10:00
}
func (a *App) debug(message string, args ...any) {
2023-08-13 16:52:39 +10:00
if a.Logger != nil {
2023-09-13 16:47:14 +10:00
go a.Logger.Debug(message, args...)
2023-08-13 16:52:39 +10:00
}
2023-02-06 20:50:11 +11:00
}
func (a *App) fatal(message string, args ...any) {
msg := "A FATAL ERROR HAS OCCURRED: " + message
2023-08-13 16:52:39 +10:00
if a.Logger != nil {
a.Logger.Error(msg, args...)
2023-08-13 16:52:39 +10:00
} else {
println(msg)
}
2023-02-06 20:50:11 +11:00
os.Exit(1)
}
func (a *App) error(message string, args ...any) {
2023-08-13 16:52:39 +10:00
if a.Logger != nil {
2023-09-13 16:47:14 +10:00
go a.Logger.Error(message, args...)
2023-08-13 16:52:39 +10:00
}
2023-02-06 20:50:11 +11:00
}
func (a *App) NewWebviewWindowWithOptions(windowOptions WebviewWindowOptions) *WebviewWindow {
2023-09-28 11:38:59 -05:00
newWindow := NewWindow(windowOptions)
id := newWindow.ID()
2023-01-18 21:42:49 +11:00
a.windowsLock.Lock()
a.windows[id] = newWindow
a.windowsLock.Unlock()
2023-03-23 06:51:24 +11:00
// Call hooks
for _, hook := range a.windowCreatedCallbacks {
hook(newWindow)
}
a.runOrDeferToAppRun(newWindow)
2023-01-18 21:42:49 +11:00
return newWindow
}
func (a *App) NewSystemTray() *SystemTray {
id := a.getSystemTrayID()
2023-09-24 08:57:22 +10:00
newSystemTray := newSystemTray(id)
2023-01-18 21:42:49 +11:00
a.systemTraysLock.Lock()
a.systemTrays[id] = newSystemTray
a.systemTraysLock.Unlock()
a.runOrDeferToAppRun(newSystemTray)
2023-01-18 21:42:49 +11:00
return newSystemTray
}
func (a *App) Run() error {
2023-07-03 19:52:50 +10:00
// Setup panic handler
defer processPanicHandlerRecover()
2023-07-03 19:52:50 +10:00
2024-01-19 21:34:49 +11:00
// Call post-create hooks
err := a.preRun()
if err != nil {
return err
}
2023-01-18 21:42:49 +11:00
a.impl = newPlatformApp(a)
go func() {
for {
event := <-applicationEvents
go a.handleApplicationEvent(event)
2023-01-18 21:42:49 +11:00
}
}()
go func() {
for {
event := <-windowEvents
go a.handleWindowEvent(event)
2023-01-18 21:42:49 +11:00
}
}()
go func() {
for {
request := <-webviewRequests
go a.handleWebViewRequest(request)
2023-01-18 21:42:49 +11:00
}
}()
go func() {
for {
event := <-windowMessageBuffer
go a.handleWindowMessage(event)
2023-01-18 21:42:49 +11:00
}
}()
2023-09-20 20:35:35 +10:00
go func() {
for {
event := <-windowKeyEvents
go a.handleWindowKeyEvent(event)
2023-09-20 20:35:35 +10:00
}
}()
2023-02-12 13:36:58 +11:00
go func() {
for {
dragAndDropMessage := <-windowDragAndDropBuffer
go a.handleDragAndDropMessage(dragAndDropMessage)
2023-02-12 13:36:58 +11:00
}
}()
2023-01-18 21:42:49 +11:00
go func() {
for {
menuItemID := <-menuItemClicked
go a.handleMenuItemClicked(menuItemID)
2023-01-18 21:42:49 +11:00
}
}()
a.runLock.Lock()
a.running = true
2023-01-18 21:42:49 +11:00
for _, systray := range a.pendingRun {
2023-09-28 11:38:59 -05:00
go systray.Run()
2023-01-18 21:42:49 +11:00
}
a.pendingRun = nil
a.runLock.Unlock()
2023-01-18 21:42:49 +11:00
// set the application menu
2023-09-11 17:08:19 -05:00
if runtime.GOOS == "darwin" {
2023-04-26 21:06:41 +10:00
a.impl.setApplicationMenu(a.ApplicationMenu)
}
2023-06-10 10:19:33 +10:00
a.impl.setIcon(a.options.Icon)
2023-01-18 21:42:49 +11:00
2024-01-19 21:34:49 +11:00
err = a.impl.run()
2023-03-18 09:12:44 +11:00
if err != nil {
return err
}
2024-03-26 20:56:58 +11:00
errors := a.plugins.Shutdown()
if len(errors) > 0 {
for _, err := range errors {
a.error("Error shutting down plugin: " + err.Error())
}
}
2023-03-18 09:12:44 +11:00
return nil
2023-01-18 21:42:49 +11:00
}
2023-09-15 17:12:35 +10:00
func (a *App) handleApplicationEvent(event *Event) {
2023-01-18 21:42:49 +11:00
a.applicationEventListenersLock.RLock()
2023-09-15 17:12:35 +10:00
listeners, ok := a.applicationEventListeners[event.Id]
2023-01-18 21:42:49 +11:00
a.applicationEventListenersLock.RUnlock()
if !ok {
return
}
2023-07-12 20:31:13 +10:00
// Process Hooks
a.applicationEventHooksLock.RLock()
2023-09-15 17:12:35 +10:00
hooks, ok := a.applicationEventHooks[event.Id]
2023-07-12 20:31:13 +10:00
a.applicationEventHooksLock.RUnlock()
if ok {
for _, thisHook := range hooks {
2023-09-15 17:12:35 +10:00
thisHook.callback(event)
if event.Cancelled {
2023-07-12 20:31:13 +10:00
return
}
}
}
2023-01-18 21:42:49 +11:00
for _, listener := range listeners {
2023-09-15 17:12:35 +10:00
go listener.callback(event)
2023-01-18 21:42:49 +11:00
}
}
2023-02-12 13:36:58 +11:00
func (a *App) handleDragAndDropMessage(event *dragAndDropMessage) {
// Get window from window map
a.windowsLock.Lock()
window, ok := a.windows[event.windowId]
a.windowsLock.Unlock()
if !ok {
log.Printf("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
2023-09-28 11:38:59 -05:00
window.HandleDragAndDropMessage(event.filenames)
2023-02-12 13:36:58 +11:00
}
2023-01-18 21:42:49 +11:00
func (a *App) handleWindowMessage(event *windowMessage) {
// Get window from window map
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
2023-01-18 21:42:49 +11:00
window, ok := a.windows[event.windowId]
2023-10-14 09:34:00 +00:00
a.windowsLock.RUnlock()
2023-01-18 21:42:49 +11:00
if !ok {
log.Printf("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
2023-09-28 11:38:59 -05:00
window.HandleMessage(event.message)
2023-01-18 21:42:49 +11:00
}
func (a *App) handleWebViewRequest(request *webViewAssetRequest) {
a.assets.ServeWebViewRequest(request)
2023-01-18 21:42:49 +11:00
}
2023-07-12 20:31:13 +10:00
func (a *App) handleWindowEvent(event *windowEvent) {
2023-01-18 21:42:49 +11:00
// Get window from window map
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
2023-01-18 21:42:49 +11:00
window, ok := a.windows[event.WindowID]
2023-10-14 09:34:00 +00:00
a.windowsLock.RUnlock()
2023-01-18 21:42:49 +11:00
if !ok {
2023-09-28 11:38:59 -05:00
log.Printf("Window #%d not found", event.WindowID)
2023-01-18 21:42:49 +11:00
return
}
2023-09-28 11:38:59 -05:00
window.HandleWindowEvent(event.EventID)
2023-01-18 21:42:49 +11:00
}
func (a *App) handleMenuItemClicked(menuItemID uint) {
menuItem := getMenuItemByID(menuItemID)
if menuItem == nil {
log.Printf("MenuItem #%d not found", menuItemID)
return
}
menuItem.handleClick()
}
2023-09-28 16:46:19 -05:00
func (a *App) CurrentWindow() *WebviewWindow {
2023-01-18 21:42:49 +11:00
if a.impl == nil {
return nil
}
id := a.impl.getCurrentWindowID()
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
defer a.windowsLock.RUnlock()
2023-10-17 20:25:36 +11:00
result := a.windows[id]
if result == nil {
return nil
}
return result.(*WebviewWindow)
2023-01-18 21:42:49 +11:00
}
// OnShutdown adds a function to be run when the application is shutting down.
func (a *App) OnShutdown(f func()) {
if f == nil {
return
}
a.shutdownTasks = append(a.shutdownTasks, f)
}
2024-01-17 20:45:30 +11:00
func (a *App) cleanup() {
if a.performingShutdown {
return
}
a.performingShutdown = true
for _, shutdownTask := range a.shutdownTasks {
InvokeSync(shutdownTask)
}
2023-09-24 08:57:22 +10:00
InvokeSync(func() {
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
2023-01-18 21:42:49 +11:00
for _, window := range a.windows {
window.Destroy()
}
2024-01-09 07:45:56 +11:00
a.windows = nil
2023-10-14 09:34:00 +00:00
a.windowsLock.RUnlock()
2023-01-18 21:42:49 +11:00
a.systemTraysLock.Lock()
for _, systray := range a.systemTrays {
systray.Destroy()
}
2024-01-09 07:45:56 +11:00
a.systemTrays = nil
2023-01-18 21:42:49 +11:00
a.systemTraysLock.Unlock()
})
2023-01-18 21:42:49 +11:00
}
2024-01-17 20:45:30 +11:00
func (a *App) Quit() {
if a.impl != nil {
InvokeSync(a.impl.destroy)
a.postQuit()
2024-01-17 20:45:30 +11:00
}
}
2023-12-19 20:43:28 +08:00
func (a *App) SetIcon(icon []byte) {
if a.impl != nil {
a.impl.setIcon(icon)
}
}
2023-01-18 21:42:49 +11:00
func (a *App) SetMenu(menu *Menu) {
a.ApplicationMenu = menu
if a.impl != nil {
a.impl.setApplicationMenu(menu)
}
}
func (a *App) ShowAboutDialog() {
if a.impl != nil {
a.impl.showAboutDialog(a.options.Name, a.options.Description, a.options.Icon)
}
}
2023-06-24 13:57:51 +10:00
func InfoDialog() *MessageDialog {
return newMessageDialog(InfoDialogType)
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func QuestionDialog() *MessageDialog {
return newMessageDialog(QuestionDialogType)
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func WarningDialog() *MessageDialog {
return newMessageDialog(WarningDialogType)
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func ErrorDialog() *MessageDialog {
return newMessageDialog(ErrorDialogType)
2023-01-18 21:42:49 +11:00
}
2024-01-19 21:34:49 +11:00
// TODO: Why isn't this used?
2023-06-24 13:57:51 +10:00
func OpenDirectoryDialog() *MessageDialog {
return newMessageDialog(OpenDirectoryDialogType)
2023-01-18 21:42:49 +11:00
}
2023-06-24 13:57:51 +10:00
func OpenFileDialog() *OpenFileDialogStruct {
2023-01-18 21:42:49 +11:00
return newOpenFileDialog()
}
2023-06-24 13:57:51 +10:00
func SaveFileDialog() *SaveFileDialogStruct {
2023-01-18 21:42:49 +11:00
return newSaveFileDialog()
}
func (a *App) GetPrimaryScreen() (*Screen, error) {
2023-04-18 21:27:09 +10:00
return a.impl.getPrimaryScreen()
2023-01-18 21:42:49 +11:00
}
func (a *App) GetScreens() ([]*Screen, error) {
2023-04-18 21:27:09 +10:00
return a.impl.getScreens()
2023-01-18 21:42:49 +11:00
}
func (a *App) Clipboard() *Clipboard {
if a.clipboard == nil {
a.clipboard = newClipboard()
}
return a.clipboard
}
func (a *App) dispatchOnMainThread(fn func()) {
2023-06-16 20:48:57 +10:00
// If we are on the main thread, just call the function
if a.impl.isOnMainThread() {
fn()
return
}
2023-01-18 21:42:49 +11:00
mainThreadFunctionStoreLock.Lock()
id := generateFunctionStoreID()
mainThreadFunctionStore[id] = fn
mainThreadFunctionStoreLock.Unlock()
// Call platform specific dispatch function
a.impl.dispatchOnMainThread(id)
}
2023-02-06 20:50:11 +11:00
2023-06-24 13:57:51 +10:00
func OpenFileDialogWithOptions(options *OpenFileDialogOptions) *OpenFileDialogStruct {
result := OpenFileDialog()
2023-02-06 20:50:11 +11:00
result.SetOptions(options)
return result
}
2023-06-24 13:57:51 +10:00
func SaveFileDialogWithOptions(s *SaveFileDialogOptions) *SaveFileDialogStruct {
result := SaveFileDialog()
2023-02-06 20:50:11 +11:00
result.SetOptions(s)
return result
}
func (a *App) dispatchEventToListeners(event *WailsEvent) {
listeners := a.wailsEventListeners
2023-02-06 20:50:11 +11:00
for _, window := range a.windows {
2023-09-28 11:38:59 -05:00
window.DispatchWailsEvent(event)
2023-02-06 20:50:11 +11:00
}
for _, listener := range listeners {
listener.DispatchWailsEvent(event)
}
2023-02-06 20:50:11 +11:00
}
func (a *App) IsDarkMode() bool {
if a.impl == nil {
return false
}
return a.impl.isDarkMode()
}
2023-02-06 20:50:11 +11:00
func (a *App) Hide() {
if a.impl != nil {
a.impl.hide()
}
}
func (a *App) Show() {
if a.impl != nil {
a.impl.show()
}
}
2023-02-12 13:36:58 +11:00
func (a *App) RegisterContextMenu(name string, menu *Menu) {
a.contextMenusLock.Lock()
defer a.contextMenusLock.Unlock()
a.contextMenus[name] = menu
}
func (a *App) getContextMenu(name string) (*Menu, bool) {
a.contextMenusLock.Lock()
defer a.contextMenusLock.Unlock()
menu, ok := a.contextMenus[name]
return menu, ok
}
2023-03-23 06:51:24 +11:00
2023-09-28 11:38:59 -05:00
func (a *App) OnWindowCreation(callback func(window Window)) {
2023-03-23 06:51:24 +11:00
a.windowCreatedCallbacks = append(a.windowCreatedCallbacks, callback)
}
2023-04-07 19:47:01 +10:00
2023-09-28 11:38:59 -05:00
func (a *App) GetWindowByName(name string) Window {
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
defer a.windowsLock.RUnlock()
2023-04-07 19:47:01 +10:00
for _, window := range a.windows {
if window.Name() == name {
return window
}
}
return nil
}
func (a *App) runOrDeferToAppRun(r runnable) {
a.runLock.Lock()
running := a.running
if !running {
a.pendingRun = append(a.pendingRun, r)
}
a.runLock.Unlock()
if running {
2023-09-28 11:38:59 -05:00
r.Run()
}
}
2023-09-17 20:48:30 +10:00
func (a *App) processKeyBinding(acceleratorString string, window *WebviewWindow) bool {
if len(a.keyBindings) == 0 {
2023-09-17 20:48:30 +10:00
return false
}
// Check key bindings
callback, ok := a.keyBindings[acceleratorString]
if !ok {
return false
}
// Execute callback
go callback(window)
return true
}
2023-09-20 20:35:35 +10:00
func (a *App) handleWindowKeyEvent(event *windowKeyEvent) {
// Get window from window map
2023-10-14 09:34:00 +00:00
a.windowsLock.RLock()
2023-09-20 20:35:35 +10:00
window, ok := a.windows[event.windowId]
2023-10-14 09:34:00 +00:00
a.windowsLock.RUnlock()
2023-09-20 20:35:35 +10:00
if !ok {
log.Printf("WebviewWindow #%d not found", event.windowId)
return
}
// Get callback from window
2023-09-28 11:38:59 -05:00
window.HandleKeyEvent(event.acceleratorString)
}
func (a *App) AssetServerHandler() func(rw http.ResponseWriter, req *http.Request) {
return a.assets.ServeHTTP
}
func (a *App) BrowserOpenURL(url string) error {
return browser.OpenURL(url)
}
func (a *App) BrowserOpenFile(path string) error {
return browser.OpenFile(path)
}
func (a *App) Environment() EnvironmentInfo {
2024-03-19 20:36:52 +11:00
info, _ := operatingsystem.Info()
result := EnvironmentInfo{
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Debug: a.isDebugMode,
OSInfo: info,
}
2024-03-19 20:36:52 +11:00
result.PlatformInfo = a.platformEnvironment()
return result
}
2024-01-17 20:45:30 +11:00
func (a *App) shouldQuit() bool {
if a.options.ShouldQuit != nil {
return a.options.ShouldQuit()
}
return true
}