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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 KT-G@yGywn=%3+TH diff --git a/v2/test/kitchensink/appicon.png b/v2/test/kitchensink/icons/appicon.png similarity index 100% rename from v2/test/kitchensink/appicon.png rename to v2/test/kitchensink/icons/appicon.png diff --git a/v2/test/kitchensink/icons/dialog/info.png b/v2/test/kitchensink/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/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