From f0bf8cd827e6455e81663bc3d7fb8e95722c7c16 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 5 Jul 2023 20:35:45 +1000 Subject: [PATCH] [v3 windows] Refactor events + mappings. Fix app.Quit(). Make systray example work slightly better --- v3/examples/systray/main.go | 21 ++-- v3/pkg/application/application.go | 17 +-- v3/pkg/application/application_windows.go | 3 +- v3/pkg/application/options_mac.go | 4 + v3/pkg/application/options_win.go | 5 + v3/pkg/application/webview_window.go | 27 +++++ v3/pkg/application/webview_window_windows.go | 25 +++-- v3/pkg/events/defaults.go | 33 ++++++ v3/pkg/events/events.go | 111 +++++++++++++------ v3/pkg/events/events.txt | 13 +++ 10 files changed, 188 insertions(+), 71 deletions(-) create mode 100644 v3/pkg/events/defaults.go diff --git a/v3/examples/systray/main.go b/v3/examples/systray/main.go index dfec69839..26cf0431d 100644 --- a/v3/examples/systray/main.go +++ b/v3/examples/systray/main.go @@ -6,6 +6,7 @@ import ( "github.com/wailsapp/wails/v3/pkg/icons" "log" "runtime" + "time" "github.com/wailsapp/wails/v3/pkg/application" ) @@ -31,12 +32,15 @@ func main() { }, }) + var justClosed bool + window.On(events.Common.WindowLostFocus, func(ctx *application.WindowEventContext) { window.Hide() - }) - - app.On(events.Mac.ApplicationDidResignActiveNotification, func() { - window.Hide() + justClosed = true + go func() { + time.Sleep(200 * time.Millisecond) + justClosed = false + }() }) systemTray := app.NewSystemTray() @@ -79,15 +83,10 @@ func main() { } showWindow := func() { - if window.IsVisible() { - window.Hide() - return - } - err := systemTray.PositionWindow(window, 5) - if err != nil { - application.InfoDialog().SetTitle("Error").SetMessage(err.Error()).Show() + if justClosed { return } + _ = systemTray.PositionWindow(window, 5) window.Show().Focus() } systemTray.OnClick(showWindow) diff --git a/v3/pkg/application/application.go b/v3/pkg/application/application.go index b7207044e..8d439cb99 100644 --- a/v3/pkg/application/application.go +++ b/v3/pkg/application/application.go @@ -534,28 +534,21 @@ func (a *App) CurrentWindow() *WebviewWindow { } func (a *App) Quit() { - var wg sync.WaitGroup - wg.Add(2) - go func() { + invokeSync(func() { a.windowsLock.Lock() for _, window := range a.windows { window.Destroy() } a.windowsLock.Unlock() - wg.Done() - }() - go func() { a.systemTraysLock.Lock() for _, systray := range a.systemTrays { systray.Destroy() } a.systemTraysLock.Unlock() - wg.Done() - }() - wg.Wait() - if a.impl != nil { - a.impl.destroy() - } + if a.impl != nil { + a.impl.destroy() + } + }) } func (a *App) SetMenu(menu *Menu) { diff --git a/v3/pkg/application/application_windows.go b/v3/pkg/application/application_windows.go index e7a9c0d2e..7264452e5 100644 --- a/v3/pkg/application/application_windows.go +++ b/v3/pkg/application/application_windows.go @@ -182,7 +182,8 @@ func (m *windowsApp) run() error { } func (m *windowsApp) destroy() { - //C.destroyApp() + // Post a quit message to the main thread + w32.PostQuitMessage(0) } func (m *windowsApp) init() { diff --git a/v3/pkg/application/options_mac.go b/v3/pkg/application/options_mac.go index 109071021..7b39f87f7 100644 --- a/v3/pkg/application/options_mac.go +++ b/v3/pkg/application/options_mac.go @@ -1,5 +1,7 @@ package application +import "github.com/wailsapp/wails/v3/pkg/events" + type ActivationPolicy int const ( @@ -48,6 +50,8 @@ type MacWindow struct { TitleBar MacTitleBar Appearance MacAppearanceType InvisibleTitleBarHeight int + // Maps events from platform specific to the event name + EventMapping map[events.WindowEventType]events.WindowEventType } // MacTitleBar contains options for the Mac titlebar diff --git a/v3/pkg/application/options_win.go b/v3/pkg/application/options_win.go index 1dcd4f3d6..8235d6c97 100644 --- a/v3/pkg/application/options_win.go +++ b/v3/pkg/application/options_win.go @@ -1,5 +1,7 @@ package application +import "github.com/wailsapp/wails/v3/pkg/events" + type WindowsApplicationOptions struct { // WndProcInterceptor is a function that will be called for every message sent in the application. // Use this to hook into the main message loop. This is useful for handling custom window messages. @@ -55,6 +57,9 @@ type WindowsWindow struct { // Disable the menu bar for this window DisableMenu bool + + // Event mapping for the window + EventMapping map[events.WindowEventType]events.WindowEventType } type Theme int diff --git a/v3/pkg/application/webview_window.go b/v3/pkg/application/webview_window.go index 380ff1575..9577a983b 100644 --- a/v3/pkg/application/webview_window.go +++ b/v3/pkg/application/webview_window.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/samber/lo" + "runtime" "strings" "sync" "time" @@ -109,6 +110,30 @@ func (w *WebviewWindow) onApplicationEvent(eventType events.ApplicationEventType w.addCancellationFunction(cancelFn) } +func (w *WebviewWindow) setupEventMapping() { + + var mapping map[events.WindowEventType]events.WindowEventType + switch runtime.GOOS { + case "darwin": + mapping = w.options.Mac.EventMapping + case "windows": + mapping = w.options.Windows.EventMapping + case "linux": + // TBD + } + if mapping == nil { + mapping = events.DefaultWindowEventMapping() + } + + for source, target := range mapping { + source := source + target := target + w.On(source, func(ctx *WindowEventContext) { + w.emit(target) + }) + } +} + // NewWindow creates a new window with the given options func NewWindow(options WebviewWindowOptions) *WebviewWindow { if options.Width == 0 { @@ -128,6 +153,8 @@ func NewWindow(options WebviewWindowOptions) *WebviewWindow { contextMenus: make(map[string]*Menu), } + result.setupEventMapping() + // Listen for window closing events and de result.On(events.Common.WindowClosing, func(ctx *WindowEventContext) { shouldClose := true diff --git a/v3/pkg/application/webview_window_windows.go b/v3/pkg/application/webview_window_windows.go index 6c31d04e4..c7ec6f092 100644 --- a/v3/pkg/application/webview_window_windows.go +++ b/v3/pkg/application/webview_window_windows.go @@ -368,8 +368,7 @@ func (w *windowsWebviewWindow) relativePosition() (int, int) { } func (w *windowsWebviewWindow) destroy() { - //TODO implement me - panic("implement me") + // Not sure if we have anything to destroy... } func (w *windowsWebviewWindow) reload() { @@ -740,11 +739,15 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp switch msg { case w32.WM_ACTIVATE: if int(wparam&0xffff) == w32.WA_INACTIVE { - w.parent.emit(events.Common.WindowLostFocus) + w.parent.emit(events.Windows.WindowInactive) } - if wparam == w32.WA_ACTIVE || wparam == w32.WA_CLICKACTIVE { + if wparam == w32.WA_ACTIVE { getNativeApplication().currentWindowID = w.parent.id - w.parent.emit(events.Common.WindowFocus) + w.parent.emit(events.Windows.WindowActive) + } + if wparam == w32.WA_CLICKACTIVE { + getNativeApplication().currentWindowID = w.parent.id + w.parent.emit(events.Windows.WindowClickActive) } // If we want to have a frameless window but with the default frame decorations, extend the DWM client area. // This Option is not affected by returning 0 in WM_NCCALCSIZE. @@ -754,15 +757,15 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp w32.ExtendFrameIntoClientArea(w.hwnd, true) } case w32.WM_CLOSE: - w.parent.emit(events.Common.WindowClosing) + w.parent.emit(events.Windows.WindowClose) return 0 case w32.WM_KILLFOCUS: if w.focusingChromium { return 0 } - w.parent.emit(events.Common.WindowLostFocus) + w.parent.emit(events.Windows.WindowKillFocus) case w32.WM_SETFOCUS: - w.parent.emit(events.Common.WindowFocus) + w.parent.emit(events.Windows.WindowSetFocus) case w32.WM_NCLBUTTONDOWN: w32.SetFocus(w.hwnd) case w32.WM_MOVE, w32.WM_MOVING: @@ -770,11 +773,11 @@ func (w *windowsWebviewWindow) WndProc(msg uint32, wparam, lparam uintptr) uintp case w32.WM_SIZE: switch wparam { case w32.SIZE_MAXIMIZED: - w.parent.emit(events.Common.WindowMaximise) + w.parent.emit(events.Windows.WindowMaximise) case w32.SIZE_RESTORED: - w.parent.emit(events.Common.WindowRestore) + w.parent.emit(events.Windows.WindowRestore) case w32.SIZE_MINIMIZED: - w.parent.emit(events.Common.WindowMinimise) + w.parent.emit(events.Windows.WindowMinimise) } if w.parent.options.Frameless && wparam == w32.SIZE_MINIMIZED { // If the window is frameless, and we are minimizing, then we need to suppress the Resize on the diff --git a/v3/pkg/events/defaults.go b/v3/pkg/events/defaults.go new file mode 100644 index 000000000..a52d17186 --- /dev/null +++ b/v3/pkg/events/defaults.go @@ -0,0 +1,33 @@ +package events + +import "runtime" + +var defaultWindowEventMapping = map[string]map[WindowEventType]WindowEventType{ + "windows": { + Windows.WindowInactive: Common.WindowLostFocus, + Windows.WindowClickActive: Common.WindowFocus, + Windows.WindowActive: Common.WindowFocus, + Windows.WindowMaximise: Common.WindowMaximise, + Windows.WindowMinimise: Common.WindowMinimise, + Windows.WindowRestore: Common.WindowRestore, + Windows.WindowUnMaximise: Common.WindowUnMaximise, + Windows.WindowUnMinimise: Common.WindowUnMinimise, + Windows.WindowFullscreen: Common.WindowFullscreen, + Windows.WindowUnFullscreen: Common.WindowUnFullscreen, + }, + "darwin": { + Mac.WindowDidResignKey: Common.WindowLostFocus, + Mac.WindowDidResignKey: Common.WindowLostFocus, + Mac.WindowDidBecomeKey: Common.WindowFocus, + Mac.WindowDidMiniaturize: Common.WindowMinimise, + Mac.WindowDidDeminiaturize: Common.WindowUnMinimise, + Mac.WindowDidEnterFullScreen: Common.WindowFullscreen, + Mac.WindowDidExitFullScreen: Common.WindowUnFullscreen, + }, + "linux": {}, +} + +func DefaultWindowEventMapping() map[WindowEventType]WindowEventType { + platform := runtime.GOOS + return defaultWindowEventMapping[platform] +} diff --git a/v3/pkg/events/events.go b/v3/pkg/events/events.go index 59dd7e2a1..00bfd5dca 100644 --- a/v3/pkg/events/events.go +++ b/v3/pkg/events/events.go @@ -32,24 +32,24 @@ type commonEvents struct { func newCommonEvents() commonEvents { return commonEvents{ - ApplicationStarted: 1153, - WindowMaximise: 1154, - WindowUnMaximise: 1155, - WindowFullscreen: 1156, - WindowUnFullscreen: 1157, - WindowRestore: 1158, - WindowMinimise: 1159, - WindowUnMinimise: 1160, - WindowClosing: 1161, - WindowZoom: 1162, - WindowZoomIn: 1163, - WindowZoomOut: 1164, - WindowZoomReset: 1165, - WindowFocus: 1166, - WindowLostFocus: 1167, - WindowShow: 1168, - WindowHide: 1169, - WindowDPIChanged: 1170, + ApplicationStarted: 1166, + WindowMaximise: 1167, + WindowUnMaximise: 1168, + WindowFullscreen: 1169, + WindowUnFullscreen: 1170, + WindowRestore: 1171, + WindowMinimise: 1172, + WindowUnMinimise: 1173, + WindowClosing: 1174, + WindowZoom: 1175, + WindowZoomIn: 1176, + WindowZoomOut: 1177, + WindowZoomReset: 1178, + WindowFocus: 1179, + WindowLostFocus: 1180, + WindowShow: 1181, + WindowHide: 1182, + WindowDPIChanged: 1183, } } @@ -317,6 +317,19 @@ type windowsEvents struct { APMResumeSuspend ApplicationEventType APMPowerSettingChange ApplicationEventType WebViewNavigationCompleted WindowEventType + WindowInactive WindowEventType + WindowActive WindowEventType + WindowClickActive WindowEventType + WindowMaximise WindowEventType + WindowUnMaximise WindowEventType + WindowFullscreen WindowEventType + WindowUnFullscreen WindowEventType + WindowRestore WindowEventType + WindowMinimise WindowEventType + WindowUnMinimise WindowEventType + WindowClose WindowEventType + WindowSetFocus WindowEventType + WindowKillFocus WindowEventType } func newWindowsEvents() windowsEvents { @@ -328,6 +341,19 @@ func newWindowsEvents() windowsEvents { APMResumeSuspend: 1150, APMPowerSettingChange: 1151, WebViewNavigationCompleted: 1152, + WindowInactive: 1153, + WindowActive: 1154, + WindowClickActive: 1155, + WindowMaximise: 1156, + WindowUnMaximise: 1157, + WindowFullscreen: 1158, + WindowUnFullscreen: 1159, + WindowRestore: 1160, + WindowMinimise: 1161, + WindowUnMinimise: 1162, + WindowClose: 1163, + WindowSetFocus: 1164, + WindowKillFocus: 1165, } } @@ -465,22 +491,35 @@ var eventToJS = map[uint]string{ 1150: "windows:APMResumeSuspend", 1151: "windows:APMPowerSettingChange", 1152: "windows:WebViewNavigationCompleted", - 1153: "common:ApplicationStarted", - 1154: "common:WindowMaximise", - 1155: "common:WindowUnMaximise", - 1156: "common:WindowFullscreen", - 1157: "common:WindowUnFullscreen", - 1158: "common:WindowRestore", - 1159: "common:WindowMinimise", - 1160: "common:WindowUnMinimise", - 1161: "common:WindowClosing", - 1162: "common:WindowZoom", - 1163: "common:WindowZoomIn", - 1164: "common:WindowZoomOut", - 1165: "common:WindowZoomReset", - 1166: "common:WindowFocus", - 1167: "common:WindowLostFocus", - 1168: "common:WindowShow", - 1169: "common:WindowHide", - 1170: "common:WindowDPIChanged", + 1153: "windows:WindowInactive", + 1154: "windows:WindowActive", + 1155: "windows:WindowClickActive", + 1156: "windows:WindowMaximise", + 1157: "windows:WindowUnMaximise", + 1158: "windows:WindowFullscreen", + 1159: "windows:WindowUnFullscreen", + 1160: "windows:WindowRestore", + 1161: "windows:WindowMinimise", + 1162: "windows:WindowUnMinimise", + 1163: "windows:WindowClose", + 1164: "windows:WindowSetFocus", + 1165: "windows:WindowKillFocus", + 1166: "common:ApplicationStarted", + 1167: "common:WindowMaximise", + 1168: "common:WindowUnMaximise", + 1169: "common:WindowFullscreen", + 1170: "common:WindowUnFullscreen", + 1171: "common:WindowRestore", + 1172: "common:WindowMinimise", + 1173: "common:WindowUnMinimise", + 1174: "common:WindowClosing", + 1175: "common:WindowZoom", + 1176: "common:WindowZoomIn", + 1177: "common:WindowZoomOut", + 1178: "common:WindowZoomReset", + 1179: "common:WindowFocus", + 1180: "common:WindowLostFocus", + 1181: "common:WindowShow", + 1182: "common:WindowHide", + 1183: "common:WindowDPIChanged", } diff --git a/v3/pkg/events/events.txt b/v3/pkg/events/events.txt index a94008fd6..ce1716c62 100644 --- a/v3/pkg/events/events.txt +++ b/v3/pkg/events/events.txt @@ -127,6 +127,19 @@ windows:APMResumeAutomatic windows:APMResumeSuspend windows:APMPowerSettingChange windows:WebViewNavigationCompleted +windows:WindowInactive +windows:WindowActive +windows:WindowClickActive +windows:WindowMaximise +windows:WindowUnMaximise +windows:WindowFullscreen +windows:WindowUnFullscreen +windows:WindowRestore +windows:WindowMinimise +windows:WindowUnMinimise +windows:WindowClose +windows:WindowSetFocus +windows:WindowKillFocus common:ApplicationStarted common:WindowMaximise common:WindowUnMaximise