mirror of
https://github.com/taigrr/wails.git
synced 2026-04-17 04:05:12 -07:00
Compare commits
20 Commits
v2.0.0-alp
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6424579a9e | ||
|
|
a962ae6f63 | ||
|
|
c7dee158ba | ||
|
|
237d25089d | ||
|
|
265328d648 | ||
|
|
6f40e85a6e | ||
|
|
96d8509da3 | ||
|
|
598445ab0f | ||
|
|
24788e2fd3 | ||
|
|
bbf4dde43f | ||
|
|
0afd27ab45 | ||
|
|
d498423ec2 | ||
|
|
66ce84973c | ||
|
|
55e6a0f312 | ||
|
|
81e83fdf18 | ||
|
|
f9b79d24f8 | ||
|
|
0599a47bfe | ||
|
|
817c55d318 | ||
|
|
14146c8c0c | ||
|
|
18adac20d4 |
@@ -23,14 +23,14 @@ func fatal(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func banner(_ *clir.Cli) string {
|
func banner(_ *clir.Cli) string {
|
||||||
return fmt.Sprintf("%s %s - Go/HTML Application Framework", colour.Yellow("Wails"), colour.DarkRed(version))
|
return fmt.Sprintf("%s %s", colour.Yellow("Wails CLI"), colour.DarkRed(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
app := clir.NewCli("Wails", "Go/HTML Application Framework", version)
|
app := clir.NewCli("Wails", "Go/HTML Appkit", version)
|
||||||
|
|
||||||
app.SetBannerFunction(banner)
|
app.SetBannerFunction(banner)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
var version = "v2.0.0-alpha.44"
|
var version = "v2.0.0-alpha.52"
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ struct hashmap_s dialogIconCache;
|
|||||||
// Dispatch Method
|
// Dispatch Method
|
||||||
typedef void (^dispatchMethod)(void);
|
typedef void (^dispatchMethod)(void);
|
||||||
|
|
||||||
|
TrayMenuStore *TrayMenuStoreSingleton;
|
||||||
|
|
||||||
// dispatch will execute the given `func` pointer
|
// dispatch will execute the given `func` pointer
|
||||||
void dispatch(dispatchMethod func) {
|
void dispatch(dispatchMethod func) {
|
||||||
dispatch_async(dispatch_get_main_queue(), func);
|
dispatch_async(dispatch_get_main_queue(), func);
|
||||||
@@ -55,6 +57,9 @@ void filelog(const char *message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The delegate class for tray menus
|
||||||
|
Class trayMenuDelegateClass;
|
||||||
|
|
||||||
// Utility function to visualise a hashmap
|
// Utility function to visualise a hashmap
|
||||||
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
void dumpHashmap(const char *name, struct hashmap_s *hashmap) {
|
||||||
printf("%s = { ", name);
|
printf("%s = { ", name);
|
||||||
@@ -123,13 +128,11 @@ struct Application {
|
|||||||
int hideToolbarSeparator;
|
int hideToolbarSeparator;
|
||||||
int windowBackgroundIsTranslucent;
|
int windowBackgroundIsTranslucent;
|
||||||
int hasURLHandlers;
|
int hasURLHandlers;
|
||||||
|
const char *startupURL;
|
||||||
|
|
||||||
// Menu
|
// Menu
|
||||||
Menu *applicationMenu;
|
Menu *applicationMenu;
|
||||||
|
|
||||||
// Tray
|
|
||||||
TrayMenuStore* trayMenuStore;
|
|
||||||
|
|
||||||
// Context Menus
|
// Context Menus
|
||||||
ContextMenuStore *contextMenuStore;
|
ContextMenuStore *contextMenuStore;
|
||||||
|
|
||||||
@@ -142,6 +145,9 @@ struct Application {
|
|||||||
// shutting down flag
|
// shutting down flag
|
||||||
bool shuttingDown;
|
bool shuttingDown;
|
||||||
|
|
||||||
|
// Running flag
|
||||||
|
bool running;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug works like sprintf but mutes if the global debug flag is true
|
// Debug works like sprintf but mutes if the global debug flag is true
|
||||||
@@ -302,8 +308,19 @@ void messageHandler(id self, SEL cmd, id contentController, id message) {
|
|||||||
// TODO: Check this actually does reduce flicker
|
// TODO: Check this actually does reduce flicker
|
||||||
msg(app->config, s("setValue:forKey:"), msg(c("NSNumber"), s("numberWithBool:"), 0), str("suppressesIncrementalRendering"));
|
msg(app->config, s("setValue:forKey:"), msg(c("NSNumber"), s("numberWithBool:"), 0), str("suppressesIncrementalRendering"));
|
||||||
|
|
||||||
|
// We are now running!
|
||||||
|
app->running = true;
|
||||||
|
|
||||||
|
|
||||||
// Notify backend we are ready (system startup)
|
// Notify backend we are ready (system startup)
|
||||||
app->sendMessageToBackend("SS");
|
const char *readyMessage = "SS";
|
||||||
|
if( app->startupURL == NULL ) {
|
||||||
|
app->sendMessageToBackend("SS");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readyMessage = concat("SS", app->startupURL);
|
||||||
|
app->sendMessageToBackend(readyMessage);
|
||||||
|
MEMFREE(readyMessage);
|
||||||
|
|
||||||
} else if( strcmp(name, "windowDrag") == 0 ) {
|
} else if( strcmp(name, "windowDrag") == 0 ) {
|
||||||
// Guard against null events
|
// Guard against null events
|
||||||
@@ -401,6 +418,11 @@ void ExecJS(struct Application *app, const char *js) {
|
|||||||
|
|
||||||
void willFinishLaunching(id self, SEL cmd, id sender) {
|
void willFinishLaunching(id self, SEL cmd, id sender) {
|
||||||
struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
||||||
|
// If there are URL Handlers, register a listener for them
|
||||||
|
if( app->hasURLHandlers ) {
|
||||||
|
id eventManager = msg(c("NSAppleEventManager"), s("sharedAppleEventManager"));
|
||||||
|
msg(eventManager, s("setEventHandler:andSelector:forEventClass:andEventID:"), self, s("getUrl:withReplyEvent:"), kInternetEventClass, kAEGetURL);
|
||||||
|
}
|
||||||
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
messageFromWindowCallback("Ej{\"name\":\"wails:launched\",\"data\":[]}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,12 +447,6 @@ void themeChanged(id self, SEL cmd, id sender) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void willFinishLaunching(id self) {
|
|
||||||
// struct Application *app = (struct Application *) objc_getAssociatedObject(self, "application");
|
|
||||||
// Debug(app, "willFinishLaunching called!");
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
int releaseNSObject(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"));
|
msg(e->data, s("release"));
|
||||||
return -1;
|
return -1;
|
||||||
@@ -463,6 +479,10 @@ void DestroyApplication(struct Application *app) {
|
|||||||
Debug(app, "Almost a double free for app->bindings");
|
Debug(app, "Almost a double free for app->bindings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( app->startupURL != NULL ) {
|
||||||
|
MEMFREE(app->startupURL);
|
||||||
|
}
|
||||||
|
|
||||||
// Remove mouse monitors
|
// Remove mouse monitors
|
||||||
if( app->mouseDownMonitor != NULL ) {
|
if( app->mouseDownMonitor != NULL ) {
|
||||||
msg( c("NSEvent"), s("removeMonitor:"), app->mouseDownMonitor);
|
msg( c("NSEvent"), s("removeMonitor:"), app->mouseDownMonitor);
|
||||||
@@ -477,7 +497,7 @@ void DestroyApplication(struct Application *app) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the tray menu store
|
// Delete the tray menu store
|
||||||
DeleteTrayMenuStore(app->trayMenuStore);
|
DeleteTrayMenuStore(TrayMenuStoreSingleton);
|
||||||
|
|
||||||
// Delete the context menu store
|
// Delete the context menu store
|
||||||
DeleteContextMenuStore(app->contextMenuStore);
|
DeleteContextMenuStore(app->contextMenuStore);
|
||||||
@@ -1044,7 +1064,7 @@ void AddTrayMenu(struct Application *app, const char *trayMenuJSON) {
|
|||||||
// Guard against calling during shutdown
|
// Guard against calling during shutdown
|
||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
AddTrayMenuToStore(app->trayMenuStore, trayMenuJSON);
|
AddTrayMenuToStore(TrayMenuStoreSingleton, trayMenuJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
||||||
@@ -1053,13 +1073,13 @@ void SetTrayMenu(struct Application *app, const char* trayMenuJSON) {
|
|||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuInStore(app->trayMenuStore, trayMenuJSON);
|
UpdateTrayMenuInStore(TrayMenuStoreSingleton, trayMenuJSON);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeleteTrayMenuByID(struct Application *app, const char *id) {
|
void DeleteTrayMenuByID(struct Application *app, const char *id) {
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
DeleteTrayMenuInStore(app->trayMenuStore, id);
|
DeleteTrayMenuInStore(TrayMenuStoreSingleton, id);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1068,7 +1088,7 @@ void UpdateTrayMenuLabel(struct Application* app, const char* JSON) {
|
|||||||
if( app->shuttingDown ) return;
|
if( app->shuttingDown ) return;
|
||||||
|
|
||||||
ON_MAIN_THREAD(
|
ON_MAIN_THREAD(
|
||||||
UpdateTrayMenuLabelInStore(app->trayMenuStore, JSON);
|
UpdateTrayMenuLabelInStore(TrayMenuStoreSingleton, JSON);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1154,46 +1174,58 @@ void DarkModeEnabled(struct Application *app, const char *callbackID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void getURL(id self, SEL selector, id event, id replyEvent) {
|
void getURL(id self, SEL selector, id event, id replyEvent) {
|
||||||
id desc = msg(event, s("paramDescriptorForKeyword:"), keyDirectObject);
|
struct Application *app = (struct Application *)objc_getAssociatedObject(self, "application");
|
||||||
id url = msg(desc, s("stringValue"));
|
id desc = msg(event, s("paramDescriptorForKeyword:"), keyDirectObject);
|
||||||
const char* curl = cstr(url);
|
id url = msg(desc, s("stringValue"));
|
||||||
const char* message = concat("UC", curl);
|
const char* curl = cstr(url);
|
||||||
messageFromWindowCallback(message);
|
if( curl == NULL ) {
|
||||||
MEMFREE(message);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this was an incoming URL, but we aren't running yet
|
||||||
|
// save it to return when we complete
|
||||||
|
if( app->running != true ) {
|
||||||
|
app->startupURL = STRCOPY(curl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* message = concat("UC", curl);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openURLs(id self, SEL selector, id event) {
|
||||||
|
filelog("\n\nI AM HERE!!!!!\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void createDelegate(struct Application *app) {
|
void createDelegate(struct Application *app) {
|
||||||
// Define delegate
|
|
||||||
Class delegateClass = objc_allocateClassPair((Class) c("NSObject"), "AppDelegate", 0);
|
// Define delegate
|
||||||
bool resultAddProtoc = class_addProtocol(delegateClass, objc_getProtocol("NSApplicationDelegate"));
|
Class appDelegate = objc_allocateClassPair((Class) c("NSResponder"), "AppDelegate", 0);
|
||||||
class_addMethod(delegateClass, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
class_addProtocol(appDelegate, objc_getProtocol("NSTouchBarProvider"));
|
||||||
class_addMethod(delegateClass, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
|
||||||
|
class_addMethod(appDelegate, s("applicationShouldTerminateAfterLastWindowClosed:"), (IMP) no, "c@:@");
|
||||||
|
class_addMethod(appDelegate, s("applicationWillFinishLaunching:"), (IMP) willFinishLaunching, "v@:@");
|
||||||
|
|
||||||
// All Menu Items use a common callback
|
// All Menu Items use a common callback
|
||||||
class_addMethod(delegateClass, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
|
class_addMethod(appDelegate, s("menuItemCallback:"), (IMP)menuItemCallback, "v@:@");
|
||||||
|
|
||||||
// If there are URL Handlers, register the callback method
|
// If there are URL Handlers, register the callback method
|
||||||
if( app->hasURLHandlers ) {
|
if( app->hasURLHandlers ) {
|
||||||
class_addMethod(delegateClass, s("getUrl:withReplyEvent:"), (IMP) getURL, "i@:@@");
|
class_addMethod(appDelegate, s("getUrl:withReplyEvent:"), (IMP) getURL, "i@:@@");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Script handler
|
// Script handler
|
||||||
class_addMethod(delegateClass, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
class_addMethod(appDelegate, s("userContentController:didReceiveScriptMessage:"), (IMP) messageHandler, "v@:@@");
|
||||||
objc_registerClassPair(delegateClass);
|
objc_registerClassPair(appDelegate);
|
||||||
|
|
||||||
// Create delegate
|
// Create delegate
|
||||||
id delegate = msg((id)delegateClass, s("new"));
|
id delegate = msg((id)appDelegate, s("new"));
|
||||||
objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN);
|
objc_setAssociatedObject(delegate, "application", (id)app, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
// If there are URL Handlers, register a listener for them
|
|
||||||
if( app->hasURLHandlers ) {
|
|
||||||
id eventManager = msg(c("NSAppleEventManager"), s("sharedAppleEventManager"));
|
|
||||||
msg(eventManager, s("setEventHandler:andSelector:forEventClass:andEventID:"), delegate, s("getUrl:withReplyEvent:"), kInternetEventClass, kAEGetURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Theme change listener
|
// Theme change listener
|
||||||
class_addMethod(delegateClass, s("themeChanged:"), (IMP) themeChanged, "v@:@@");
|
class_addMethod(appDelegate, s("themeChanged:"), (IMP) themeChanged, "v@:@@");
|
||||||
|
|
||||||
// Get defaultCenter
|
// Get defaultCenter
|
||||||
id defaultCenter = msg(c("NSDistributedNotificationCenter"), s("defaultCenter"));
|
id defaultCenter = msg(c("NSDistributedNotificationCenter"), s("defaultCenter"));
|
||||||
@@ -1661,6 +1693,35 @@ void processUserDialogIcons(struct Application *app) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TrayMenuWillOpen(id self, SEL selector, id menu) {
|
||||||
|
// Extract tray menu id from menu
|
||||||
|
id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID");
|
||||||
|
const char* trayMenuID = cstr(trayMenuIDStr);
|
||||||
|
const char *message = concat("Mo", trayMenuID);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TrayMenuDidClose(id self, SEL selector, id menu) {
|
||||||
|
// Extract tray menu id from menu
|
||||||
|
id trayMenuIDStr = objc_getAssociatedObject(menu, "trayMenuID");
|
||||||
|
const char* trayMenuID = cstr(trayMenuIDStr);
|
||||||
|
const char *message = concat("Mc", trayMenuID);
|
||||||
|
messageFromWindowCallback(message);
|
||||||
|
MEMFREE(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void createTrayMenuDelegate() {
|
||||||
|
// Define delegate
|
||||||
|
trayMenuDelegateClass = objc_allocateClassPair((Class) c("NSObject"), "MenuDelegate", 0);
|
||||||
|
class_addProtocol(trayMenuDelegateClass, objc_getProtocol("NSMenuDelegate"));
|
||||||
|
class_addMethod(trayMenuDelegateClass, s("menuWillOpen:"), (IMP) TrayMenuWillOpen, "v@:@");
|
||||||
|
class_addMethod(trayMenuDelegateClass, s("menuDidClose:"), (IMP) TrayMenuDidClose, "v@:@");
|
||||||
|
|
||||||
|
// Script handler
|
||||||
|
objc_registerClassPair(trayMenuDelegateClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Run(struct Application *app, int argc, char **argv) {
|
void Run(struct Application *app, int argc, char **argv) {
|
||||||
|
|
||||||
@@ -1673,6 +1734,9 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
// Define delegate
|
// Define delegate
|
||||||
createDelegate(app);
|
createDelegate(app);
|
||||||
|
|
||||||
|
// Define tray delegate
|
||||||
|
createTrayMenuDelegate();
|
||||||
|
|
||||||
// Create the main window
|
// Create the main window
|
||||||
createMainWindow(app);
|
createMainWindow(app);
|
||||||
|
|
||||||
@@ -1843,7 +1907,7 @@ void Run(struct Application *app, int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup initial trays
|
// Setup initial trays
|
||||||
ShowTrayMenusInStore(app->trayMenuStore);
|
ShowTrayMenusInStore(TrayMenuStoreSingleton);
|
||||||
|
|
||||||
// Process dialog icons
|
// Process dialog icons
|
||||||
processUserDialogIcons(app);
|
processUserDialogIcons(app);
|
||||||
@@ -1920,7 +1984,7 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
result->applicationMenu = NULL;
|
result->applicationMenu = NULL;
|
||||||
|
|
||||||
// Tray
|
// Tray
|
||||||
result->trayMenuStore = NewTrayMenuStore();
|
TrayMenuStoreSingleton = NewTrayMenuStore();
|
||||||
|
|
||||||
// Context Menus
|
// Context Menus
|
||||||
result->contextMenuStore = NewContextMenuStore();
|
result->contextMenuStore = NewContextMenuStore();
|
||||||
@@ -1940,6 +2004,10 @@ void* NewApplication(const char *title, int width, int height, int resizable, in
|
|||||||
|
|
||||||
result->hasURLHandlers = 0;
|
result->hasURLHandlers = 0;
|
||||||
|
|
||||||
|
result->startupURL = NULL;
|
||||||
|
|
||||||
|
result->running = false;
|
||||||
|
|
||||||
return (void*) result;
|
return (void*) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -576,38 +576,7 @@ id processCheckboxMenuItem(Menu *menu, id parentmenu, const char *title, const c
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate) {
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA) {
|
||||||
id item = ALLOC("NSMenuItem");
|
|
||||||
|
|
||||||
// Create a MenuItemCallbackData
|
|
||||||
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
|
||||||
|
|
||||||
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
|
||||||
msg(item, s("setRepresentedObject:"), wrappedId);
|
|
||||||
|
|
||||||
if( !alternate ) {
|
|
||||||
id key = processAcceleratorKey(acceleratorkey);
|
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
|
||||||
s("menuItemCallback:"), key);
|
|
||||||
} else {
|
|
||||||
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
if( tooltip != NULL ) {
|
|
||||||
msg(item, s("setToolTip:"), str(tooltip));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process image
|
|
||||||
if( image != NULL && strlen(image) > 0) {
|
|
||||||
id data = ALLOC("NSData");
|
|
||||||
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
|
||||||
id nsimage = ALLOC("NSImage");
|
|
||||||
msg(nsimage, s("initWithData:"), imageData);
|
|
||||||
if( templateImage ) {
|
|
||||||
msg(nsimage, s("template"), YES);
|
|
||||||
}
|
|
||||||
msg(item, s("setImage:"), nsimage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process Menu Item attributes
|
// Process Menu Item attributes
|
||||||
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
id dictionary = ALLOC_INIT("NSMutableDictionary");
|
||||||
@@ -658,10 +627,46 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
|
|
||||||
id attributedString = ALLOC("NSMutableAttributedString");
|
id attributedString = ALLOC("NSMutableAttributedString");
|
||||||
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
msg(attributedString, s("initWithString:attributes:"), str(title), dictionary);
|
||||||
msg(dictionary, s("release"));
|
|
||||||
|
|
||||||
msg(item, s("setAttributedTitle:"), attributedString);
|
|
||||||
msg(attributedString, s("autorelease"));
|
msg(attributedString, s("autorelease"));
|
||||||
|
msg(dictionary, s("release"));
|
||||||
|
return attributedString;
|
||||||
|
}
|
||||||
|
|
||||||
|
id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char *menuid, bool disabled, const char *acceleratorkey, const char **modifiers, const char* tooltip, const char* image, const char* fontName, int fontSize, const char* RGBA, bool templateImage, bool alternate) {
|
||||||
|
id item = ALLOC("NSMenuItem");
|
||||||
|
|
||||||
|
// Create a MenuItemCallbackData
|
||||||
|
MenuItemCallbackData *callback = CreateMenuItemCallbackData(menu, item, menuid, Text);
|
||||||
|
|
||||||
|
id wrappedId = msg(c("NSValue"), s("valueWithPointer:"), callback);
|
||||||
|
msg(item, s("setRepresentedObject:"), wrappedId);
|
||||||
|
|
||||||
|
if( !alternate ) {
|
||||||
|
id key = processAcceleratorKey(acceleratorkey);
|
||||||
|
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title),
|
||||||
|
s("menuItemCallback:"), key);
|
||||||
|
} else {
|
||||||
|
msg(item, s("initWithTitle:action:keyEquivalent:"), str(title), s("menuItemCallback:"), str(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( tooltip != NULL ) {
|
||||||
|
msg(item, s("setToolTip:"), str(tooltip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process image
|
||||||
|
if( image != NULL && strlen(image) > 0) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(image), 0);
|
||||||
|
id nsimage = ALLOC("NSImage");
|
||||||
|
msg(nsimage, s("initWithData:"), imageData);
|
||||||
|
if( templateImage ) {
|
||||||
|
msg(nsimage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
msg(item, s("setImage:"), nsimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
id attributedString = createAttributedString(title, fontName, fontSize, RGBA);
|
||||||
|
msg(item, s("setAttributedTitle:"), attributedString);
|
||||||
|
|
||||||
msg(item, s("setEnabled:"), !disabled);
|
msg(item, s("setEnabled:"), !disabled);
|
||||||
msg(item, s("autorelease"));
|
msg(item, s("autorelease"));
|
||||||
@@ -762,7 +767,7 @@ void processMenuItem(Menu *menu, id parentMenu, JsonNode *item) {
|
|||||||
bool templateImage = false;
|
bool templateImage = false;
|
||||||
getJSONBool(item, "MacTemplateImage", &templateImage);
|
getJSONBool(item, "MacTemplateImage", &templateImage);
|
||||||
|
|
||||||
int fontSize = 12;
|
int fontSize = 13;
|
||||||
getJSONInt(item, "FontSize", &fontSize);
|
getJSONInt(item, "FontSize", &fontSize);
|
||||||
|
|
||||||
// If we have an accelerator
|
// If we have an accelerator
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ id processTextMenuItem(Menu *menu, id parentMenu, const char *title, const char
|
|||||||
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
void processMenuItem(Menu *menu, id parentMenu, JsonNode *item);
|
||||||
void processMenuData(Menu *menu, JsonNode *menuData);
|
void processMenuData(Menu *menu, JsonNode *menuData);
|
||||||
|
|
||||||
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup) ;
|
void processRadioGroupJSON(Menu *menu, JsonNode *radioGroup);
|
||||||
id GetMenu(Menu *menu);
|
id GetMenu(Menu *menu);
|
||||||
|
id createAttributedString(const char* title, const char* fontName, int fontSize, const char* RGBA);
|
||||||
|
|
||||||
#endif //ASSETS_C_MENU_DARWIN_H
|
#endif //ASSETS_C_MENU_DARWIN_H
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "traymenu_darwin.h"
|
#include "traymenu_darwin.h"
|
||||||
#include "trayicons.h"
|
#include "trayicons.h"
|
||||||
|
|
||||||
|
extern Class trayMenuDelegateClass;
|
||||||
|
|
||||||
// A cache for all our tray menu icons
|
// A cache for all our tray menu icons
|
||||||
// Global because it's a singleton
|
// Global because it's a singleton
|
||||||
struct hashmap_s trayIconCache;
|
struct hashmap_s trayIconCache;
|
||||||
@@ -29,12 +31,19 @@ TrayMenu* NewTrayMenu(const char* menuJSON) {
|
|||||||
|
|
||||||
result->ID = mustJSONString(processedJSON, "ID");
|
result->ID = mustJSONString(processedJSON, "ID");
|
||||||
result->label = mustJSONString(processedJSON, "Label");
|
result->label = mustJSONString(processedJSON, "Label");
|
||||||
result->icon = mustJSONString(processedJSON, "Icon");
|
result->icon = mustJSONString(processedJSON, "Image");
|
||||||
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
result->fontName = getJSONString(processedJSON, "FontName");
|
||||||
|
result->RGBA = getJSONString(processedJSON, "RGBA");
|
||||||
|
getJSONBool(processedJSON, "MacTemplateImage", &result->templateImage);
|
||||||
|
result->fontSize = 13;
|
||||||
|
getJSONInt(processedJSON, "FontSize", &result->fontSize);
|
||||||
|
|
||||||
// Create the menu
|
// Create the menu
|
||||||
|
JsonNode* processedMenu = mustJSONObject(processedJSON, "ProcessedMenu");
|
||||||
result->menu = NewMenu(processedMenu);
|
result->menu = NewMenu(processedMenu);
|
||||||
|
|
||||||
|
result->delegate = NULL;
|
||||||
|
|
||||||
// Init tray status bar item
|
// Init tray status bar item
|
||||||
result->statusbaritem = NULL;
|
result->statusbaritem = NULL;
|
||||||
|
|
||||||
@@ -50,7 +59,7 @@ void DumpTrayMenu(TrayMenu* trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA) {
|
||||||
|
|
||||||
// Exit early if NULL
|
// Exit early if NULL
|
||||||
if( trayMenu->label == NULL ) {
|
if( trayMenu->label == NULL ) {
|
||||||
@@ -58,7 +67,8 @@ void UpdateTrayLabel(TrayMenu *trayMenu, const char *label) {
|
|||||||
}
|
}
|
||||||
// Update button label
|
// Update button label
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||||
msg(statusBarButton, s("setTitle:"), str(label));
|
id attributedString = createAttributedString(label, fontName, fontSize, RGBA);
|
||||||
|
msg(statusBarButton, s("setAttributedTitle:"), attributedString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
void UpdateTrayIcon(TrayMenu *trayMenu) {
|
||||||
@@ -78,12 +88,24 @@ void UpdateTrayIcon(TrayMenu *trayMenu) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
id trayImage = hashmap_get(&trayIconCache, trayMenu->icon, strlen(trayMenu->icon));
|
||||||
|
|
||||||
|
// If we don't have the image in the icon cache then assume it's base64 encoded image data
|
||||||
|
if (trayImage == NULL) {
|
||||||
|
id data = ALLOC("NSData");
|
||||||
|
id imageData = msg(data, s("initWithBase64EncodedString:options:"), str(trayMenu->icon), 0);
|
||||||
|
trayImage = ALLOC("NSImage");
|
||||||
|
msg(trayImage, s("initWithData:"), imageData);
|
||||||
|
|
||||||
|
if( trayMenu->templateImage ) {
|
||||||
|
msg(trayImage, s("setTemplate:"), YES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
msg(statusBarButton, s("setImagePosition:"), trayMenu->trayIconPosition);
|
||||||
msg(statusBarButton, s("setImage:"), trayImage);
|
msg(statusBarButton, s("setImage:"), trayImage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ShowTrayMenu(TrayMenu* trayMenu) {
|
void ShowTrayMenu(TrayMenu* trayMenu) {
|
||||||
|
|
||||||
// Create a status bar item if we don't have one
|
// Create a status bar item if we don't have one
|
||||||
@@ -91,7 +113,6 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
id statusBar = msg( c("NSStatusBar"), s("systemStatusBar") );
|
||||||
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
trayMenu->statusbaritem = msg(statusBar, s("statusItemWithLength:"), NSVariableStatusItemLength);
|
||||||
msg(trayMenu->statusbaritem, s("retain"));
|
msg(trayMenu->statusbaritem, s("retain"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
id statusBarButton = msg(trayMenu->statusbaritem, s("button"));
|
||||||
@@ -101,10 +122,20 @@ void ShowTrayMenu(TrayMenu* trayMenu) {
|
|||||||
UpdateTrayIcon(trayMenu);
|
UpdateTrayIcon(trayMenu);
|
||||||
|
|
||||||
// Update the label if needed
|
// Update the label if needed
|
||||||
UpdateTrayLabel(trayMenu, trayMenu->label);
|
UpdateTrayLabel(trayMenu, trayMenu->label, trayMenu->fontName, trayMenu->fontSize, trayMenu->RGBA);
|
||||||
|
|
||||||
// Update the menu
|
// Update the menu
|
||||||
id menu = GetMenu(trayMenu->menu);
|
id menu = GetMenu(trayMenu->menu);
|
||||||
|
objc_setAssociatedObject(menu, "trayMenuID", str(trayMenu->ID), OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create delegate
|
||||||
|
id trayMenuDelegate = msg((id)trayMenuDelegateClass, s("new"));
|
||||||
|
msg(menu, s("setDelegate:"), trayMenuDelegate);
|
||||||
|
objc_setAssociatedObject(trayMenuDelegate, "menu", menu, OBJC_ASSOCIATION_ASSIGN);
|
||||||
|
|
||||||
|
// Create menu delegate
|
||||||
|
trayMenu->delegate = trayMenuDelegate;
|
||||||
|
|
||||||
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
msg(trayMenu->statusbaritem, s("setMenu:"), menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +184,10 @@ void DeleteTrayMenu(TrayMenu* trayMenu) {
|
|||||||
trayMenu->statusbaritem = NULL;
|
trayMenu->statusbaritem = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( trayMenu->delegate != NULL ) {
|
||||||
|
msg(trayMenu->delegate, s("release"));
|
||||||
|
}
|
||||||
|
|
||||||
// Free the tray menu memory
|
// Free the tray menu memory
|
||||||
MEMFREE(trayMenu);
|
MEMFREE(trayMenu);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ typedef struct {
|
|||||||
const char *icon;
|
const char *icon;
|
||||||
const char *ID;
|
const char *ID;
|
||||||
|
|
||||||
|
bool templateImage;
|
||||||
|
const char *fontName;
|
||||||
|
int fontSize;
|
||||||
|
const char *RGBA;
|
||||||
|
|
||||||
Menu* menu;
|
Menu* menu;
|
||||||
|
|
||||||
id statusbaritem;
|
id statusbaritem;
|
||||||
@@ -21,6 +26,8 @@ typedef struct {
|
|||||||
|
|
||||||
JsonNode* processedJSON;
|
JsonNode* processedJSON;
|
||||||
|
|
||||||
|
id delegate;
|
||||||
|
|
||||||
} TrayMenu;
|
} TrayMenu;
|
||||||
|
|
||||||
TrayMenu* NewTrayMenu(const char *trayJSON);
|
TrayMenu* NewTrayMenu(const char *trayJSON);
|
||||||
@@ -28,7 +35,7 @@ void DumpTrayMenu(TrayMenu* trayMenu);
|
|||||||
void ShowTrayMenu(TrayMenu* trayMenu);
|
void ShowTrayMenu(TrayMenu* trayMenu);
|
||||||
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
void UpdateTrayMenuInPlace(TrayMenu* currentMenu, TrayMenu* newMenu);
|
||||||
void UpdateTrayIcon(TrayMenu *trayMenu);
|
void UpdateTrayIcon(TrayMenu *trayMenu);
|
||||||
void UpdateTrayLabel(TrayMenu *trayMenu, const char*);
|
void UpdateTrayLabel(TrayMenu *trayMenu, const char *label, const char *fontName, int fontSize, const char *RGBA);
|
||||||
|
|
||||||
void LoadTrayIcons();
|
void LoadTrayIcons();
|
||||||
void UnloadTrayIcons();
|
void UnloadTrayIcons();
|
||||||
|
|||||||
@@ -118,7 +118,13 @@ void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON) {
|
|||||||
|
|
||||||
// Check we have this menu
|
// Check we have this menu
|
||||||
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
TrayMenu *menu = MustGetTrayMenuFromStore(store, ID);
|
||||||
UpdateTrayLabel(menu, Label);
|
|
||||||
|
const char *fontName = getJSONString(parsedUpdate, "FontName");
|
||||||
|
const char *RGBA = getJSONString(parsedUpdate, "RGBA");
|
||||||
|
int fontSize = 13;
|
||||||
|
getJSONInt(parsedUpdate, "FontSize", &fontSize);
|
||||||
|
|
||||||
|
UpdateTrayLabel(menu, Label, fontName, fontSize, RGBA);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#ifndef TRAYMENUSTORE_DARWIN_H
|
#ifndef TRAYMENUSTORE_DARWIN_H
|
||||||
#define TRAYMENUSTORE_DARWIN_H
|
#define TRAYMENUSTORE_DARWIN_H
|
||||||
|
|
||||||
|
#include "traymenu_darwin.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -26,6 +28,8 @@ void UpdateTrayMenuInStore(TrayMenuStore* store, const char* menuJSON);
|
|||||||
void ShowTrayMenusInStore(TrayMenuStore* store);
|
void ShowTrayMenusInStore(TrayMenuStore* store);
|
||||||
void DeleteTrayMenuStore(TrayMenuStore* store);
|
void DeleteTrayMenuStore(TrayMenuStore* store);
|
||||||
|
|
||||||
|
TrayMenu* GetTrayMenuByID(TrayMenuStore* store, const char* menuID);
|
||||||
|
|
||||||
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
void UpdateTrayMenuLabelInStore(TrayMenuStore* store, const char* JSON);
|
||||||
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
void DeleteTrayMenuInStore(TrayMenuStore* store, const char* id);
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,17 @@ func generateTrayID() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TrayMenu struct {
|
type TrayMenu struct {
|
||||||
ID string
|
ID string
|
||||||
Label string
|
Label string
|
||||||
Icon string
|
FontSize int
|
||||||
menuItemMap *MenuItemMap
|
FontName string
|
||||||
menu *menu.Menu
|
Image string
|
||||||
ProcessedMenu *WailsMenu
|
MacTemplateImage bool
|
||||||
|
RGBA string
|
||||||
|
menuItemMap *MenuItemMap
|
||||||
|
menu *menu.Menu
|
||||||
|
ProcessedMenu *WailsMenu
|
||||||
|
trayMenu *menu.TrayMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TrayMenu) AsJSON() (string, error) {
|
func (t *TrayMenu) AsJSON() (string, error) {
|
||||||
@@ -42,10 +47,15 @@ func (t *TrayMenu) AsJSON() (string, error) {
|
|||||||
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
||||||
|
|
||||||
result := &TrayMenu{
|
result := &TrayMenu{
|
||||||
Label: trayMenu.Label,
|
Label: trayMenu.Label,
|
||||||
Icon: trayMenu.Icon,
|
FontName: trayMenu.FontName,
|
||||||
menu: trayMenu.Menu,
|
FontSize: trayMenu.FontSize,
|
||||||
menuItemMap: NewMenuItemMap(),
|
Image: trayMenu.Image,
|
||||||
|
MacTemplateImage: trayMenu.MacTemplateImage,
|
||||||
|
menu: trayMenu.Menu,
|
||||||
|
RGBA: trayMenu.RGBA,
|
||||||
|
menuItemMap: NewMenuItemMap(),
|
||||||
|
trayMenu: trayMenu,
|
||||||
}
|
}
|
||||||
|
|
||||||
result.menuItemMap.AddMenu(trayMenu.Menu)
|
result.menuItemMap.AddMenu(trayMenu.Menu)
|
||||||
@@ -54,6 +64,28 @@ func NewTrayMenu(trayMenu *menu.TrayMenu) *TrayMenu {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuOpen(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnOpen == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnTrayMenuClose(id string) {
|
||||||
|
trayMenu, ok := m.trayMenus[id]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if trayMenu.trayMenu.OnClose == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go trayMenu.trayMenu.OnClose()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
func (m *Manager) AddTrayMenu(trayMenu *menu.TrayMenu) (string, error) {
|
||||||
newTrayMenu := NewTrayMenu(trayMenu)
|
newTrayMenu := NewTrayMenu(trayMenu)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ func menuMessageParser(message string) (*parsedMessage, error) {
|
|||||||
callbackid := message[2:]
|
callbackid := message[2:]
|
||||||
topic = "menu:clicked"
|
topic = "menu:clicked"
|
||||||
data = callbackid
|
data = callbackid
|
||||||
|
case 'o':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayopen"
|
||||||
|
data = callbackid
|
||||||
|
case 'c':
|
||||||
|
callbackid := message[2:]
|
||||||
|
topic = "menu:ontrayclose"
|
||||||
|
data = callbackid
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid menu message: %s", message)
|
return nil, fmt.Errorf("invalid menu message: %s", message)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func systemMessageParser(message string) (*parsedMessage, error) {
|
|||||||
// This is our startup hook - the frontend is now ready
|
// This is our startup hook - the frontend is now ready
|
||||||
case 'S':
|
case 'S':
|
||||||
topic := "hooks:startup"
|
topic := "hooks:startup"
|
||||||
responseMessage = &parsedMessage{Topic: topic, Data: nil}
|
startupURL := message[1:]
|
||||||
|
responseMessage = &parsedMessage{Topic: topic, Data: startupURL}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
return nil, fmt.Errorf("Invalid message to systemMessageParser()")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ func (m *Menu) Start() error {
|
|||||||
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
splitTopic := strings.Split(menuMessage.Topic(), ":")
|
||||||
menuMessageType := splitTopic[1]
|
menuMessageType := splitTopic[1]
|
||||||
switch menuMessageType {
|
switch menuMessageType {
|
||||||
|
case "ontrayopen":
|
||||||
|
trayID := menuMessage.Data().(string)
|
||||||
|
m.menuManager.OnTrayMenuOpen(trayID)
|
||||||
|
case "ontrayclose":
|
||||||
|
trayID := menuMessage.Data().(string)
|
||||||
|
m.menuManager.OnTrayMenuClose(trayID)
|
||||||
case "clicked":
|
case "clicked":
|
||||||
if len(splitTopic) != 2 {
|
if len(splitTopic) != 2 {
|
||||||
m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
|
m.logger.Error("Received clicked message with invalid topic format. Expected 2 sections in topic, got %s", splitTopic)
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ type Runtime struct {
|
|||||||
|
|
||||||
// Startup Hook
|
// Startup Hook
|
||||||
startupOnce sync.Once
|
startupOnce sync.Once
|
||||||
|
|
||||||
|
// Service bus
|
||||||
|
bus *servicebus.ServiceBus
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRuntime creates a new runtime subsystem
|
// NewRuntime creates a new runtime subsystem
|
||||||
@@ -58,6 +61,7 @@ func NewRuntime(ctx context.Context, bus *servicebus.ServiceBus, logger *logger.
|
|||||||
runtime: runtime.New(bus),
|
runtime: runtime.New(bus),
|
||||||
startupCallback: startupCallback,
|
startupCallback: startupCallback,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
bus: bus,
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@@ -79,7 +83,15 @@ func (r *Runtime) Start() error {
|
|||||||
case "startup":
|
case "startup":
|
||||||
if r.startupCallback != nil {
|
if r.startupCallback != nil {
|
||||||
r.startupOnce.Do(func() {
|
r.startupOnce.Do(func() {
|
||||||
go r.startupCallback(r.runtime)
|
go func() {
|
||||||
|
r.startupCallback(r.runtime)
|
||||||
|
|
||||||
|
// If we got a url, publish it now startup completed
|
||||||
|
url, ok := hooksMessage.Data().(string)
|
||||||
|
if ok && len(url) > 0 {
|
||||||
|
r.bus.Publish("url:handler", url)
|
||||||
|
}
|
||||||
|
}()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
r.logger.Warning("no startup callback registered!")
|
r.logger.Warning("no startup callback registered!")
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ func (u *URL) Start() error {
|
|||||||
u.wg.Done()
|
u.wg.Done()
|
||||||
return
|
return
|
||||||
case urlMessage := <-u.urlChannel:
|
case urlMessage := <-u.urlChannel:
|
||||||
|
// Guard against nil messages
|
||||||
|
if urlMessage == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
messageType := strings.TrimPrefix(urlMessage.Topic(), "url:")
|
messageType := strings.TrimPrefix(urlMessage.Topic(), "url:")
|
||||||
switch messageType {
|
switch messageType {
|
||||||
case "handler":
|
case "handler":
|
||||||
|
|||||||
@@ -223,9 +223,6 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
// Set the directory
|
// Set the directory
|
||||||
cmd.Dir = b.projectData.Path
|
cmd.Dir = b.projectData.Path
|
||||||
|
|
||||||
// Set GO111MODULE environment variable
|
|
||||||
cmd.Env = append(os.Environ(), "GO111MODULE=on")
|
|
||||||
|
|
||||||
// Add CGO flags
|
// Add CGO flags
|
||||||
// We use the project/build dir as a temporary place for our generated c headers
|
// We use the project/build dir as a temporary place for our generated c headers
|
||||||
buildBaseDir, err := fs.RelativeToCwd("build")
|
buildBaseDir, err := fs.RelativeToCwd("build")
|
||||||
@@ -233,7 +230,16 @@ func (b *BaseBuilder) CompileProject(options *Options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Env = append(os.Environ(), fmt.Sprintf("CGO_CFLAGS=-I%s", buildBaseDir))
|
cmd.Env = os.Environ() // inherit env
|
||||||
|
|
||||||
|
// Use upsertEnv so we don't overwrite user's CGO_CFLAGS
|
||||||
|
cmd.Env = upsertEnv(cmd.Env, "CGO_CFLAGS", func(v string) string {
|
||||||
|
if v != "" {
|
||||||
|
v += " "
|
||||||
|
}
|
||||||
|
v += "-I" + buildBaseDir
|
||||||
|
return v
|
||||||
|
})
|
||||||
|
|
||||||
// Setup buffers
|
// Setup buffers
|
||||||
var stdo, stde bytes.Buffer
|
var stdo, stde bytes.Buffer
|
||||||
@@ -391,3 +397,22 @@ func (b *BaseBuilder) ExtractAssets() (*html.AssetBundle, error) {
|
|||||||
// Read in html
|
// Read in html
|
||||||
return html.NewAssetBundle(b.projectData.HTML)
|
return html.NewAssetBundle(b.projectData.HTML)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func upsertEnv(env []string, key string, update func(v string) string) []string {
|
||||||
|
newEnv := make([]string, len(env), len(env)+1)
|
||||||
|
found := false
|
||||||
|
for i := range env {
|
||||||
|
if strings.HasPrefix(env[i], key+"=") {
|
||||||
|
eqIndex := strings.Index(env[i], "=")
|
||||||
|
val := env[i][eqIndex+1:]
|
||||||
|
newEnv[i] = fmt.Sprintf("%s=%v", key, update(val))
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newEnv[i] = env[i]
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
newEnv = append(newEnv, fmt.Sprintf("%s=%v", key, update("")))
|
||||||
|
}
|
||||||
|
return newEnv
|
||||||
|
}
|
||||||
|
|||||||
31
v2/pkg/commands/build/base_test.go
Normal file
31
v2/pkg/commands/build/base_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package build
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestUpdateEnv(t *testing.T) {
|
||||||
|
|
||||||
|
env := []string{"one=1", "two=a=b", "three="}
|
||||||
|
newEnv := upsertEnv(env, "two", func(v string) string {
|
||||||
|
return v + "+added"
|
||||||
|
})
|
||||||
|
newEnv = upsertEnv(newEnv, "newVar", func(v string) string {
|
||||||
|
return "added"
|
||||||
|
})
|
||||||
|
newEnv = upsertEnv(newEnv, "three", func(v string) string {
|
||||||
|
return "3"
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(newEnv) != 4 {
|
||||||
|
t.Errorf("expected: 4, got: %d", len(newEnv))
|
||||||
|
}
|
||||||
|
if newEnv[1] != "two=a=b+added" {
|
||||||
|
t.Errorf("expected: \"two=a=b+added\", got: %q", newEnv[1])
|
||||||
|
}
|
||||||
|
if newEnv[2] != "three=3" {
|
||||||
|
t.Errorf("expected: \"three=3\", got: %q", newEnv[2])
|
||||||
|
}
|
||||||
|
if newEnv[3] != "newVar=added" {
|
||||||
|
t.Errorf("expected: \"newVar=added\", got: %q", newEnv[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
66
v2/pkg/logger/filelogger.go
Normal file
66
v2/pkg/logger/filelogger.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileLogger is a utility to log messages to a number of destinations
|
||||||
|
type FileLogger struct {
|
||||||
|
filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileLogger creates a new Logger.
|
||||||
|
func NewFileLogger(filename string) Logger {
|
||||||
|
return &FileLogger{
|
||||||
|
filename: filename,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print works like Sprintf.
|
||||||
|
func (l *FileLogger) Print(message string) {
|
||||||
|
f, err := os.OpenFile(l.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := f.WriteString(message); err != nil {
|
||||||
|
f.Close()
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *FileLogger) Println(message string) {
|
||||||
|
l.Print(message + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Trace(message string) {
|
||||||
|
l.Println("TRACE | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Debug(message string) {
|
||||||
|
l.Println("DEBUG | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Info(message string) {
|
||||||
|
l.Println("INFO | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Warning(message string) {
|
||||||
|
l.Println("WARN | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Error(message string) {
|
||||||
|
l.Println("ERROR | " + message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal level logging. Works like Sprintf.
|
||||||
|
func (l *FileLogger) Fatal(message string) {
|
||||||
|
l.Println("FATAL | " + message)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
@@ -30,7 +30,8 @@ var modifierMap = map[string]Modifier{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseModifier(text string) (*Modifier, error) {
|
func parseModifier(text string) (*Modifier, error) {
|
||||||
result, valid := modifierMap[text]
|
lowertext := strings.ToLower(text)
|
||||||
|
result, valid := modifierMap[lowertext]
|
||||||
if !valid {
|
if !valid {
|
||||||
return nil, fmt.Errorf("'%s' is not a valid modifier", text)
|
return nil, fmt.Errorf("'%s' is not a valid modifier", text)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ type MenuItem struct {
|
|||||||
// Callback function when menu clicked
|
// Callback function when menu clicked
|
||||||
Click Callback `json:"-"`
|
Click Callback `json:"-"`
|
||||||
|
|
||||||
// Colour
|
// Text Colour
|
||||||
RGBA string
|
RGBA string
|
||||||
|
|
||||||
// Font
|
// Font
|
||||||
|
|||||||
@@ -6,12 +6,29 @@ type TrayMenu struct {
|
|||||||
// Label is the text we wish to display in the tray
|
// Label is the text we wish to display in the tray
|
||||||
Label string
|
Label string
|
||||||
|
|
||||||
// Icon is the name of the tray icon we wish to display.
|
// Image is the name of the tray icon we wish to display.
|
||||||
// These are read up during build from <projectdir>/trayicons and
|
// These are read up during build from <projectdir>/trayicons and
|
||||||
// the filenames are used as IDs, minus the extension
|
// the filenames are used as IDs, minus the extension
|
||||||
// EG: <projectdir>/trayicons/main.png can be referenced here with "main"
|
// EG: <projectdir>/trayicons/main.png can be referenced here with "main"
|
||||||
Icon string
|
// If the image is not a filename, it will be treated as base64 image data
|
||||||
|
Image string
|
||||||
|
|
||||||
|
// MacTemplateImage indicates that on a Mac, this image is a template image
|
||||||
|
MacTemplateImage bool
|
||||||
|
|
||||||
|
// Text Colour
|
||||||
|
RGBA string
|
||||||
|
|
||||||
|
// Font
|
||||||
|
FontSize int
|
||||||
|
FontName string
|
||||||
|
|
||||||
// Menu is the initial menu we wish to use for the tray
|
// Menu is the initial menu we wish to use for the tray
|
||||||
Menu *Menu
|
Menu *Menu
|
||||||
|
|
||||||
|
// OnOpen is called when the Menu is opened
|
||||||
|
OnOpen func()
|
||||||
|
|
||||||
|
// OnClose is called when the Menu is closed
|
||||||
|
OnClose func()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user