From 05b16f1d63c9408248e22d56431e17f7cbc1f674 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Sun, 4 Feb 2024 18:16:59 +1100 Subject: [PATCH] Improve Linux application events and refactor app method receivers This commit includes the addition of common events for the Linux platform. Refactored and standardized the method receivers for the application from 'm' to 'l'. Also, the application startup events in the window example have been updated according to the new naming scheme. --- v3/examples/events/assets/index.html | 1 + v3/examples/window/main.go | 2 +- v3/pkg/application/application_linux.go | 74 +++++++++++----------- v3/pkg/application/dialogs_linux.go | 4 +- v3/pkg/application/events_common_linux.go | 21 ++++++ v3/pkg/application/linux_cgo.go | 40 +++++++++--- v3/pkg/application/mainthread_linux.go | 2 +- v3/pkg/application/screen_linux.go | 6 +- v3/pkg/application/webview_window_linux.go | 10 +-- 9 files changed, 103 insertions(+), 57 deletions(-) create mode 100644 v3/pkg/application/events_common_linux.go diff --git a/v3/examples/events/assets/index.html b/v3/examples/events/assets/index.html index d8e0906e..a64a42c3 100644 --- a/v3/examples/events/assets/index.html +++ b/v3/examples/events/assets/index.html @@ -3,6 +3,7 @@ Title + diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index 9c0e390f..41fc4660 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -24,7 +24,7 @@ func main() { ApplicationShouldTerminateAfterLastWindowClosed: false, }, }) - app.On(events.Mac.ApplicationDidFinishLaunching, func(event *application.Event) { + app.On(events.Common.ApplicationStarted, func(event *application.Event) { log.Println("ApplicationDidFinishLaunching") }) diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index 4272cd5c..c3b54673 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -35,7 +35,7 @@ type linuxApp struct { theme string } -func (m *linuxApp) GetFlags(options Options) map[string]any { +func (l *linuxApp) GetFlags(options Options) map[string]any { if options.Flags == nil { options.Flags = make(map[string]any) } @@ -46,30 +46,30 @@ func getNativeApplication() *linuxApp { return globalApplication.impl.(*linuxApp) } -func (m *linuxApp) hide() { - hideAllWindows(m.application) +func (l *linuxApp) hide() { + hideAllWindows(l.application) } -func (m *linuxApp) show() { - showAllWindows(m.application) +func (l *linuxApp) show() { + showAllWindows(l.application) } -func (m *linuxApp) on(eventID uint) { +func (l *linuxApp) on(eventID uint) { // TODO: What do we need to do here? log.Println("linuxApp.on()", eventID) } -func (m *linuxApp) setIcon(icon []byte) { +func (l *linuxApp) setIcon(icon []byte) { log.Println("linuxApp.setIcon", "not implemented") } -func (m *linuxApp) name() string { +func (l *linuxApp) name() string { return appName() } -func (m *linuxApp) getCurrentWindowID() uint { - return getCurrentWindowID(m.application, m.windows) +func (l *linuxApp) getCurrentWindowID() uint { + return getCurrentWindowID(l.application, l.windows) } type rnr struct { @@ -80,9 +80,9 @@ func (r rnr) run() { r.f() } -func (m *linuxApp) getApplicationMenu() pointer { - if m.applicationMenu != nilPointer { - return m.applicationMenu +func (l *linuxApp) getApplicationMenu() pointer { + if l.applicationMenu != nilPointer { + return l.applicationMenu } menu := globalApplication.ApplicationMenu @@ -90,12 +90,12 @@ func (m *linuxApp) getApplicationMenu() pointer { InvokeSync(func() { menu.Update() }) - m.applicationMenu = (menu.impl).(*linuxMenu).native + l.applicationMenu = (menu.impl).(*linuxMenu).native } - return m.applicationMenu + return l.applicationMenu } -func (m *linuxApp) setApplicationMenu(menu *Menu) { +func (l *linuxApp) setApplicationMenu(menu *Menu) { // FIXME: How do we avoid putting a menu? if menu == nil { // Create a default menu @@ -104,46 +104,44 @@ func (m *linuxApp) setApplicationMenu(menu *Menu) { } } -func (m *linuxApp) run() error { +func (l *linuxApp) run() error { - // Add a hook to the ApplicationDidFinishLaunching event - // FIXME: add Wails specific events - i.e. Shouldn't platform specific ones be translated to Wails events? - m.parent.On(events.Mac.ApplicationDidFinishLaunching, func(evt *Event) { - // Do we need to do anything now? - fmt.Println("events.Mac.ApplicationDidFinishLaunching received!") + l.parent.On(events.Linux.ApplicationStartup, func(evt *Event) { + fmt.Println("events.Linux.ApplicationStartup received!") }) - m.monitorThemeChanges() - return appRun(m.application) + l.setupCommonEvents() + l.monitorThemeChanges() + return appRun(l.application) } -func (m *linuxApp) destroy() { +func (l *linuxApp) destroy() { if !globalApplication.shouldQuit() { return } globalApplication.cleanup() - appDestroy(m.application) + appDestroy(l.application) } -func (m *linuxApp) isOnMainThread() bool { +func (l *linuxApp) isOnMainThread() bool { return isOnMainThread() } // register our window to our parent mapping -func (m *linuxApp) registerWindow(window pointer, id uint) { - m.windowsLock.Lock() - m.windows[windowPointer(window)] = id - m.windowsLock.Unlock() +func (l *linuxApp) registerWindow(window pointer, id uint) { + l.windowsLock.Lock() + l.windows[windowPointer(window)] = id + l.windowsLock.Unlock() } -func (m *linuxApp) isDarkMode() bool { - return strings.Contains(m.theme, "dark") +func (l *linuxApp) isDarkMode() bool { + return strings.Contains(l.theme, "dark") } -func (m *linuxApp) monitorThemeChanges() { +func (l *linuxApp) monitorThemeChanges() { go func() { conn, err := dbus.ConnectSessionBus() if err != nil { - m.parent.info("[WARNING] Failed to connect to session bus; monitoring for theme changes will not function:", err) + l.parent.info("[WARNING] Failed to connect to session bus; monitoring for theme changes will not function:", err) return } defer conn.Close() @@ -176,10 +174,10 @@ func (m *linuxApp) monitorThemeChanges() { continue } - if theme != m.theme { - m.theme = theme + if theme != l.theme { + l.theme = theme event := newApplicationEvent(events.Common.ThemeChanged) - event.Context().setIsDarkMode(m.isDarkMode()) + event.Context().setIsDarkMode(l.isDarkMode()) applicationEvents <- event } diff --git a/v3/pkg/application/dialogs_linux.go b/v3/pkg/application/dialogs_linux.go index 4943766f..49499dda 100644 --- a/v3/pkg/application/dialogs_linux.go +++ b/v3/pkg/application/dialogs_linux.go @@ -1,7 +1,7 @@ package application -func (m *linuxApp) showAboutDialog(title string, message string, icon []byte) { - window := globalApplication.getWindowForID(m.getCurrentWindowID()) +func (l *linuxApp) showAboutDialog(title string, message string, icon []byte) { + window := globalApplication.getWindowForID(l.getCurrentWindowID()) var parent uintptr if window != nil { parent, _ = window.(*WebviewWindow).NativeWindowHandle() diff --git a/v3/pkg/application/events_common_linux.go b/v3/pkg/application/events_common_linux.go new file mode 100644 index 00000000..530ac156 --- /dev/null +++ b/v3/pkg/application/events_common_linux.go @@ -0,0 +1,21 @@ +//go:build linux + +package application + +import "github.com/wailsapp/wails/v3/pkg/events" + +var commonApplicationEventMap = map[events.ApplicationEventType]events.ApplicationEventType{ + events.Linux.ApplicationStartup: events.Common.ApplicationStarted, + events.Linux.SystemThemeChanged: events.Common.ThemeChanged, +} + +func (l *linuxApp) setupCommonEvents() { + for sourceEvent, targetEvent := range commonApplicationEventMap { + sourceEvent := sourceEvent + targetEvent := targetEvent + l.parent.On(sourceEvent, func(event *Event) { + event.Id = uint(targetEvent) + applicationEvents <- event + }) + } +} diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 36db3d50..379af254 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -206,7 +206,36 @@ func dispatchOnMainThreadCallback(callbackID C.uint) { //export activateLinux func activateLinux(data pointer) { - // NOOP: Callback for now + processApplicationEvent(C.uint(events.Linux.ApplicationStartup), data) +} + +//export processApplicationEvent +func processApplicationEvent(eventID C.uint, data pointer) { + event := newApplicationEvent(events.ApplicationEventType(eventID)) + + //if data != nil { + // dataCStrJSON := C.serializationNSDictionary(data) + // if dataCStrJSON != nil { + // defer C.free(unsafe.Pointer(dataCStrJSON)) + // + // dataJSON := C.GoString(dataCStrJSON) + // var result map[string]any + // err := json.Unmarshal([]byte(dataJSON), &result) + // + // if err != nil { + // panic(err) + // } + // + // event.Context().setData(result) + // } + //} + + switch event.Id { + case uint(events.Linux.SystemThemeChanged): + isDark := globalApplication.IsDarkMode() + event.Context().setIsDarkMode(isDark) + } + applicationEvents <- event } func isOnMainThread() bool { @@ -236,17 +265,12 @@ func appNew(name string) pointer { func appRun(app pointer) error { application := (*C.GApplication)(app) + //TODO: Only set this if we configure it to do so C.g_application_hold(application) // allows it to run without a window signal := C.CString("activate") defer C.free(unsafe.Pointer(signal)) - C.g_signal_connect_data( - C.gpointer(application), - signal, - C.GCallback(C.activateLinux), - nil, - nil, - 0) + C.signal_connect(unsafe.Pointer(application), signal, C.activateLinux, nil) status := C.g_application_run(application, 0, nil) C.g_application_release(application) C.g_object_unref(C.gpointer(app)) diff --git a/v3/pkg/application/mainthread_linux.go b/v3/pkg/application/mainthread_linux.go index 572b081b..2a5ff3e2 100644 --- a/v3/pkg/application/mainthread_linux.go +++ b/v3/pkg/application/mainthread_linux.go @@ -2,7 +2,7 @@ package application -func (m *linuxApp) dispatchOnMainThread(id uint) { +func (l *linuxApp) dispatchOnMainThread(id uint) { dispatchOnMainThread(id) } diff --git a/v3/pkg/application/screen_linux.go b/v3/pkg/application/screen_linux.go index 36411045..526752ac 100644 --- a/v3/pkg/application/screen_linux.go +++ b/v3/pkg/application/screen_linux.go @@ -7,17 +7,17 @@ import ( "sync" ) -func (m *linuxApp) getPrimaryScreen() (*Screen, error) { +func (l *linuxApp) getPrimaryScreen() (*Screen, error) { return nil, fmt.Errorf("not implemented") } -func (m *linuxApp) getScreens() ([]*Screen, error) { +func (l *linuxApp) getScreens() ([]*Screen, error) { var wg sync.WaitGroup var screens []*Screen var err error wg.Add(1) InvokeSync(func() { - screens, err = getScreens(m.application) + screens, err = getScreens(l.application) wg.Done() }) wg.Wait() diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go index 1f36059e..5314ea6b 100644 --- a/v3/pkg/application/webview_window_linux.go +++ b/v3/pkg/application/webview_window_linux.go @@ -259,11 +259,12 @@ func (w *linuxWebviewWindow) setAlwaysOnTop(alwaysOnTop bool) { func newWindowImpl(parent *WebviewWindow) *linuxWebviewWindow { // (*C.struct__GtkWidget)(m.native) //var menubar *C.struct__GtkWidget - return &linuxWebviewWindow{ + result := &linuxWebviewWindow{ application: getNativeApplication().application, parent: parent, // menubar: menubar, } + return result } func (w *linuxWebviewWindow) setTitle(title string) { @@ -405,9 +406,7 @@ func (w *linuxWebviewWindow) run() { } w.setURL(startURL) - // We need to wait for the HTML to load before we can execute the javascript - // FIXME: What event is this? DomReady? - w.parent.On(events.Mac.WebViewDidFinishNavigation, func(_ *WindowEvent) { + w.parent.On(events.Linux.WindowLoadChanged, func(_ *WindowEvent) { if w.parent.options.JS != "" { w.execJS(w.parent.options.JS) } @@ -416,6 +415,9 @@ func (w *linuxWebviewWindow) run() { w.execJS(js) } }) + w.parent.On(events.Linux.WindowDeleteEvent, func(e *WindowEvent) { + w.parent.emit(events.Common.WindowClosing) + }) w.parent.RegisterHook(events.Linux.WindowLoadChanged, func(e *WindowEvent) { w.execJS(runtime.Core()) })