5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-07 04:59:04 +08:00

[v3] Update application.On and window.On to return functions that unregister the listener. WebviewWindow.onApplicationEvent is a helper which will manage the unregistering for you on window destroy.

This commit is contained in:
Lea Anthony 2023-04-29 19:39:05 +10:00 committed by Misite Bao
parent 178ea9c8c5
commit f08ae2fc62
3 changed files with 66 additions and 13 deletions

View File

@ -1,6 +1,7 @@
package application package application
import ( import (
"github.com/samber/lo"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -23,6 +24,10 @@ func init() {
runtime.LockOSThread() runtime.LockOSThread()
} }
type EventListener struct {
callback func()
}
func New(appOptions Options) *App { func New(appOptions Options) *App {
if globalApplication != nil { if globalApplication != nil {
return globalApplication return globalApplication
@ -32,7 +37,7 @@ func New(appOptions Options) *App {
result := &App{ result := &App{
options: appOptions, options: appOptions,
applicationEventListeners: make(map[uint][]func()), applicationEventListeners: make(map[uint][]*EventListener),
systemTrays: make(map[uint]*SystemTray), systemTrays: make(map[uint]*SystemTray),
log: logger.New(appOptions.Logger.CustomLoggers...), log: logger.New(appOptions.Logger.CustomLoggers...),
contextMenus: make(map[string]*Menu), contextMenus: make(map[string]*Menu),
@ -155,7 +160,7 @@ var webviewRequests = make(chan *webViewAssetRequest)
type App struct { type App struct {
options Options options Options
applicationEventListeners map[uint][]func() applicationEventListeners map[uint][]*EventListener
applicationEventListenersLock sync.RWMutex applicationEventListenersLock sync.RWMutex
// Windows // Windows
@ -216,14 +221,25 @@ func (a *App) deleteWindowByID(id uint) {
delete(a.windows, id) delete(a.windows, id)
} }
func (a *App) On(eventType events.ApplicationEventType, callback func()) { func (a *App) On(eventType events.ApplicationEventType, callback func()) func() {
eventID := uint(eventType) eventID := uint(eventType)
a.applicationEventListenersLock.Lock() a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock() defer a.applicationEventListenersLock.Unlock()
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], callback) listener := &EventListener{
callback: callback,
}
a.applicationEventListeners[eventID] = append(a.applicationEventListeners[eventID], listener)
if a.impl != nil { if a.impl != nil {
go a.impl.on(eventID) go a.impl.on(eventID)
} }
return func() {
// lock the map
a.applicationEventListenersLock.Lock()
defer a.applicationEventListenersLock.Unlock()
// Remove listener
a.applicationEventListeners[eventID] = lo.Without(a.applicationEventListeners[eventID], listener)
}
} }
func (a *App) NewWebviewWindow() *WebviewWindow { func (a *App) NewWebviewWindow() *WebviewWindow {
return a.NewWebviewWindowWithOptions(&WebviewWindowOptions{}) return a.NewWebviewWindowWithOptions(&WebviewWindowOptions{})
@ -383,7 +399,7 @@ func (a *App) handleApplicationEvent(event uint) {
return return
} }
for _, listener := range listeners { for _, listener := range listeners {
go listener() go listener.callback()
} }
} }

View File

@ -2,6 +2,7 @@ package application
import ( import (
"fmt" "fmt"
"github.com/samber/lo"
"sync" "sync"
"time" "time"
@ -61,17 +62,25 @@ type (
} }
) )
type WindowEventListener struct {
callback func(ctx *WindowEventContext)
}
type WebviewWindow struct { type WebviewWindow struct {
options *WebviewWindowOptions options *WebviewWindowOptions
impl webviewWindowImpl impl webviewWindowImpl
implLock sync.RWMutex implLock sync.RWMutex
id uint id uint
eventListeners map[uint][]func(ctx *WindowEventContext) eventListeners map[uint][]*WindowEventListener
eventListenersLock sync.RWMutex eventListenersLock sync.RWMutex
contextMenus map[string]*Menu contextMenus map[string]*Menu
contextMenusLock sync.RWMutex contextMenusLock sync.RWMutex
// A map of listener cancellation functions
cancellersLock sync.RWMutex
cancellers []func()
} }
var windowID uint var windowID uint
@ -84,6 +93,13 @@ func getWindowID() uint {
return windowID return windowID
} }
// Use onApplicationEvent to register a callback for an application event from a window.
// This will handle tidying up the callback when the window is destroyed
func (w *WebviewWindow) onApplicationEvent(eventType events.ApplicationEventType, callback func()) {
cancelFn := globalApplication.On(eventType, callback)
w.addCancellationFunction(cancelFn)
}
func NewWindow(options *WebviewWindowOptions) *WebviewWindow { func NewWindow(options *WebviewWindowOptions) *WebviewWindow {
if options.Width == 0 { if options.Width == 0 {
options.Width = 800 options.Width = 800
@ -98,13 +114,19 @@ func NewWindow(options *WebviewWindowOptions) *WebviewWindow {
result := &WebviewWindow{ result := &WebviewWindow{
id: getWindowID(), id: getWindowID(),
options: options, options: options,
eventListeners: make(map[uint][]func(ctx *WindowEventContext)), eventListeners: make(map[uint][]*WindowEventListener),
contextMenus: make(map[string]*Menu), contextMenus: make(map[string]*Menu),
} }
return result return result
} }
func (w *WebviewWindow) addCancellationFunction(canceller func()) {
w.cancellersLock.Lock()
defer w.cancellersLock.Unlock()
w.cancellers = append(w.cancellers, canceller)
}
func (w *WebviewWindow) SetTitle(title string) *WebviewWindow { func (w *WebviewWindow) SetTitle(title string) *WebviewWindow {
w.implLock.RLock() w.implLock.RLock()
defer w.implLock.RUnlock() defer w.implLock.RUnlock()
@ -371,20 +393,30 @@ func (w *WebviewWindow) Center() {
w.impl.center() w.impl.center()
} }
func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(ctx *WindowEventContext)) { func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(ctx *WindowEventContext)) func() {
eventID := uint(eventType) eventID := uint(eventType)
w.eventListenersLock.Lock() w.eventListenersLock.Lock()
defer w.eventListenersLock.Unlock() defer w.eventListenersLock.Unlock()
w.eventListeners[eventID] = append(w.eventListeners[eventID], callback) windowEventListener := &WindowEventListener{
callback: callback,
}
w.eventListeners[eventID] = append(w.eventListeners[eventID], windowEventListener)
if w.impl != nil { if w.impl != nil {
w.impl.on(eventID) w.impl.on(eventID)
} }
return func() {
w.eventListenersLock.Lock()
defer w.eventListenersLock.Unlock()
w.eventListeners[eventID] = lo.Without(w.eventListeners[eventID], windowEventListener)
}
} }
func (w *WebviewWindow) handleWindowEvent(id uint) { func (w *WebviewWindow) handleWindowEvent(id uint) {
w.eventListenersLock.RLock() w.eventListenersLock.RLock()
for _, callback := range w.eventListeners[id] { for _, listener := range w.eventListeners[id] {
go callback(blankWindowEventContext) go listener.callback(blankWindowEventContext)
} }
w.eventListenersLock.RUnlock() w.eventListenersLock.RUnlock()
} }
@ -416,6 +448,10 @@ func (w *WebviewWindow) Destroy() {
if w.impl == nil { if w.impl == nil {
return return
} }
// Cancel the callbacks
for _, cancelFunc := range w.cancellers {
cancelFunc()
}
w.impl.destroy() w.impl.destroy()
} }
@ -631,7 +667,7 @@ func (w *WebviewWindow) handleDragAndDropMessage(event *dragAndDropMessage) {
ctx := newWindowEventContext() ctx := newWindowEventContext()
ctx.setDroppedFiles(event.filenames) ctx.setDroppedFiles(event.filenames)
for _, listener := range w.eventListeners[uint(events.FilesDropped)] { for _, listener := range w.eventListeners[uint(events.FilesDropped)] {
listener(ctx) listener.callback(ctx)
} }
} }

View File

@ -140,7 +140,8 @@ func (w *windowsWebviewWindow) _run() {
switch options.Windows.Theme { switch options.Windows.Theme {
case SystemDefault: case SystemDefault:
w.updateTheme(w32.IsCurrentlyDarkMode()) w.updateTheme(w32.IsCurrentlyDarkMode())
globalApplication.On(events.Windows.SystemThemeChanged, func() { // Setup a listener to respond to theme changes
w.parent.onApplicationEvent(events.Windows.SystemThemeChanged, func() {
w.updateTheme(w32.IsCurrentlyDarkMode()) w.updateTheme(w32.IsCurrentlyDarkMode())
}) })
case Light: case Light: