5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 04:59:38 +08:00
wails/v2/internal/frontend/desktop/linux/menu.go
2024-05-08 19:26:27 -05:00

165 lines
5.0 KiB
Go

//go:build linux
// +build linux
package linux
/*
#cgo linux pkg-config: gtk+-3.0
#cgo !webkit2_41 pkg-config: webkit2gtk-4.0
#cgo webkit2_41 pkg-config: webkit2gtk-4.1
#include "gtk/gtk.h"
static GtkMenuItem *toGtkMenuItem(void *pointer) { return (GTK_MENU_ITEM(pointer)); }
static GtkMenuShell *toGtkMenuShell(void *pointer) { return (GTK_MENU_SHELL(pointer)); }
static GtkCheckMenuItem *toGtkCheckMenuItem(void *pointer) { return (GTK_CHECK_MENU_ITEM(pointer)); }
static GtkRadioMenuItem *toGtkRadioMenuItem(void *pointer) { return (GTK_RADIO_MENU_ITEM(pointer)); }
extern void handleMenuItemClick(void*);
void blockClick(GtkWidget* menuItem, gulong handler_id) {
g_signal_handler_block (menuItem, handler_id);
}
void unblockClick(GtkWidget* menuItem, gulong handler_id) {
g_signal_handler_unblock (menuItem, handler_id);
}
gulong connectClick(GtkWidget* menuItem) {
return g_signal_connect(menuItem, "activate", G_CALLBACK(handleMenuItemClick), (void*)menuItem);
}
void addAccelerator(GtkWidget* menuItem, GtkAccelGroup* group, guint key, GdkModifierType mods) {
gtk_widget_add_accelerator(menuItem, "activate", group, key, mods, GTK_ACCEL_VISIBLE);
}
*/
import "C"
import "github.com/wailsapp/wails/v2/pkg/menu"
import "unsafe"
var menuIdCounter int
var menuItemToId map[*menu.MenuItem]int
var menuIdToItem map[int]*menu.MenuItem
var gtkCheckboxCache map[*menu.MenuItem][]*C.GtkWidget
var gtkMenuCache map[*menu.MenuItem]*C.GtkWidget
var gtkRadioMenuCache map[*menu.MenuItem][]*C.GtkWidget
var gtkSignalHandlers map[*C.GtkWidget]C.gulong
var gtkSignalToMenuItem map[*C.GtkWidget]*menu.MenuItem
func (f *Frontend) MenuSetApplicationMenu(menu *menu.Menu) {
f.mainWindow.SetApplicationMenu(menu)
}
func (f *Frontend) MenuUpdateApplicationMenu() {
f.mainWindow.SetApplicationMenu(f.mainWindow.applicationMenu)
}
func (w *Window) SetApplicationMenu(inmenu *menu.Menu) {
if inmenu == nil {
return
}
// Setup accelerator group
w.accels = C.gtk_accel_group_new()
C.gtk_window_add_accel_group(w.asGTKWindow(), w.accels)
menuItemToId = make(map[*menu.MenuItem]int)
menuIdToItem = make(map[int]*menu.MenuItem)
gtkCheckboxCache = make(map[*menu.MenuItem][]*C.GtkWidget)
gtkMenuCache = make(map[*menu.MenuItem]*C.GtkWidget)
gtkRadioMenuCache = make(map[*menu.MenuItem][]*C.GtkWidget)
gtkSignalHandlers = make(map[*C.GtkWidget]C.gulong)
gtkSignalToMenuItem = make(map[*C.GtkWidget]*menu.MenuItem)
// Increase ref count?
w.menubar = C.gtk_menu_bar_new()
processMenu(w, inmenu)
C.gtk_widget_show(w.menubar)
}
func processMenu(window *Window, menu *menu.Menu) {
for _, menuItem := range menu.Items {
submenu := processSubmenu(menuItem, window.accels)
C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(window.menubar)), submenu)
}
}
func processSubmenu(menuItem *menu.MenuItem, group *C.GtkAccelGroup) *C.GtkWidget {
existingMenu := gtkMenuCache[menuItem]
if existingMenu != nil {
return existingMenu
}
gtkMenu := C.gtk_menu_new()
submenu := GtkMenuItemWithLabel(menuItem.Label)
for _, menuItem := range menuItem.SubMenu.Items {
menuID := menuIdCounter
menuIdToItem[menuID] = menuItem
menuItemToId[menuItem] = menuID
menuIdCounter++
processMenuItem(gtkMenu, menuItem, group)
}
C.gtk_menu_item_set_submenu(C.toGtkMenuItem(unsafe.Pointer(submenu)), gtkMenu)
gtkMenuCache[menuItem] = existingMenu
return submenu
}
var currentRadioGroup *C.GSList
func processMenuItem(parent *C.GtkWidget, menuItem *menu.MenuItem, group *C.GtkAccelGroup) {
if menuItem.Hidden {
return
}
if menuItem.Type != menu.RadioType {
currentRadioGroup = nil
}
if menuItem.Type == menu.SeparatorType {
result := C.gtk_separator_menu_item_new()
C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(parent)), result)
return
}
var result *C.GtkWidget
switch menuItem.Type {
case menu.TextType:
result = GtkMenuItemWithLabel(menuItem.Label)
case menu.CheckboxType:
result = GtkCheckMenuItemWithLabel(menuItem.Label)
if menuItem.Checked {
C.gtk_check_menu_item_set_active(C.toGtkCheckMenuItem(unsafe.Pointer(result)), 1)
}
gtkCheckboxCache[menuItem] = append(gtkCheckboxCache[menuItem], result)
case menu.RadioType:
result = GtkRadioMenuItemWithLabel(menuItem.Label, currentRadioGroup)
currentRadioGroup = C.gtk_radio_menu_item_get_group(C.toGtkRadioMenuItem(unsafe.Pointer(result)))
if menuItem.Checked {
C.gtk_check_menu_item_set_active(C.toGtkCheckMenuItem(unsafe.Pointer(result)), 1)
}
gtkRadioMenuCache[menuItem] = append(gtkRadioMenuCache[menuItem], result)
case menu.SubmenuType:
result = processSubmenu(menuItem, group)
}
C.gtk_menu_shell_append(C.toGtkMenuShell(unsafe.Pointer(parent)), result)
C.gtk_widget_show(result)
if menuItem.Click != nil {
handler := C.connectClick(result)
gtkSignalHandlers[result] = handler
gtkSignalToMenuItem[result] = menuItem
}
if menuItem.Disabled {
C.gtk_widget_set_sensitive(result, 0)
}
if menuItem.Accelerator != nil {
key, mods := acceleratorToGTK(menuItem.Accelerator)
C.addAccelerator(result, group, key, mods)
}
}