Retina & theme aware dialog icons. Default icons + override ability
41
v2/internal/ffenestri/defaultdialogicons.c
Normal file
@@ -31,6 +31,7 @@ extern void ToggleFullscreen(void *app);
|
||||
extern void DisableFrame(void *app);
|
||||
extern void OpenDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int allowFiles, int allowDirs, int allowMultiple, int showHiddenFiles, int canCreateDirectories, int resolvesAliases, int treatPackagesAsDirectories);
|
||||
extern void SaveDialog(void *appPointer, char *callbackID, char *title, char *filters, char *defaultFilename, char *defaultDir, int showHiddenFiles, int canCreateDirectories, int treatPackagesAsDirectories);
|
||||
extern void MessageDialog(void *appPointer, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton);
|
||||
extern void DarkModeEnabled(void *appPointer, char *callbackID);
|
||||
extern void UpdateMenu(void *app, char *menuAsJSON);
|
||||
extern void UpdateTray(void *app, char *menuAsJSON);
|
||||
|
||||
@@ -153,6 +153,39 @@ func (c *Client) SaveDialog(dialogOptions *options.SaveDialog, callbackID string
|
||||
)
|
||||
}
|
||||
|
||||
// MessageDialog will open a message dialog with the given options
|
||||
func (c *Client) MessageDialog(dialogOptions *options.MessageDialog, callbackID string) {
|
||||
|
||||
// Sanity check button length
|
||||
if len(dialogOptions.Buttons) > 4 {
|
||||
c.app.logger.Error("Given %d message dialog buttons. Maximum is 4", len(dialogOptions.Buttons))
|
||||
return
|
||||
}
|
||||
|
||||
// Reverse
|
||||
|
||||
// Process buttons
|
||||
buttons := []string{"", "", "", ""}
|
||||
count := 0
|
||||
for i := len(dialogOptions.Buttons) - 1; i >= 0; i-- {
|
||||
buttons[count] = dialogOptions.Buttons[i]
|
||||
count++
|
||||
}
|
||||
|
||||
C.MessageDialog(c.app.app,
|
||||
c.app.string2CString(callbackID),
|
||||
c.app.string2CString(string(dialogOptions.Type)),
|
||||
c.app.string2CString(dialogOptions.Title),
|
||||
c.app.string2CString(dialogOptions.Message),
|
||||
c.app.string2CString(dialogOptions.Icon),
|
||||
c.app.string2CString(buttons[0]),
|
||||
c.app.string2CString(buttons[1]),
|
||||
c.app.string2CString(buttons[2]),
|
||||
c.app.string2CString(buttons[3]),
|
||||
c.app.string2CString(dialogOptions.DefaultButton),
|
||||
c.app.string2CString(dialogOptions.CancelButton))
|
||||
}
|
||||
|
||||
func (c *Client) DarkModeEnabled(callbackID string) {
|
||||
C.DarkModeEnabled(c.app.app, c.app.string2CString(callbackID))
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@
|
||||
#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"))
|
||||
#define GET_BACKINGSCALEFACTOR(receiver) ((CGFloat(*)(id, SEL))msg)(receiver, s("backingScaleFactor"))
|
||||
|
||||
#define STREQ(a,b) strcmp(a, b) == 0
|
||||
#define STRCOPY(a) concat(a, "")
|
||||
#define STR_HAS_CHARS(input) input != NULL && strlen(input) > 0
|
||||
#define MEMFREE(input) free((void*)input); input = NULL;
|
||||
|
||||
#define ON_MAIN_THREAD(str) dispatch( ^{ str; } )
|
||||
@@ -103,7 +105,8 @@ extern const unsigned char runtime;
|
||||
extern const unsigned char *trayIcons[];
|
||||
|
||||
// Dialog icons
|
||||
extern const unsigned char *dialogIcons[];
|
||||
extern const unsigned char *defaultDialogIcons[];
|
||||
extern const unsigned char *userDialogIcons[];
|
||||
|
||||
// MAIN DEBUG FLAG
|
||||
int debug;
|
||||
@@ -287,6 +290,14 @@ void Fatal(struct Application *app, const char *message, ... ) {
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
bool isRetina(struct Application *app) {
|
||||
CGFloat scale = GET_BACKINGSCALEFACTOR(app->mainWindow);
|
||||
if( (int)scale == 1 ) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TitlebarAppearsTransparent(struct Application* app) {
|
||||
app->titlebarAppearsTransparent = 1;
|
||||
}
|
||||
@@ -1168,32 +1179,124 @@ void SetPosition(struct Application *app, int x, int y) {
|
||||
);
|
||||
}
|
||||
|
||||
void processDialogButton(id alert, char *buttonTitle, char *cancelButton, char *defaultButton) {
|
||||
// If this button is set
|
||||
if( STR_HAS_CHARS(buttonTitle) ) {
|
||||
id button = msg(alert, s("addButtonWithTitle:"), str(buttonTitle));
|
||||
if ( STREQ( buttonTitle, defaultButton) ) {
|
||||
msg(button, s("setKeyEquivalent:"), str("\r"));
|
||||
}
|
||||
if ( STREQ( buttonTitle, cancelButton) ) {
|
||||
msg(button, s("setKeyEquivalent:"), str("\033"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void showDialog(struct Application *app) {
|
||||
extern void MessageDialog(struct Application *app, char *callbackID, char *type, char *title, char *message, char *icon, char *button1, char *button2, char *button3, char *button4, char *defaultButton, char *cancelButton) {
|
||||
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"));
|
||||
char *dialogType = type;
|
||||
char *dialogIcon = type;
|
||||
|
||||
// Default to info type
|
||||
if( dialogType == NULL ) {
|
||||
dialogType = "info";
|
||||
}
|
||||
|
||||
// Set the dialog style
|
||||
if( STREQ(dialogType, "info") || STREQ(dialogType, "question") ) {
|
||||
msg(alert, s("setAlertStyle:"), NSAlertStyleInformational);
|
||||
} else if( STREQ(dialogType, "warning") ) {
|
||||
msg(alert, s("setAlertStyle:"), NSAlertStyleWarning);
|
||||
} else if( STREQ(dialogType, "error") ) {
|
||||
msg(alert, s("setAlertStyle:"), NSAlertStyleCritical);
|
||||
}
|
||||
|
||||
// Set title if given
|
||||
if( strlen(title) > 0 ) {
|
||||
msg(alert, s("setMessageText:"), str(title));
|
||||
}
|
||||
|
||||
// Set message if given
|
||||
if( strlen(message) > 0) {
|
||||
msg(alert, s("setInformativeText:"), str(message));
|
||||
}
|
||||
|
||||
// Process buttons
|
||||
processDialogButton(alert, button1, cancelButton, defaultButton);
|
||||
processDialogButton(alert, button2, cancelButton, defaultButton);
|
||||
processDialogButton(alert, button3, cancelButton, defaultButton);
|
||||
processDialogButton(alert, button4, cancelButton, defaultButton);
|
||||
|
||||
// Check for custom dialog icon
|
||||
if( strlen(icon) > 0 ) {
|
||||
dialogIcon = icon;
|
||||
}
|
||||
|
||||
// Determine what dialog icon we are looking for
|
||||
id dialogImage;
|
||||
// Look for `name-theme2x` first
|
||||
char *themeIcon = concat(dialogIcon, (isDarkMode(app) ? "-dark" : "-light") );
|
||||
if( isRetina(app) ) {
|
||||
char *dialogIcon2x = concat(themeIcon, "2x");
|
||||
dialogImage = hashmap_get(&dialogIconCache, dialogIcon2x, strlen(dialogIcon2x));
|
||||
// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon2x);
|
||||
MEMFREE(dialogIcon2x);
|
||||
|
||||
// Now look for non-themed icon `name2x`
|
||||
if ( dialogImage == NULL ) {
|
||||
dialogIcon2x = concat(dialogIcon, "2x");
|
||||
dialogImage = hashmap_get(&dialogIconCache, dialogIcon2x, strlen(dialogIcon2x));
|
||||
// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon2x);
|
||||
MEMFREE(dialogIcon2x);
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a retina icon, try the 1x name-theme icon
|
||||
if( dialogImage == NULL ) {
|
||||
dialogImage = hashmap_get(&dialogIconCache, themeIcon, strlen(themeIcon));
|
||||
// if (dialogImage != NULL ) printf("Using %s\n", themeIcon);
|
||||
}
|
||||
|
||||
// Free the theme icon memory
|
||||
MEMFREE(themeIcon);
|
||||
|
||||
// Finally try the name itself
|
||||
if( dialogImage == NULL ) {
|
||||
dialogImage = hashmap_get(&dialogIconCache, dialogIcon, strlen(dialogIcon));
|
||||
// if (dialogImage != NULL ) printf("Using %s\n", dialogIcon);
|
||||
}
|
||||
|
||||
msg(alert, s("setIcon:"), dialogImage);
|
||||
|
||||
// Run modal
|
||||
char *buttonPressed;
|
||||
int response = (int)msg(alert, s("runModal"));
|
||||
if( response == NSAlertFirstButtonReturn ) {
|
||||
printf("FIRST BUTTON PRESSED");
|
||||
buttonPressed = button1;
|
||||
}
|
||||
else if( response == NSAlertSecondButtonReturn ) {
|
||||
printf("SECOND BUTTON PRESSED");
|
||||
buttonPressed = button2;
|
||||
}
|
||||
else if( response == NSAlertThirdButtonReturn ) {
|
||||
printf("THIRD BUTTON PRESSED");
|
||||
buttonPressed = button3;
|
||||
}
|
||||
else {
|
||||
printf("FORTH BUTTON PRESSED");
|
||||
buttonPressed = button4;
|
||||
}
|
||||
|
||||
// Construct callback message. Format "DS<callbackID>|<selected button index>"
|
||||
const char *callback = concat("DM", callbackID);
|
||||
const char *header = concat(callback, "|");
|
||||
const char *responseMessage = concat(header, buttonPressed);
|
||||
|
||||
// Send message to backend
|
||||
app->sendMessageToBackend(responseMessage);
|
||||
|
||||
// Free memory
|
||||
MEMFREE(header);
|
||||
MEMFREE(callback);
|
||||
MEMFREE(responseMessage);
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2467,14 +2570,7 @@ 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;
|
||||
}
|
||||
void processDialogIcons(struct hashmap_s *hashmap, const unsigned char *dialogIcons[]) {
|
||||
|
||||
unsigned int count = 0;
|
||||
while( 1 ) {
|
||||
@@ -2496,11 +2592,25 @@ void processDialogIcons(struct Application *app) {
|
||||
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);
|
||||
hashmap_put(hashmap, (const char *)name, strlen((const char *)name), dialogImage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void processUserDialogIcons(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;
|
||||
}
|
||||
|
||||
processDialogIcons(&dialogIconCache, defaultDialogIcons);
|
||||
processDialogIcons(&dialogIconCache, userDialogIcons);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Run(struct Application *app, int argc, char **argv) {
|
||||
|
||||
@@ -2688,7 +2798,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Process dialog icons
|
||||
processDialogIcons(app);
|
||||
processUserDialogIcons(app);
|
||||
|
||||
// We set it to be invisible by default. It will become visible when everything has initialised
|
||||
msg(app->mainWindow, s("setIsVisible:"), NO);
|
||||
|
||||
@@ -17,6 +17,7 @@ type Client interface {
|
||||
CallResult(message string)
|
||||
OpenDialog(dialogOptions *options.OpenDialog, callbackID string)
|
||||
SaveDialog(dialogOptions *options.SaveDialog, callbackID string)
|
||||
MessageDialog(dialogOptions *options.MessageDialog, callbackID string)
|
||||
WindowSetTitle(title string)
|
||||
WindowShow()
|
||||
WindowHide()
|
||||
|
||||
@@ -42,6 +42,9 @@ func dialogMessageParser(message string) (*parsedMessage, error) {
|
||||
case 'S':
|
||||
topic = "dialog:saveselected:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData}
|
||||
case 'M':
|
||||
topic = "dialog:messageselected:" + callbackID
|
||||
responseMessage = &parsedMessage{Topic: topic, Data: payloadData}
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -416,7 +416,20 @@ func (d *Dispatcher) processDialogMessage(result *servicebus.Message) {
|
||||
for _, client := range d.clients {
|
||||
client.frontend.SaveDialog(dialogOptions, callbackID)
|
||||
}
|
||||
case "message":
|
||||
dialogOptions, ok := result.Data().(*options.MessageDialog)
|
||||
if !ok {
|
||||
d.logger.Error("Invalid data for 'dialog:select:message' : %#v", result.Data())
|
||||
return
|
||||
}
|
||||
// This is hardcoded in the sender too
|
||||
callbackID := splitTopic[3]
|
||||
|
||||
// TODO: Work out what we mean in a multi window environment...
|
||||
// For now we will just pick the first one
|
||||
for _, client := range d.clients {
|
||||
client.frontend.MessageDialog(dialogOptions, callbackID)
|
||||
}
|
||||
default:
|
||||
d.logger.Error("Unknown dialog type: %s", dialogType)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
type Dialog interface {
|
||||
Open(dialogOptions *options.OpenDialog) []string
|
||||
Save(dialogOptions *options.SaveDialog) string
|
||||
Message(dialogOptions *options.MessageDialog) string
|
||||
}
|
||||
|
||||
// dialog exposes the Dialog interface
|
||||
@@ -92,3 +93,28 @@ func (r *dialog) Save(dialogOptions *options.SaveDialog) string {
|
||||
|
||||
return result.Data().(string)
|
||||
}
|
||||
|
||||
// Message show a message to the user
|
||||
func (r *dialog) Message(dialogOptions *options.MessageDialog) string {
|
||||
|
||||
// Create unique dialog callback
|
||||
uniqueCallback := crypto.RandomID()
|
||||
|
||||
// Subscribe to the respose channel
|
||||
responseTopic := "dialog:messageselected:" + uniqueCallback
|
||||
dialogResponseChannel, err := r.bus.Subscribe(responseTopic)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: Cannot subscribe to bus topic: %+v\n", err.Error())
|
||||
}
|
||||
|
||||
message := "dialog:select:message:" + uniqueCallback
|
||||
r.bus.Publish(message, dialogOptions)
|
||||
|
||||
// Wait for result
|
||||
var result *servicebus.Message = <-dialogResponseChannel
|
||||
|
||||
// Delete subscription to response topic
|
||||
r.bus.UnSubscribe(responseTopic)
|
||||
|
||||
return result.Data().(string)
|
||||
}
|
||||
|
||||
@@ -127,9 +127,9 @@ func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) e
|
||||
}
|
||||
|
||||
// Setup target
|
||||
targetFilename := "dialogicons"
|
||||
targetFilename := "userdialogicons"
|
||||
targetFile := filepath.Join(assetDir, targetFilename+".c")
|
||||
//d.addFileToDelete(targetFile)
|
||||
d.addFileToDelete(targetFile)
|
||||
|
||||
var dataBytes []byte
|
||||
|
||||
@@ -137,7 +137,7 @@ func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) e
|
||||
var cdata strings.Builder
|
||||
|
||||
// Write header
|
||||
header := `// dialogicons.c
|
||||
header := `// userdialogicons.c
|
||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL.
|
||||
// This file was auto-generated. DO NOT MODIFY.
|
||||
|
||||
@@ -156,16 +156,16 @@ func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) e
|
||||
}
|
||||
|
||||
iconname := strings.TrimSuffix(filepath.Base(filename), ".png")
|
||||
dialogIconName := fmt.Sprintf("dialogIcon%dName", count)
|
||||
dialogIconName := fmt.Sprintf("userDialogIcon%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)
|
||||
dialogIconLength := fmt.Sprintf("userDialogIcon%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)
|
||||
dialogIconData := fmt.Sprintf("userDialogIcon%dData", count)
|
||||
variableList.Add(dialogIconData)
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { ", dialogIconData))
|
||||
|
||||
@@ -178,7 +178,7 @@ func (d *DesktopBuilder) processDialogIcons(assetDir string, options *Options) e
|
||||
}
|
||||
|
||||
// Write out main dialogIcons data
|
||||
cdata.WriteString("const unsigned char *dialogIcons[] = { ")
|
||||
cdata.WriteString("const unsigned char *userDialogIcons[] = { ")
|
||||
cdata.WriteString(variableList.Join(", "))
|
||||
if len(dialogIconFilenames) > 0 {
|
||||
cdata.WriteString(", ")
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Default Dialog Icons
|
||||
|
||||
This directory contains the default dialog icons. These are pre-compiled into a single C file (`defaultDialogIcons.c`) which resides in the `ffenestri` directory. If these icons are ever updated, then there is a need to run: `go run build.go` in this directory. This will generate a new `defaultDialogIcons.c` file in the `ffenestri` directory.
|
||||
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leaanthony/slicer"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func convertToHexLiteral(bytes []byte) string {
|
||||
result := ""
|
||||
for _, b := range bytes {
|
||||
result += fmt.Sprintf("0x%x, ", b)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func main() {
|
||||
dialogIconFilenames, err := filepath.Glob("*.png")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Setup target
|
||||
targetFile := "../../../../../../../internal/ffenestri/defaultdialogicons.c"
|
||||
|
||||
var dataBytes []byte
|
||||
|
||||
// Use a strings builder
|
||||
var cdata strings.Builder
|
||||
|
||||
// Write header
|
||||
header := `// defaultdialogicons.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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
iconname := strings.TrimSuffix(filepath.Base(filename), ".png")
|
||||
dialogIconName := fmt.Sprintf("defaultDialogIcon%dName", count)
|
||||
variableList.Add(dialogIconName)
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconName, convertToHexLiteral([]byte(iconname))))
|
||||
|
||||
dialogIconLength := fmt.Sprintf("defaultDialogIcon%dLength", count)
|
||||
variableList.Add(dialogIconLength)
|
||||
lengthAsString := strconv.Itoa(len(dataBytes))
|
||||
cdata.WriteString(fmt.Sprintf("const unsigned char %s[] = { %s0x00 };\n", dialogIconLength, convertToHexLiteral([]byte(lengthAsString))))
|
||||
|
||||
dialogIconData := fmt.Sprintf("defaultDialogIcon%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 *defaultDialogIcons[] = { ")
|
||||
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 {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 780 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 764 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 941 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 930 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 830 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 720 B |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -25,3 +25,23 @@ type SaveDialog struct {
|
||||
CanCreateDirectories bool
|
||||
TreatPackagesAsDirectories bool
|
||||
}
|
||||
|
||||
type DialogType string
|
||||
|
||||
const (
|
||||
InfoDialog DialogType = "info"
|
||||
WarningDialog DialogType = "warning"
|
||||
ErrorDialog DialogType = "error"
|
||||
QuestionDialog DialogType = "question"
|
||||
)
|
||||
|
||||
// MessageDialog contains the options for the Message dialogs, EG Info, Warning, etc runtime methods
|
||||
type MessageDialog struct {
|
||||
Type DialogType
|
||||
Title string
|
||||
Message string
|
||||
Buttons []string
|
||||
DefaultButton string
|
||||
CancelButton string
|
||||
Icon string
|
||||
}
|
||||
|
||||
@@ -26,3 +26,22 @@ func (l *Dialog) Open(options *options.OpenDialog) []string {
|
||||
func (l *Dialog) Save(options *options.SaveDialog) string {
|
||||
return l.runtime.Dialog.Save(options)
|
||||
}
|
||||
|
||||
// Message Dialog
|
||||
func (l *Dialog) Message(options *options.MessageDialog) string {
|
||||
return l.runtime.Dialog.Message(options)
|
||||
}
|
||||
|
||||
// Message Dialog
|
||||
func (l *Dialog) Test() string {
|
||||
return l.runtime.Dialog.Message(&options.MessageDialog{
|
||||
Type: options.InfoDialog,
|
||||
Title: " ",
|
||||
Message: "I am a longer message but these days, can't be too long!",
|
||||
// Buttons are declared in the order they should be appear in
|
||||
Buttons: []string{"test", "Cancel", "OK"},
|
||||
DefaultButton: "OK",
|
||||
CancelButton: "Cancel",
|
||||
//Icon: "wails",
|
||||
})
|
||||
}
|
||||
|
||||
BIN
v2/test/kitchensink/icons/dialog/info-light.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
v2/test/kitchensink/icons/dialog/info-light2x.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
BIN
v2/test/kitchensink/icons/dialog/wails.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
v2/test/kitchensink/icons/dialog/wails2x.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
v2/test/kitchensink/icons/tray/light2x.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |