mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 11:30:18 +08:00
Support loading multiple tray icons
This commit is contained in:
parent
16b872352d
commit
a0774cf71c
@ -6,6 +6,7 @@
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include "json.h"
|
||||
#include "hashmap.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
// Macros to make it slightly more sane
|
||||
#define msg objc_msgSend
|
||||
@ -80,8 +81,7 @@ extern const unsigned char *assets[];
|
||||
extern const unsigned char runtime;
|
||||
|
||||
// Tray icon
|
||||
extern const unsigned int trayIconLength;
|
||||
extern const unsigned char *trayIcon[];
|
||||
extern const unsigned char *trayIcons[];
|
||||
|
||||
// MAIN DEBUG FLAG
|
||||
int debug;
|
||||
@ -98,6 +98,9 @@ struct hashmap_s menuItemMapForTrayMenu;
|
||||
// RadioGroup map for the tray menu. Maps a menuitem id with its associated radio group items
|
||||
struct hashmap_s radioGroupMapForTrayMenu;
|
||||
|
||||
// A cache for all our tray icons
|
||||
struct hashmap_s trayIconCache;
|
||||
|
||||
// contextMenuMap is a hashmap of context menus keyed on a string ID
|
||||
struct hashmap_s contextMenuMap;
|
||||
|
||||
@ -215,6 +218,7 @@ struct Application {
|
||||
const char *trayMenuAsJSON;
|
||||
const char *trayType;
|
||||
const char *trayLabel;
|
||||
const char *trayIconName;
|
||||
JsonNode *processedTrayMenu;
|
||||
id statusItem;
|
||||
|
||||
@ -730,6 +734,13 @@ void allocateTrayHashMaps(struct Application *app) {
|
||||
Fatal(app, "Not enough memory to allocate radioGroupMapForTrayMenu!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate the Tray Icons
|
||||
if( 0 != hashmap_create((const unsigned)4, &trayIconCache)) {
|
||||
// Couldn't allocate map
|
||||
Fatal(app, "Not enough memory to allocate radioGroupMapForTrayMenu!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void allocateContextMenuHashMaps(struct Application *app) {
|
||||
@ -798,6 +809,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
||||
result->trayMenuAsJSON = NULL;
|
||||
result->trayType = NULL;
|
||||
result->trayLabel = NULL;
|
||||
result->trayIconName = NULL;
|
||||
result->processedTrayMenu = NULL;
|
||||
result->statusItem = NULL;
|
||||
|
||||
@ -819,7 +831,7 @@ int freeHashmapItem(void *const context, struct hashmap_element_s *const e) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int freeNSMenu(void *const context, struct hashmap_element_s *const e) {
|
||||
int releaseNSObject(void *const context, struct hashmap_element_s *const e) {
|
||||
msg(e->data, s("release"));
|
||||
return -1;
|
||||
}
|
||||
@ -873,7 +885,7 @@ void destroyContextMenus(struct Application *app) {
|
||||
|
||||
// Free context menus
|
||||
if( hashmap_num_entries(&contextMenuMap) > 0 ) {
|
||||
if (0!=hashmap_iterate_pairs(&contextMenuMap, freeNSMenu, NULL)) {
|
||||
if (0!=hashmap_iterate_pairs(&contextMenuMap, releaseNSObject, NULL)) {
|
||||
Fatal(app, "failed to deallocate hashmap entries!");
|
||||
}
|
||||
}
|
||||
@ -892,6 +904,16 @@ void destroyContextMenus(struct Application *app) {
|
||||
|
||||
void destroyTray(struct Application *app) {
|
||||
|
||||
// Release the tray cache images
|
||||
if( hashmap_num_entries(&trayIconCache) > 0 ) {
|
||||
if (0!=hashmap_iterate_pairs(&radioGroupMapForApplicationMenu, releaseNSObject, NULL)) {
|
||||
Fatal(app, "failed to release hashmap entries!");
|
||||
}
|
||||
}
|
||||
|
||||
//Free radio groups hashmap
|
||||
hashmap_destroy(&trayIconCache);
|
||||
|
||||
// If we don't have a tray, exit!
|
||||
if( app->trayMenuAsJSON == NULL ) {
|
||||
return;
|
||||
@ -1306,10 +1328,11 @@ void SetMenu(struct Application *app, const char *menuAsJSON) {
|
||||
}
|
||||
|
||||
// SetTray sets the initial tray menu for the application
|
||||
void SetTray(struct Application *app, const char *trayMenuAsJSON, const char *trayType, const char *trayLabel) {
|
||||
void SetTray(struct Application *app, const char *trayMenuAsJSON, const char *trayType, const char *trayLabel, const char *trayIconName) {
|
||||
app->trayMenuAsJSON = trayMenuAsJSON;
|
||||
app->trayType = trayType;
|
||||
app->trayLabel = trayLabel;
|
||||
app->trayIconName = trayIconName;
|
||||
}
|
||||
|
||||
// SetContextMenus sets the context menu map for this application
|
||||
@ -2228,11 +2251,42 @@ void UpdateTrayLabel(struct Application *app, const char *label) {
|
||||
msg(statusBarButton, s("setTitle:"), str(label));
|
||||
}
|
||||
|
||||
void processTrayIconData(struct Application *app) {
|
||||
|
||||
unsigned int count = 0;
|
||||
while( 1 ) {
|
||||
const unsigned char *name = trayIcons[count++];
|
||||
if( name == 0x00 ) {
|
||||
break;
|
||||
}
|
||||
const unsigned char *lengthAsString = trayIcons[count++];
|
||||
if( name == 0x00 ) {
|
||||
break;
|
||||
}
|
||||
const unsigned char *data = trayIcons[count++];
|
||||
if( data == 0x00 ) {
|
||||
break;
|
||||
}
|
||||
int length = atoi((const char *)lengthAsString);
|
||||
printf("Got tray name: %s with data %p\n", name, data);
|
||||
printf("Length = %d\n", length);
|
||||
|
||||
// Create the icon and add to the hashmap
|
||||
id imageData = msg(c("NSData"), s("dataWithBytes:length:"), data, length);
|
||||
id trayImage = ALLOC("NSImage");
|
||||
msg(trayImage, s("initWithData:"), imageData);
|
||||
hashmap_put(&trayIconCache, (const char *)name, strlen((const char *)name), trayImage);
|
||||
}
|
||||
}
|
||||
|
||||
void parseTrayData(struct Application *app) {
|
||||
|
||||
// Allocate the hashmaps we need
|
||||
allocateTrayHashMaps(app);
|
||||
|
||||
// Process Tray icon data
|
||||
processTrayIconData(app);
|
||||
|
||||
// Create a new menu
|
||||
id traymenu = createMenu(str(""));
|
||||
|
||||
@ -2247,11 +2301,9 @@ void parseTrayData(struct Application *app) {
|
||||
id statusBarButton = msg(statusItem, s("button"));
|
||||
|
||||
if( STREQ(app->trayType, "icon") ) {
|
||||
// If we have a tray icon
|
||||
if ( trayIconLength > 0 ) {
|
||||
id imageData = msg(c("NSData"), s("dataWithBytes:length:"), trayIcon, trayIconLength);
|
||||
id trayImage = ALLOC("NSImage");
|
||||
msg(trayImage, s("initWithData:"), imageData);
|
||||
// Get the icon
|
||||
if ( app->trayIconName != NULL ) {
|
||||
id trayImage = hashmap_get(&trayIconCache, app->trayIconName, strlen(app->trayIconName));
|
||||
msg(statusBarButton, s("setImage:"), trayImage);
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ func (a *Application) processPlatformSettings() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
C.SetTray(a.app, a.string2CString(string(trayMenuJSON)), a.string2CString(string(tray.Type)), a.string2CString(tray.Label))
|
||||
C.SetTray(a.app, a.string2CString(string(trayMenuJSON)), a.string2CString(string(tray.Type)), a.string2CString(tray.Label), a.string2CString(tray.Icon))
|
||||
}
|
||||
|
||||
// Process context menus
|
||||
|
@ -13,7 +13,7 @@ extern void SetAppearance(void *, const char *);
|
||||
extern void WebviewIsTransparent(void *);
|
||||
extern void WindowBackgroundIsTranslucent(void *);
|
||||
extern void SetMenu(void *, const char *);
|
||||
extern void SetTray(void *, const char *, const char *, const char *);
|
||||
extern void SetTray(void *, const char *, const char *, const char *, const char *);
|
||||
extern void SetContextMenus(void *, const char *);
|
||||
|
||||
#endif
|
@ -4,55 +4,91 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/wailsapp/wails/v2/internal/fs"
|
||||
"github.com/leaanthony/slicer"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (d *DesktopBuilder) convertToHexLiteral(bytes []byte) string {
|
||||
result := ""
|
||||
for _, b := range bytes {
|
||||
result += fmt.Sprintf("0x%x, ", b)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// desktop_linux.go will compile the tray icon found at <projectdir>/trayicon.png into the application
|
||||
func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error {
|
||||
|
||||
// Determine icon file
|
||||
iconFile := filepath.Join(options.ProjectData.Path, "trayicon.png")
|
||||
|
||||
var err error
|
||||
|
||||
// Get all the tray icon filenames
|
||||
trayIconDirectory := filepath.Join(options.ProjectData.Path, "trayicons")
|
||||
trayIconFilenames, err := filepath.Glob(trayIconDirectory + "/*.png")
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup target
|
||||
targetFilename := "trayicon"
|
||||
targetFilename := "trayicons"
|
||||
targetFile := filepath.Join(assetDir, targetFilename+".c")
|
||||
d.addFileToDelete(targetFile)
|
||||
|
||||
var dataBytes []byte
|
||||
|
||||
// If the icon file exists, load it up
|
||||
if fs.FileExists(iconFile) {
|
||||
// Load the tray icon
|
||||
dataBytes, err = ioutil.ReadFile(iconFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Use a strings builder
|
||||
var cdata strings.Builder
|
||||
|
||||
// Write header
|
||||
header := `// trayicon.c
|
||||
header := `// trayicons.c
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL.
|
||||
// This file was auto-generated. DO NOT MODIFY.
|
||||
|
||||
`
|
||||
cdata.WriteString(header)
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned int trayIconLength = %d;\n", len(dataBytes)))
|
||||
cdata.WriteString("const unsigned char trayIcon[] = { ")
|
||||
|
||||
// Convert each byte to hex
|
||||
for _, b := range dataBytes {
|
||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||
var variableList slicer.StringSlicer
|
||||
|
||||
// Loop over icons
|
||||
for count, filename := range trayIconFilenames {
|
||||
|
||||
// Load the tray icon
|
||||
dataBytes, err = ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
iconname := strings.TrimSuffix(filepath.Base(filename), ".png")
|
||||
trayIconName := fmt.Sprintf("trayIcon%dName", count)
|
||||
variableList.Add(trayIconName)
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconName, d.convertToHexLiteral([]byte(iconname))))
|
||||
|
||||
trayIconLength := fmt.Sprintf("trayIcon%dLength", count)
|
||||
variableList.Add(trayIconLength)
|
||||
lengthAsString := strconv.Itoa(len(dataBytes))
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", trayIconLength, d.convertToHexLiteral([]byte(lengthAsString))))
|
||||
|
||||
trayIconData := fmt.Sprintf("trayIcon%dData", count)
|
||||
variableList.Add(trayIconData)
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", trayIconData))
|
||||
|
||||
// Convert each byte to hex
|
||||
for _, b := range dataBytes {
|
||||
cdata.WriteString(fmt.Sprintf("0x%x, ", b))
|
||||
}
|
||||
|
||||
cdata.WriteString("0x00 };\n")
|
||||
}
|
||||
|
||||
cdata.WriteString("0x00 };\n")
|
||||
// Write out main trayIcons data
|
||||
cdata.WriteString("const unsigned char *trayIcons[] = { ")
|
||||
cdata.WriteString(variableList.Join(", "))
|
||||
cdata.WriteString(", 0x00 };\n")
|
||||
|
||||
err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600)
|
||||
if err != nil {
|
||||
|
@ -7,8 +7,20 @@ const (
|
||||
TrayLabel TrayType = "label"
|
||||
)
|
||||
|
||||
// TrayOptions are the options
|
||||
type TrayOptions struct {
|
||||
Type TrayType
|
||||
// Type is the type of tray item we want
|
||||
Type TrayType
|
||||
|
||||
// Label is what is displayed initially when the type is TrayLabel
|
||||
Label string
|
||||
Menu *Menu
|
||||
|
||||
// Icon is the name of the tray icon we wish to display.
|
||||
// These are read up during build from <projectdir>/trayicons and
|
||||
// the filenames are used as IDs, minus the extension
|
||||
// EG: <projectdir>/trayicons/main.png can be referenced here with "main"
|
||||
Icon string
|
||||
|
||||
// Menu is the initial menu we wish to use for the tray
|
||||
Menu *Menu
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ func main() {
|
||||
//Type: menu.TrayLabel,
|
||||
Type: menu.TrayIcon,
|
||||
Label: "Hi Go BitBar!",
|
||||
Icon: "main",
|
||||
Menu: createApplicationTray(),
|
||||
},
|
||||
},
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB |
Loading…
Reference in New Issue
Block a user