mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-19 02:19:31 +08:00
[v3] Initial hooks implementation
This commit is contained in:
parent
ba7ab2e607
commit
dc865404a9
@ -32,7 +32,7 @@ func main() {
|
||||
})
|
||||
|
||||
// OS specific application events
|
||||
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
|
||||
app.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) {
|
||||
for {
|
||||
log.Println("Sending event")
|
||||
app.Events.Emit(&application.WailsEvent{
|
||||
@ -44,7 +44,7 @@ func main() {
|
||||
})
|
||||
|
||||
// Platform agnostic events
|
||||
app.On(events.Common.ApplicationStarted, func() {
|
||||
app.On(events.Common.ApplicationStarted, func(event *application.Event) {
|
||||
println("events.Common.ApplicationStarted fired!")
|
||||
})
|
||||
|
||||
@ -56,7 +56,7 @@ func main() {
|
||||
InvisibleTitleBarHeight: 50,
|
||||
},
|
||||
})
|
||||
app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
win2 := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
|
||||
Title: "Events Demo",
|
||||
Mac: application.MacWindow{
|
||||
Backdrop: application.MacBackdropTranslucent,
|
||||
@ -65,6 +65,20 @@ func main() {
|
||||
},
|
||||
})
|
||||
|
||||
var cancel bool
|
||||
|
||||
win2.RegisterHook(events.Common.WindowFocus, func(e *application.WindowEvent) {
|
||||
println("---------------\n[Hook] Window focus!")
|
||||
cancel = !cancel
|
||||
if cancel {
|
||||
e.Cancel()
|
||||
}
|
||||
})
|
||||
|
||||
win2.On(events.Common.WindowFocus, func(e *application.WindowEventContext) {
|
||||
println("[Event] Window focus!")
|
||||
})
|
||||
|
||||
err := app.Run()
|
||||
|
||||
if err != nil {
|
||||
|
@ -22,7 +22,7 @@ func main() {
|
||||
ApplicationShouldTerminateAfterLastWindowClosed: true,
|
||||
},
|
||||
})
|
||||
app.On(events.Mac.ApplicationDidFinishLaunching, func() {
|
||||
app.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) {
|
||||
log.Println("ApplicationDidFinishLaunching")
|
||||
})
|
||||
|
||||
|
@ -35,7 +35,7 @@ func init() {
|
||||
}
|
||||
|
||||
type EventListener struct {
|
||||
callback func()
|
||||
callback func(*Event)
|
||||
}
|
||||
|
||||
func Get() *App {
|
||||
@ -219,10 +219,16 @@ func (r *webViewAssetRequest) Header() (http.Header, error) {
|
||||
|
||||
var webviewRequests = make(chan *webViewAssetRequest)
|
||||
|
||||
type eventHook struct {
|
||||
callback func(*Event)
|
||||
}
|
||||
|
||||
type App struct {
|
||||
options Options
|
||||
applicationEventListeners map[uint][]*EventListener
|
||||
applicationEventListenersLock sync.RWMutex
|
||||
applicationEventHooks map[uint][]*eventHook
|
||||
applicationEventHooksLock sync.RWMutex
|
||||
|
||||
// Windows
|
||||
windows map[uint]*WebviewWindow
|
||||
@ -293,7 +299,7 @@ func (a *App) Capabilities() capabilities.Capabilities {
|
||||
return a.capabilities
|
||||
}
|
||||
|
||||
func (a *App) On(eventType events.ApplicationEventType, callback func()) func() {
|
||||
func (a *App) On(eventType events.ApplicationEventType, callback func(event *Event)) func() {
|
||||
eventID := uint(eventType)
|
||||
a.applicationEventListenersLock.Lock()
|
||||
defer a.applicationEventListenersLock.Unlock()
|
||||
@ -313,6 +319,25 @@ func (a *App) On(eventType events.ApplicationEventType, callback func()) func()
|
||||
a.applicationEventListeners[eventID] = lo.Without(a.applicationEventListeners[eventID], listener)
|
||||
}
|
||||
}
|
||||
|
||||
// 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) NewWebviewWindow() *WebviewWindow {
|
||||
return a.NewWebviewWindowWithOptions(WebviewWindowOptions{})
|
||||
}
|
||||
@ -468,8 +493,24 @@ func (a *App) handleApplicationEvent(event uint) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
thisEvent := &Event{}
|
||||
|
||||
// Process Hooks
|
||||
a.applicationEventHooksLock.RLock()
|
||||
hooks, ok := a.applicationEventHooks[event]
|
||||
a.applicationEventHooksLock.RUnlock()
|
||||
if ok {
|
||||
for _, thisHook := range hooks {
|
||||
thisHook.callback(thisEvent)
|
||||
if thisEvent.Cancelled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range listeners {
|
||||
go listener.callback()
|
||||
go listener.callback(thisEvent)
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,7 +547,7 @@ func (a *App) handleWebViewRequest(request *webViewAssetRequest) {
|
||||
a.assets.ServeWebViewRequest(request)
|
||||
}
|
||||
|
||||
func (a *App) handleWindowEvent(event *WindowEvent) {
|
||||
func (a *App) handleWindowEvent(event *windowEvent) {
|
||||
// Get window from window map
|
||||
a.windowsLock.Lock()
|
||||
window, ok := a.windows[event.WindowID]
|
||||
|
@ -220,7 +220,7 @@ func processApplicationEvent(eventID C.uint) {
|
||||
|
||||
//export processWindowEvent
|
||||
func processWindowEvent(windowID C.uint, eventID C.uint) {
|
||||
windowEvents <- &WindowEvent{
|
||||
windowEvents <- &windowEvent{
|
||||
WindowID: uint(windowID),
|
||||
EventID: uint(eventID),
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func processApplicationEvent(eventID C.uint) {
|
||||
|
||||
//export processWindowEvent
|
||||
func processWindowEvent(windowID C.uint, eventID C.uint) {
|
||||
windowEvents <- &WindowEvent{
|
||||
windowEvents <- &windowEvent{
|
||||
WindowID: uint(windowID),
|
||||
EventID: uint(eventID),
|
||||
}
|
||||
|
@ -9,19 +9,32 @@ import (
|
||||
|
||||
var applicationEvents = make(chan uint)
|
||||
|
||||
type WindowEvent struct {
|
||||
type windowEvent struct {
|
||||
WindowID uint
|
||||
EventID uint
|
||||
}
|
||||
|
||||
var windowEvents = make(chan *WindowEvent)
|
||||
type Event struct {
|
||||
Cancelled bool
|
||||
}
|
||||
|
||||
func (e *Event) Cancel() {
|
||||
e.Cancelled = true
|
||||
}
|
||||
|
||||
var windowEvents = make(chan *windowEvent)
|
||||
|
||||
var menuItemClicked = make(chan uint)
|
||||
|
||||
type WailsEvent struct {
|
||||
Name string `json:"name"`
|
||||
Data any `json:"data"`
|
||||
Sender string `json:"sender"`
|
||||
Name string `json:"name"`
|
||||
Data any `json:"data"`
|
||||
Sender string `json:"sender"`
|
||||
Cancelled bool
|
||||
}
|
||||
|
||||
func (e *WailsEvent) Cancel() {
|
||||
e.Cancelled = true
|
||||
}
|
||||
|
||||
var commonEvents = make(chan uint)
|
||||
@ -35,6 +48,10 @@ func (e WailsEvent) ToJSON() string {
|
||||
return string(marshal)
|
||||
}
|
||||
|
||||
type hook struct {
|
||||
callback func(*WailsEvent)
|
||||
}
|
||||
|
||||
// eventListener holds a callback function which is invoked when
|
||||
// the event listened for is emitted. It has a counter which indicates
|
||||
// how the total number of events it is interested in. A value of zero
|
||||
@ -51,12 +68,15 @@ type EventProcessor struct {
|
||||
listeners map[string][]*eventListener
|
||||
notifyLock sync.RWMutex
|
||||
dispatchEventToWindows func(*WailsEvent)
|
||||
hooks map[string][]*hook
|
||||
hookLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewWailsEventProcessor(dispatchEventToWindows func(*WailsEvent)) *EventProcessor {
|
||||
return &EventProcessor{
|
||||
listeners: make(map[string][]*eventListener),
|
||||
dispatchEventToWindows: dispatchEventToWindows,
|
||||
hooks: make(map[string][]*hook),
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,6 +100,19 @@ func (e *EventProcessor) Emit(thisEvent *WailsEvent) {
|
||||
if thisEvent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If we have any hooks, run them first and check if the event was cancelled
|
||||
if e.hooks != nil {
|
||||
if hooks, ok := e.hooks[thisEvent.Name]; ok {
|
||||
for _, thisHook := range hooks {
|
||||
thisHook.callback(thisEvent)
|
||||
if thisEvent.Cancelled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
go e.dispatchEventToListeners(thisEvent)
|
||||
go e.dispatchEventToWindows(thisEvent)
|
||||
}
|
||||
@ -119,6 +152,29 @@ func (e *EventProcessor) registerListener(eventName string, callback func(*Wails
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterHook provides a means of registering methods to be called before emitting the event
|
||||
func (e *EventProcessor) RegisterHook(eventName string, callback func(*WailsEvent)) func() {
|
||||
// Create new hook
|
||||
thisHook := &hook{
|
||||
callback: callback,
|
||||
}
|
||||
e.hookLock.Lock()
|
||||
// Append the new listener to the listeners slice
|
||||
e.hooks[eventName] = append(e.hooks[eventName], thisHook)
|
||||
e.hookLock.Unlock()
|
||||
return func() {
|
||||
e.hookLock.Lock()
|
||||
defer e.hookLock.Unlock()
|
||||
|
||||
if _, ok := e.hooks[eventName]; !ok {
|
||||
return
|
||||
}
|
||||
e.hooks[eventName] = lo.Filter(e.hooks[eventName], func(l *hook, i int) bool {
|
||||
return l != thisHook
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// unRegisterListener provides a means of unsubscribing to events of type "eventName"
|
||||
func (e *EventProcessor) unRegisterListener(eventName string) {
|
||||
e.notifyLock.Lock()
|
||||
|
@ -182,7 +182,7 @@ func (s *windowsSystemTray) run() {
|
||||
s.updateIcon()
|
||||
|
||||
// Listen for dark mode changes
|
||||
globalApplication.On(events.Windows.SystemThemeChanged, func() {
|
||||
globalApplication.On(events.Windows.SystemThemeChanged, func(event *Event) {
|
||||
s.updateIcon()
|
||||
})
|
||||
|
||||
|
@ -74,10 +74,23 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
type WindowEvent struct {
|
||||
WindowEventContext
|
||||
Cancelled bool
|
||||
}
|
||||
|
||||
func (w *WindowEvent) Cancel() {
|
||||
w.Cancelled = true
|
||||
}
|
||||
|
||||
type WindowEventListener struct {
|
||||
callback func(ctx *WindowEventContext)
|
||||
}
|
||||
|
||||
type windowHook struct {
|
||||
callback func(ctx *WindowEvent)
|
||||
}
|
||||
|
||||
type WebviewWindow struct {
|
||||
options WebviewWindowOptions
|
||||
impl webviewWindowImpl
|
||||
@ -85,6 +98,8 @@ type WebviewWindow struct {
|
||||
|
||||
eventListeners map[uint][]*WindowEventListener
|
||||
eventListenersLock sync.RWMutex
|
||||
eventHooks map[uint][]*windowHook
|
||||
eventHooksLock sync.RWMutex
|
||||
|
||||
contextMenus map[string]*Menu
|
||||
contextMenusLock sync.RWMutex
|
||||
@ -106,7 +121,7 @@ func getWindowID() uint {
|
||||
|
||||
// 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()) {
|
||||
func (w *WebviewWindow) onApplicationEvent(eventType events.ApplicationEventType, callback func(*Event)) {
|
||||
cancelFn := globalApplication.On(eventType, callback)
|
||||
w.addCancellationFunction(cancelFn)
|
||||
}
|
||||
@ -152,6 +167,7 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow {
|
||||
options: options,
|
||||
eventListeners: make(map[uint][]*WindowEventListener),
|
||||
contextMenus: make(map[string]*Menu),
|
||||
eventHooks: make(map[uint][]*windowHook),
|
||||
}
|
||||
|
||||
result.setupEventMapping()
|
||||
@ -553,12 +569,44 @@ func (w *WebviewWindow) On(eventType events.WindowEventType, callback func(ctx *
|
||||
defer w.eventListenersLock.Unlock()
|
||||
w.eventListeners[eventID] = lo.Without(w.eventListeners[eventID], windowEventListener)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterHook registers a hook for the given window event
|
||||
func (w *WebviewWindow) RegisterHook(eventType events.WindowEventType, callback func(ctx *WindowEvent)) func() {
|
||||
eventID := uint(eventType)
|
||||
w.eventHooksLock.Lock()
|
||||
defer w.eventHooksLock.Unlock()
|
||||
windowEventHook := &windowHook{
|
||||
callback: callback,
|
||||
}
|
||||
w.eventHooks[eventID] = append(w.eventHooks[eventID], windowEventHook)
|
||||
|
||||
return func() {
|
||||
w.eventHooksLock.Lock()
|
||||
defer w.eventHooksLock.Unlock()
|
||||
w.eventHooks[eventID] = lo.Without(w.eventHooks[eventID], windowEventHook)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) handleWindowEvent(id uint) {
|
||||
w.eventListenersLock.RLock()
|
||||
defer w.eventListenersLock.RUnlock()
|
||||
|
||||
// Get hooks
|
||||
w.eventHooksLock.RLock()
|
||||
hooks := w.eventHooks[id]
|
||||
w.eventHooksLock.RUnlock()
|
||||
|
||||
// Create new WindowEvent
|
||||
thisEvent := &WindowEvent{}
|
||||
|
||||
for _, thisHook := range hooks {
|
||||
thisHook.callback(thisEvent)
|
||||
if thisEvent.Cancelled {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, listener := range w.eventListeners[id] {
|
||||
go listener.callback(blankWindowEventContext)
|
||||
}
|
||||
@ -932,7 +980,7 @@ func (w *WebviewWindow) Focus() {
|
||||
}
|
||||
|
||||
func (w *WebviewWindow) emit(eventType events.WindowEventType) {
|
||||
windowEvents <- &WindowEvent{
|
||||
windowEvents <- &windowEvent{
|
||||
WindowID: w.id,
|
||||
EventID: uint(eventType),
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ func (w *windowsWebviewWindow) run() {
|
||||
switch options.Windows.Theme {
|
||||
case SystemDefault:
|
||||
w.updateTheme(w32.IsCurrentlyDarkMode())
|
||||
w.parent.onApplicationEvent(events.Windows.SystemThemeChanged, func() {
|
||||
w.parent.onApplicationEvent(events.Windows.SystemThemeChanged, func(*Event) {
|
||||
w.updateTheme(w32.IsCurrentlyDarkMode())
|
||||
})
|
||||
case Light:
|
||||
@ -1475,7 +1475,7 @@ func (w *windowsWebviewWindow) flash(enabled bool) {
|
||||
func (w *windowsWebviewWindow) navigationCompleted(sender *edge.ICoreWebView2, args *edge.ICoreWebView2NavigationCompletedEventArgs) {
|
||||
|
||||
// Emit DomReady Event
|
||||
windowEvents <- &WindowEvent{EventID: uint(events.Windows.WebViewNavigationCompleted), WindowID: w.parent.id}
|
||||
windowEvents <- &windowEvent{EventID: uint(events.Windows.WebViewNavigationCompleted), WindowID: w.parent.id}
|
||||
|
||||
if w.hasStarted {
|
||||
// NavigationCompleted is triggered for every Load. If an application uses reloads the Hide/Show will trigger
|
||||
|
Loading…
Reference in New Issue
Block a user