From 91fb3501c53680494c9f3c4b327ae3c1b0af5860 Mon Sep 17 00:00:00 2001 From: Lea Anthony Date: Wed, 30 Dec 2020 08:27:00 +1100 Subject: [PATCH] Support custom dialog icons. Move icons to new dir. --- v2/internal/ffenestri/ffenestri_darwin.c | 99 +++++++++++++++++- v2/internal/project/project.go | 8 ++ v2/pkg/commands/build/desktop.go | 13 ++- v2/pkg/commands/build/desktop_darwin.go | 94 ++++++++++++++++- .../internal/packager/icons/dialog/info.png | Bin 0 -> 6891 bytes v2/pkg/commands/build/packager_darwin.go | 9 +- v2/test/kitchensink/icon.png | Bin 6765 -> 0 bytes v2/test/kitchensink/{ => icons}/appicon.png | Bin v2/test/kitchensink/icons/dialog/info.png | Bin 0 -> 6891 bytes .../{trayicons => icons/tray}/dark.png | Bin .../{trayicons => icons/tray}/light.png | Bin .../{trayicons => icons/tray}/svelte.png | Bin 12 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 v2/pkg/commands/build/internal/packager/icons/dialog/info.png delete mode 100644 v2/test/kitchensink/icon.png rename v2/test/kitchensink/{ => icons}/appicon.png (100%) create mode 100644 v2/test/kitchensink/icons/dialog/info.png rename v2/test/kitchensink/{trayicons => icons/tray}/dark.png (100%) rename v2/test/kitchensink/{trayicons => icons/tray}/light.png (100%) rename v2/test/kitchensink/{trayicons => icons/tray}/svelte.png (100%) diff --git a/v2/internal/ffenestri/ffenestri_darwin.c b/v2/internal/ffenestri/ffenestri_darwin.c index 167b8605..3fa1c64e 100644 --- a/v2/internal/ffenestri/ffenestri_darwin.c +++ b/v2/internal/ffenestri/ffenestri_darwin.c @@ -20,6 +20,7 @@ #define url(input) msg(c("NSURL"), s("fileURLWithPath:"), str(input)) #define ALLOC(classname) msg(c(classname), s("alloc")) +#define ALLOC_INIT(classname) msg(msg(c(classname), s("alloc")), s("init")) #define GET_FRAME(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("frame")) #define GET_BOUNDS(receiver) ((CGRect(*)(id, SEL))objc_msgSend_stret)(receiver, s("bounds")) @@ -86,14 +87,24 @@ #define NSImageAbove 5 #define NSImageOverlaps 6 +#define NSAlertStyleWarning 0 +#define NSAlertStyleInformational 1 +#define NSAlertStyleCritical 2 + +#define NSAlertFirstButtonReturn 1000 +#define NSAlertSecondButtonReturn 1001 +#define NSAlertThirdButtonReturn 1002 // References to assets extern const unsigned char *assets[]; extern const unsigned char runtime; -// Tray icon +// Tray icons extern const unsigned char *trayIcons[]; +// Dialog icons +extern const unsigned char *dialogIcons[]; + // MAIN DEBUG FLAG int debug; @@ -121,6 +132,9 @@ struct hashmap_s menuItemMapForContextMenus; // RadioGroup map for the context menus. Maps a menuitem id with its associated radio group items struct hashmap_s radioGroupMapForContextMenus; +// A cache for all our dialog icons +struct hashmap_s dialogIconCache; + // Context menu data is given by the frontend when clicking a context menu. // We send this to the backend when an item is selected; const char *contextMenuData; @@ -927,12 +941,23 @@ void destroyContextMenus(struct Application *app) { } +void freeDialogIconCache(struct Application *app) { + // Release the tray cache images + if( hashmap_num_entries(&dialogIconCache) > 0 ) { + if (0!=hashmap_iterate_pairs(&dialogIconCache, releaseNSObject, NULL)) { + Fatal(app, "failed to release hashmap entries!"); + } + } + + //Free radio groups hashmap + hashmap_destroy(&dialogIconCache); +} void destroyTray(struct Application *app) { // Release the tray cache images if( hashmap_num_entries(&trayIconCache) > 0 ) { - if (0!=hashmap_iterate_pairs(&radioGroupMapForApplicationMenu, releaseNSObject, NULL)) { + if (0!=hashmap_iterate_pairs(&trayIconCache, releaseNSObject, NULL)) { Fatal(app, "failed to release hashmap entries!"); } } @@ -996,6 +1021,9 @@ void DestroyApplication(struct Application *app) { // Destroy the context menus destroyContextMenus(app); + // Free dialog icon cache + freeDialogIconCache(app); + // Clear context menu data if we have it if( contextMenuData != NULL ) { MEMFREE(contextMenuData); @@ -1140,6 +1168,35 @@ void SetPosition(struct Application *app, int x, int y) { ); } + +void showDialog(struct Application *app) { + ON_MAIN_THREAD( + id alert = ALLOC_INIT("NSAlert"); + msg(alert, s("setAlertStyle:"), NSAlertStyleInformational); + msg(alert, s("setMessageText:"), str("Hello World!")); + msg(alert, s("setInformativeText:"), str("Hello again World!")); + msg(alert, s("addButtonWithTitle:"), str("One")); + msg(alert, s("addButtonWithTitle:"), str("Two")); + msg(alert, s("addButtonWithTitle:"), str("Three")); + msg(alert, s("addButtonWithTitle:"), str("Four")); + id dialogImage = hashmap_get(&dialogIconCache, "info", strlen("info")); + msg(alert, s("setIcon:"), dialogImage); + int response = (int)msg(alert, s("runModal")); + if( response == NSAlertFirstButtonReturn ) { + printf("FIRST BUTTON PRESSED"); + } + else if( response == NSAlertSecondButtonReturn ) { + printf("SECOND BUTTON PRESSED"); + } + else if( response == NSAlertThirdButtonReturn ) { + printf("THIRD BUTTON PRESSED"); + } + else { + printf("FORTH BUTTON PRESSED"); + } + ); +} + // OpenDialog opens a dialog to select files/directories void OpenDialog(struct Application *app, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories) { Debug(app, "OpenDialog Called with callback id: %s", callbackID); @@ -1365,6 +1422,7 @@ void SetContextMenus(struct Application *app, const char *contextMenusAsJSON) { app->contextMenusAsJSON = contextMenusAsJSON; } + void SetBindings(struct Application *app, const char *bindings) { const char* temp = concat("window.wailsbindings = \"", bindings); const char* jscall = concat(temp, "\";"); @@ -2409,6 +2467,40 @@ void UpdateContextMenus(struct Application *app, const char *contextMenusAsJSON) ); } +void processDialogIcons(struct Application *app) { + + // Allocate the Dialog icon hashmap + if( 0 != hashmap_create((const unsigned)4, &dialogIconCache)) { + // Couldn't allocate map + Fatal(app, "Not enough memory to allocate dialogIconCache!"); + return; + } + + unsigned int count = 0; + while( 1 ) { + const unsigned char *name = dialogIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *lengthAsString = dialogIcons[count++]; + if( name == 0x00 ) { + break; + } + const unsigned char *data = dialogIcons[count++]; + if( data == 0x00 ) { + break; + } + int length = atoi((const char *)lengthAsString); + + // Create the icon and add to the hashmap + id imageData = msg(c("NSData"), s("dataWithBytes:length:"), data, length); + id dialogImage = ALLOC("NSImage"); + msg(dialogImage, s("initWithData:"), imageData); + hashmap_put(&dialogIconCache, (const char *)name, strlen((const char *)name), dialogImage); + } + +} + void Run(struct Application *app, int argc, char **argv) { @@ -2595,6 +2687,9 @@ void Run(struct Application *app, int argc, char **argv) { parseContextMenus(app); } + // Process dialog icons + processDialogIcons(app); + // We set it to be invisible by default. It will become visible when everything has initialised msg(app->mainWindow, s("setIsVisible:"), NO); diff --git a/v2/internal/project/project.go b/v2/internal/project/project.go index a196d3ac..947315ff 100644 --- a/v2/internal/project/project.go +++ b/v2/internal/project/project.go @@ -25,6 +25,9 @@ type Project struct { // The path to the project directory Path string + // Icons directory + IconsDir string `json:"icons_dir"` + // The output filename OutputFilename string `json:"outputfilename"` @@ -72,6 +75,11 @@ func Load(projectPath string) (*Project, error) { result.Name = "wailsapp" } + // Set default icons directory if none given + if result.IconsDir == "" { + result.IconsDir = filepath.Join(result.Path, "icons") + } + // Fix up OutputFilename switch runtime.GOOS { case "windows": diff --git a/v2/pkg/commands/build/desktop.go b/v2/pkg/commands/build/desktop.go index 2b98d413..e06d3da7 100644 --- a/v2/pkg/commands/build/desktop.go +++ b/v2/pkg/commands/build/desktop.go @@ -24,6 +24,11 @@ func newDesktopBuilder() *DesktopBuilder { func (d *DesktopBuilder) BuildAssets(options *Options) error { var err error + // Check icon directory exists + if !fs.DirExists(options.ProjectData.IconsDir) { + return fmt.Errorf("icon directory %s does not exist", options.ProjectData.IconsDir) + } + // Get a list of assets from the HTML assets, err := d.BaseBuilder.ExtractAssets() if err != nil { @@ -74,6 +79,12 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti return err } + // Process Dialog Icons + err = d.processDialogIcons(assetDir, options) + if err != nil { + return err + } + outputLogger.Println("done.") return nil @@ -84,7 +95,7 @@ func (d *DesktopBuilder) BuildBaseAssets(assets *html.AssetBundle, options *Opti func (d *DesktopBuilder) processApplicationIcon(assetDir string) error { // Copy default icon if one doesn't exist - iconFile := filepath.Join(d.projectData.Path, "icon.png") + iconFile := filepath.Join(d.projectData.IconsDir, "appicon.png") if !fs.FileExists(iconFile) { err := fs.CopyFile(defaultIconPath(), iconFile) if err != nil { diff --git a/v2/pkg/commands/build/desktop_darwin.go b/v2/pkg/commands/build/desktop_darwin.go index f65badec..a1c7cdc1 100644 --- a/v2/pkg/commands/build/desktop_darwin.go +++ b/v2/pkg/commands/build/desktop_darwin.go @@ -21,13 +21,13 @@ func (d *DesktopBuilder) convertToHexLiteral(bytes []byte) string { return result } -// desktop_linux.go will compile the tray icon found at /trayicon.png into the application +// We will compile all tray icons found at /icons/tray/*.png into the application func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) error { var err error // Get all the tray icon filenames - trayIconDirectory := filepath.Join(options.ProjectData.Path, "trayicons") + trayIconDirectory := filepath.Join(options.ProjectData.IconsDir, "tray") var trayIconFilenames []string if fs.DirExists(trayIconDirectory) { trayIconFilenames, err = filepath.Glob(trayIconDirectory + "/*.png") @@ -101,3 +101,93 @@ func (d *DesktopBuilder) processTrayIcons(assetDir string, options *Options) err } return nil } + +// We will compile all dialog icons found at /icons/dialog/*.png into the application +func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) error { + + var err error + + // Get all the dialog icon filenames + dialogIconDirectory := filepath.Join(options.ProjectData.IconsDir, "dialog") + var dialogIconFilenames []string + + // If the user has no custom dialog icons, copy the defaults + if !fs.DirExists(dialogIconDirectory) { + defaultDialogIconsDirectory := fs.RelativePath("./internal/packager/icons/dialog") + err := fs.CopyDir(defaultDialogIconsDirectory, dialogIconDirectory) + if err != nil { + return err + } + } + + dialogIconFilenames, err = filepath.Glob(dialogIconDirectory + "/*.png") + if err != nil { + log.Fatal(err) + return err + } + + // Setup target + targetFilename := "dialogicons" + targetFile := filepath.Join(assetDir, targetFilename+".c") + //d.addFileToDelete(targetFile) + + var dataBytes []byte + + // Use a strings builder + var cdata strings.Builder + + // Write header + header := `// dialogicons.c +// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL. +// This file was auto-generated. DO NOT MODIFY. + +` + cdata.WriteString(header) + + var variableList slicer.StringSlicer + + // Loop over icons + for count, filename := range dialogIconFilenames { + + // Load the tray icon + dataBytes, err = ioutil.ReadFile(filename) + if err != nil { + return err + } + + iconname := strings.TrimSuffix(filepath.Base(filename), ".png") + dialogIconName := fmt.Sprintf("dialogIcon%dName", count) + variableList.Add(dialogIconName) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, d.convertToHexLiteral([]byte(iconname)))) + + dialogIconLength := fmt.Sprintf("dialogIcon%dLength", count) + variableList.Add(dialogIconLength) + lengthAsString := strconv.Itoa(len(dataBytes)) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, d.convertToHexLiteral([]byte(lengthAsString)))) + + dialogIconData := fmt.Sprintf("dialogIcon%dData", count) + variableList.Add(dialogIconData) + cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData)) + + // Convert each byte to hex + for _, b := range dataBytes { + cdata.WriteString(fmt.Sprintf("0x%x, ", b)) + } + + cdata.WriteString("0x00 };\n") + } + + // Write out main dialogIcons data + cdata.WriteString("const unsigned char *dialogIcons[] = { ") + cdata.WriteString(variableList.Join(", ")) + if len(dialogIconFilenames) > 0 { + cdata.WriteString(", ") + } + cdata.WriteString("0x00 };\n") + + err = ioutil.WriteFile(targetFile, []byte(cdata.String()), 0600) + if err != nil { + return err + } + return nil +} diff --git a/v2/pkg/commands/build/internal/packager/icons/dialog/info.png b/v2/pkg/commands/build/internal/packager/icons/dialog/info.png new file mode 100644 index 0000000000000000000000000000000000000000..0812e0ff7167a60eb0582baec98b313bfc3946e7 GIT binary patch literal 6891 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 literal 0 HcmV?d00001 diff --git a/v2/pkg/commands/build/packager_darwin.go b/v2/pkg/commands/build/packager_darwin.go index 7dad5453..a52e0116 100644 --- a/v2/pkg/commands/build/packager_darwin.go +++ b/v2/pkg/commands/build/packager_darwin.go @@ -47,7 +47,7 @@ func packageApplication(options *Options) error { } // Generate Icons - err = processApplicationIcon(resourceDir) + err = processApplicationIcon(resourceDir, options.ProjectData.IconsDir) if err != nil { return err } @@ -129,12 +129,9 @@ func newPlistData(title, exe, packageID, version, author string) *plistData { } } -func processApplicationIcon(resourceDir string) (err error) { +func processApplicationIcon(resourceDir string, iconsDir string) (err error) { - appIcon, err := fs.RelativeToCwd("appicon.png") - if err != nil { - return err - } + appIcon := filepath.Join(iconsDir, "appicon.png") // Install default icon if one doesn't exist if !fs.FileExists(appIcon) { diff --git a/v2/test/kitchensink/icon.png b/v2/test/kitchensink/icon.png deleted file mode 100644 index a2b304154044f39db7f59fe497728f64bf4d551f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6765 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 literal 0 HcmV?d00001 diff --git a/v2/test/kitchensink/trayicons/dark.png b/v2/test/kitchensink/icons/tray/dark.png similarity index 100% rename from v2/test/kitchensink/trayicons/dark.png rename to v2/test/kitchensink/icons/tray/dark.png diff --git a/v2/test/kitchensink/trayicons/light.png b/v2/test/kitchensink/icons/tray/light.png similarity index 100% rename from v2/test/kitchensink/trayicons/light.png rename to v2/test/kitchensink/icons/tray/light.png diff --git a/v2/test/kitchensink/trayicons/svelte.png b/v2/test/kitchensink/icons/tray/svelte.png similarity index 100% rename from v2/test/kitchensink/trayicons/svelte.png rename to v2/test/kitchensink/icons/tray/svelte.png