From 3d93c83920e1044ce0ab736df69524ad283bc3bc Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Tue, 6 Feb 2024 21:14:51 +1100 Subject: [PATCH] Added menu cloning. Introduced the ability to clone a menu, along with its submenus, in Linux-based web applications to create a full deep copy. This fixes reusing the application menu for window menus. --- v3/examples/window/main.go | 32 ++++++++++--------- v3/pkg/application/application_linux.go | 24 +++----------- v3/pkg/application/keys.go | 5 +++ v3/pkg/application/linux_cgo.go | 16 +++++++++- v3/pkg/application/menu.go | 11 +++++++ v3/pkg/application/menuitem.go | 26 +++++++++++++++ .../messageprocessor_contextmenu.go | 9 ++++++ v3/pkg/application/options_webview_window.go | 2 +- v3/pkg/application/webview_window_linux.go | 25 +++++++++++++-- 9 files changed, 111 insertions(+), 39 deletions(-) diff --git a/v3/examples/window/main.go b/v3/examples/window/main.go index 41fc4660..4a31eaa0 100644 --- a/v3/examples/window/main.go +++ b/v3/examples/window/main.go @@ -154,21 +154,23 @@ func main() { }).Show() windowCounter++ }) - myMenu.Add("New WebviewWindow (ignores mouse events"). - SetAccelerator("CmdOrCtrl+F"). - OnClick(func(ctx *application.Context) { - app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ - HTML: "
", - X: rand.Intn(1000), - Y: rand.Intn(800), - IgnoreMouseEvents: true, - BackgroundType: application.BackgroundTypeTransparent, - Mac: application.MacWindow{ - InvisibleTitleBarHeight: 50, - }, - }).Show() - windowCounter++ - }) + if runtime.GOOS != "linux" { + myMenu.Add("New WebviewWindow (ignores mouse events)"). + SetAccelerator("CmdOrCtrl+F"). + OnClick(func(ctx *application.Context) { + app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ + HTML: "
", + X: rand.Intn(1000), + Y: rand.Intn(800), + IgnoreMouseEvents: true, + BackgroundType: application.BackgroundTypeTransparent, + Mac: application.MacWindow{ + InvisibleTitleBarHeight: 50, + }, + }).Show() + windowCounter++ + }) + } if runtime.GOOS == "darwin" { myMenu.Add("New WebviewWindow (MacTitleBarHiddenInset)"). OnClick(func(ctx *application.Context) { diff --git a/v3/pkg/application/application_linux.go b/v3/pkg/application/application_linux.go index a72775f0..2d50e988 100644 --- a/v3/pkg/application/application_linux.go +++ b/v3/pkg/application/application_linux.go @@ -22,9 +22,8 @@ func init() { } type linuxApp struct { - application pointer - applicationMenu pointer - parent *App + application pointer + parent *App startupActions []func() @@ -55,8 +54,8 @@ func (l *linuxApp) show() { } func (l *linuxApp) on(eventID uint) { - // TODO: What do we need to do here? - log.Println("linuxApp.on()", eventID) + // TODO: Test register/unregister events + //C.registerApplicationEvent(l.application, C.uint(eventID)) } func (l *linuxApp) setIcon(icon []byte) { @@ -80,21 +79,6 @@ func (r rnr) run() { r.f() } -func (l *linuxApp) getApplicationMenu() pointer { - if l.applicationMenu != nilPointer { - return l.applicationMenu - } - - menu := globalApplication.ApplicationMenu - if menu != nil { - InvokeSync(func() { - menu.Update() - }) - l.applicationMenu = (menu.impl).(*linuxMenu).native - } - return l.applicationMenu -} - func (l *linuxApp) setApplicationMenu(menu *Menu) { // FIXME: How do we avoid putting a menu? if menu == nil { diff --git a/v3/pkg/application/keys.go b/v3/pkg/application/keys.go index 4a6fc0e6..b366c106 100644 --- a/v3/pkg/application/keys.go +++ b/v3/pkg/application/keys.go @@ -70,6 +70,11 @@ type accelerator struct { Modifiers []modifier } +func (a *accelerator) clone() *accelerator { + result := *a + return &result +} + func (a *accelerator) String() string { var result []string // Sort modifiers diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index 73e32247..beba773f 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -13,7 +13,7 @@ import ( ) /* -#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 javascriptcoregtk-4.1 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 javascriptcoregtk-4.1 gdk-3.0 #include #include @@ -697,6 +697,10 @@ func windowDestroy(window pointer) { //C.gtk_widget_destroy((*C.GtkWidget)(window)) } +func menuDestroy(gtkMenu pointer) { + C.gtk_widget_destroy((*C.GtkWidget)(gtkMenu)) +} + func windowFullscreen(window pointer) { C.gtk_window_fullscreen((*C.GtkWindow)(window)) } @@ -867,6 +871,16 @@ func windowShow(window pointer) { C.gtk_widget_show_all((*C.GtkWidget)(window)) } +func windowIgnoreMouseEvents(window pointer, webview pointer, ignore bool) { + var enable C.int + if ignore { + enable = 1 + } + gdkWindow := (*C.GdkWindow)(window) + C.gdk_window_set_pass_through(gdkWindow, enable) + C.webkit_web_view_set_editable((*C.WebKitWebView)(webview), C.gboolean(enable)) +} + func windowSetBackgroundColour(vbox, webview pointer, colour RGBA) { rgba := C.GdkRGBA{C.double(colour.Red) / 255.0, C.double(colour.Green) / 255.0, C.double(colour.Blue) / 255.0, C.double(colour.Alpha) / 255.0} C.webkit_web_view_set_background_color((*C.WebKitWebView)(webview), &rgba) diff --git a/v3/pkg/application/menu.go b/v3/pkg/application/menu.go index f4a18f39..25a844a4 100644 --- a/v3/pkg/application/menu.go +++ b/v3/pkg/application/menu.go @@ -95,6 +95,17 @@ func (m *Menu) setContextData(data *ContextMenuData) { } } +// Clone recursively clones the menu and all its submenus. +func (m *Menu) clone() *Menu { + result := &Menu{ + label: m.label, + } + for _, item := range m.items { + result.items = append(result.items, item.clone()) + } + return result +} + func (a *App) NewMenu() *Menu { return &Menu{} } diff --git a/v3/pkg/application/menuitem.go b/v3/pkg/application/menuitem.go index f363fb74..7e0910a4 100644 --- a/v3/pkg/application/menuitem.go +++ b/v3/pkg/application/menuitem.go @@ -332,3 +332,29 @@ func (m *MenuItem) setContextData(data *ContextMenuData) { m.submenu.setContextData(data) } } + +// clone returns a deep copy of the MenuItem +func (m *MenuItem) clone() *MenuItem { + result := &MenuItem{ + id: m.id, + label: m.label, + tooltip: m.tooltip, + disabled: m.disabled, + checked: m.checked, + hidden: m.hidden, + bitmap: m.bitmap, + callback: m.callback, + itemType: m.itemType, + role: m.role, + } + if m.submenu != nil { + result.submenu = m.submenu.clone() + } + if m.accelerator != nil { + result.accelerator = m.accelerator.clone() + } + if m.contextMenuData != nil { + result.contextMenuData = m.contextMenuData.clone() + } + return result +} diff --git a/v3/pkg/application/messageprocessor_contextmenu.go b/v3/pkg/application/messageprocessor_contextmenu.go index c7c8a86a..0d7f2e89 100644 --- a/v3/pkg/application/messageprocessor_contextmenu.go +++ b/v3/pkg/application/messageprocessor_contextmenu.go @@ -11,6 +11,15 @@ type ContextMenuData struct { Data any `json:"data"` } +func (d ContextMenuData) clone() *ContextMenuData { + return &ContextMenuData{ + Id: d.Id, + X: d.X, + Y: d.Y, + Data: d.Data, + } +} + const ( ContextMenuOpen = 0 ) diff --git a/v3/pkg/application/options_webview_window.go b/v3/pkg/application/options_webview_window.go index 7164066e..c2058507 100644 --- a/v3/pkg/application/options_webview_window.go +++ b/v3/pkg/application/options_webview_window.go @@ -116,7 +116,7 @@ type WebviewWindowOptions struct { // KeyBindings is a map of key bindings to functions KeyBindings map[string]func(window *WebviewWindow) - // IgnoreMouseEvents will ignore mouse events in the window + // IgnoreMouseEvents will ignore mouse events in the window (Windows + Mac only) IgnoreMouseEvents bool } diff --git a/v3/pkg/application/webview_window_linux.go b/v3/pkg/application/webview_window_linux.go index 75055ddf..80d5b983 100644 --- a/v3/pkg/application/webview_window_linux.go +++ b/v3/pkg/application/webview_window_linux.go @@ -34,6 +34,7 @@ type linuxWebviewWindow struct { lastHeight int drag dragInfo lastX, lastY int + gtkmenu pointer } var ( @@ -357,8 +358,19 @@ func (w *linuxWebviewWindow) run() { app := getNativeApplication() - menu := app.getApplicationMenu() - w.window, w.webview, w.vbox = windowNew(app.application, menu, w.parent.id, w.parent.options.Linux.WebviewGpuPolicy) + var menu = w.menu + if menu == nil && globalApplication.ApplicationMenu != nil { + menu = globalApplication.ApplicationMenu.clone() + } + if menu != nil { + InvokeSync(func() { + menu.Update() + }) + w.menu = menu + w.gtkmenu = (menu.impl).(*linuxMenu).native + } + + w.window, w.webview, w.vbox = windowNew(app.application, w.gtkmenu, w.parent.id, w.parent.options.Linux.WebviewGpuPolicy) app.registerWindow(w.window, w.parent.id) // record our mapping w.connectSignals() if w.parent.options.EnableDragAndDrop { @@ -403,6 +415,10 @@ func (w *linuxWebviewWindow) run() { case WindowStateNormal: } + //if w.parent.options.IgnoreMouseEvents { + // windowIgnoreMouseEvents(w.window, w.webview, true) + //} + startURL, err := assetserver.GetStartURL(w.parent.options.URL) if err != nil { globalApplication.fatal(err.Error()) @@ -459,6 +475,11 @@ func (w *linuxWebviewWindow) relativePosition() (int, int) { func (w *linuxWebviewWindow) destroy() { w.parent.markAsDestroyed() + // Free menu + if w.gtkmenu != nil { + menuDestroy(w.gtkmenu) + w.gtkmenu = nil + } windowDestroy(w.window) }