From fa6adad4ab54299ddda254d250e21f077e2939c5 Mon Sep 17 00:00:00 2001 From: Travis McLane Date: Thu, 21 Sep 2023 15:46:37 -0500 Subject: [PATCH] [v3 linux] wip: systray implementation --- v3/pkg/application/linux_cgo.go | 73 +++++++++++++++++++++++++- v3/pkg/application/systemtray_linux.go | 32 +++++------ 2 files changed, 89 insertions(+), 16 deletions(-) diff --git a/v3/pkg/application/linux_cgo.go b/v3/pkg/application/linux_cgo.go index b389d99d6..49443ad5a 100644 --- a/v3/pkg/application/linux_cgo.go +++ b/v3/pkg/application/linux_cgo.go @@ -4,6 +4,9 @@ package application import ( "fmt" + "log" + "os" + "path/filepath" "strings" "unsafe" @@ -12,7 +15,7 @@ import ( ) /* -#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 +#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0 ayatana-appindicator3-0.1 #include #include @@ -20,12 +23,15 @@ import ( #include #include #include + #ifdef G_APPLICATION_DEFAULT_FLAGS #define APPLICATION_DEFAULT_FLAGS G_APPLICATION_DEFAULT_FLAGS #else #define APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE #endif +#include + typedef struct CallbackID { unsigned int value; @@ -1141,3 +1147,68 @@ func runSaveFileDialog(dialog *SaveFileDialogStruct) (string, error) { return results[0], nil } + +// systray +func systrayNew(label string) pointer { + labelStr := C.CString(label) + defer C.free(unsafe.Pointer(labelStr)) + emptyStr := C.CString("") + defer C.free(unsafe.Pointer(emptyStr)) + indicator := C.app_indicator_new(labelStr, labelStr, C.APP_INDICATOR_CATEGORY_APPLICATION_STATUS) + + trayMenu := C.gtk_menu_new() + // item := C.gtk_menu_item_new_with_label(labelStr) + // C.gtk_menu_shell_append((*C.GtkMenuShell)(unsafe.Pointer(trayMenu)), item) + // C.gtk_widget_show(item) + C.app_indicator_set_status(indicator, C.APP_INDICATOR_STATUS_ACTIVE) + C.app_indicator_set_menu(indicator, (*C.GtkMenu)(unsafe.Pointer(trayMenu))) + iconStr := C.CString("wails-systray-icon") + defer C.free(unsafe.Pointer(iconStr)) + C.app_indicator_set_icon_full(indicator, iconStr, emptyStr) + systraySetLabel(pointer(indicator), label) + return pointer(indicator) +} + +func systraySetTitle(tray pointer, title string) { + titleStr := C.CString(title) + defer C.free(unsafe.Pointer(titleStr)) + C.app_indicator_set_title((*C.AppIndicator)(tray), titleStr) +} + +func systraySetLabel(tray pointer, label string) { + labelStr := C.CString(label) + defer C.free(unsafe.Pointer(labelStr)) + emptyStr := C.CString("") + defer C.free(unsafe.Pointer(emptyStr)) + C.app_indicator_set_label((*C.AppIndicator)(tray), labelStr, labelStr) +} + +func systrayMenuSet(tray pointer, menu pointer) { + // C.app_indicator_set_menu((*C.AppIndicator)(tray), (*C.GtkMenu)(unsafe.Pointer(menu))) +} + +/* +GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(icon_file_path, NULL); +int width, height; +gdk_pixbuf_get_file_info (icon_file_path, &width, &height); +gtk_icon_theme_add_builtin_icon ("custom_icon", width, pixbuf); +g_object_unref (G_OBJECT (pixbuf)); + +GtkToolItem *toolbar_item = gtk_toggle_tool_button_new(); +gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON(toolbar_item), "custom_icon"); +*/ + +func systraySetTemplateIcon(tray pointer, icon []byte) { + dirname, err := os.UserHomeDir() + if err != nil { + log.Fatal(err) + } + file := filepath.Join(dirname, ".icons", "wails-systray-icon.png") + if err := os.WriteFile(file, icon, 0666); err != nil { + log.Fatal(err) + } + systrayStr := C.CString("wails-systray-icon") + defer C.free(unsafe.Pointer(systrayStr)) + + C.app_indicator_set_attention_icon_full((*C.AppIndicator)(tray), systrayStr, systrayStr) +} diff --git a/v3/pkg/application/systemtray_linux.go b/v3/pkg/application/systemtray_linux.go index f2cee68d1..7503d451c 100644 --- a/v3/pkg/application/systemtray_linux.go +++ b/v3/pkg/application/systemtray_linux.go @@ -1,4 +1,4 @@ -//go:build linux +//go:build linux package application @@ -12,6 +12,7 @@ type linuxSystemTray struct { iconPosition int isTemplateIcon bool + tray pointer } func (s *linuxSystemTray) setIconPosition(position int) { @@ -37,49 +38,50 @@ func (s *linuxSystemTray) bounds() (*Rect, error) { func (s *linuxSystemTray) run() { InvokeSync(func() { + label := s.label + if label == "" { + label = "Wails" + } + s.tray = systrayNew(label) + // if s.nsStatusItem != nil { // Fatal("System tray '%d' already running", s.id) // } // s.nsStatusItem = unsafe.Pointer(C.systemTrayNew()) if s.label != "" { - // C.systemTraySetLabel(s.nsStatusItem, C.CString(s.label)) + systraySetTitle(s.tray, s.label) } if s.icon != nil { - // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&s.icon[0]), C.int(len(s.icon)))) - // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + s.setIcon(s.icon) } if s.menu != nil { s.menu.Update() - // Convert impl to macosMenu object - // s.nsMenu = (s.menu.impl).(*macosMenu).nsMenu - // C.systemTraySetMenu(s.nsStatusItem, s.nsMenu) + menu := (s.menu.impl).(*linuxMenu).menu + systrayMenuSet(s.tray, (menu.impl).(*linuxMenu).native) } - }) } func (s *linuxSystemTray) setIcon(icon []byte) { s.icon = icon InvokeSync(func() { - // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) - // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + systraySetTemplateIcon(s.tray, icon) }) } func (s *linuxSystemTray) setDarkModeIcon(icon []byte) { s.icon = icon InvokeSync(func() { - // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) - // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + systraySetTemplateIcon(s.tray, icon) }) } func (s *linuxSystemTray) setTemplateIcon(icon []byte) { s.icon = icon s.isTemplateIcon = true + globalApplication.dispatchOnMainThread(func() { - // s.nsImage = unsafe.Pointer(C.imageFromBytes((*C.uchar)(&icon[0]), C.int(len(icon)))) - // C.systemTraySetIcon(s.nsStatusItem, s.nsImage, C.int(s.iconPosition), C.bool(s.isTemplateIcon)) + systraySetTemplateIcon(s.tray, icon) }) } @@ -99,7 +101,7 @@ func (s *linuxSystemTray) openMenu() { func (s *linuxSystemTray) setLabel(label string) { s.label = label - // C.systemTraySetLabel(s.nsStatusItem, C.CString(label)) + systraySetLabel(s.tray, label) } func (s *linuxSystemTray) destroy() {