Support custom dialog icons. Move icons to new dir.
@@ -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);
|
||||
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -21,13 +21,13 @@ func (d *DesktopBuilder) convertToHexLiteral(bytes []byte) string {
|
||||
return result
|
||||
}
|
||||
|
||||
// desktop_linux.go will compile the tray icon found at <projectdir>/trayicon.png into the application
|
||||
// We will compile all tray icons found at <projectdir>/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 <projectdir>/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
|
||||
}
|
||||
|
||||
BIN
v2/pkg/commands/build/internal/packager/icons/dialog/info.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
@@ -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) {
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
BIN
v2/test/kitchensink/icons/dialog/info.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |