github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/darwin/Application.m (about) 1 //go:build darwin 2 // 3 // Application.m 4 // 5 // Created by Lea Anthony on 10/10/21. 6 // 7 8 #import <Foundation/Foundation.h> 9 #import <Cocoa/Cocoa.h> 10 #import "WailsContext.h" 11 #import "Application.h" 12 #import "ApDelegate.h" 13 #import "WindowDelegate.h" 14 #import "WailsMenu.h" 15 #import "WailsMenuItem.h" 16 17 WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int devtoolsEnabled, int defaultContextMenuEnabled, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled, struct Preferences preferences, int singleInstanceLockEnabled, const char* singleInstanceUniqueId) { 18 19 [NSApplication sharedApplication]; 20 21 WailsContext *result = [WailsContext new]; 22 23 result.devtoolsEnabled = devtoolsEnabled; 24 result.defaultContextMenuEnabled = defaultContextMenuEnabled; 25 26 if ( windowStartState == WindowStartsFullscreen ) { 27 fullscreen = 1; 28 } 29 30 [result CreateWindow:width :height :frameless :resizable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent :minWidth :minHeight :maxWidth :maxHeight :fraudulentWebsiteWarningEnabled :preferences]; 31 [result SetTitle:safeInit(title)]; 32 [result Center]; 33 34 switch( windowStartState ) { 35 case WindowStartsMaximised: 36 [result.mainWindow zoom:nil]; 37 break; 38 case WindowStartsMinimised: 39 //TODO: Can you start a mac app minimised? 40 break; 41 } 42 43 if ( startsHidden == 1 ) { 44 result.startHidden = true; 45 } 46 47 if ( fullscreen == 1 ) { 48 result.startFullscreen = true; 49 } 50 51 if ( singleInstanceLockEnabled == 1 ) { 52 result.singleInstanceLockEnabled = true; 53 result.singleInstanceUniqueId = safeInit(singleInstanceUniqueId); 54 } 55 56 result.alwaysOnTop = alwaysOnTop; 57 result.hideOnClose = hideWindowOnClose; 58 59 return result; 60 } 61 62 void ExecJS(void* inctx, const char *script) { 63 WailsContext *ctx = (__bridge WailsContext*) inctx; 64 NSString *nsscript = safeInit(script); 65 ON_MAIN_THREAD( 66 [ctx ExecJS:nsscript]; 67 [nsscript release]; 68 ); 69 } 70 71 void SetTitle(void* inctx, const char *title) { 72 WailsContext *ctx = (__bridge WailsContext*) inctx; 73 NSString *_title = safeInit(title); 74 ON_MAIN_THREAD( 75 [ctx SetTitle:_title]; 76 ); 77 } 78 79 80 void SetBackgroundColour(void *inctx, int r, int g, int b, int a) { 81 WailsContext *ctx = (__bridge WailsContext*) inctx; 82 ON_MAIN_THREAD( 83 [ctx SetBackgroundColour:r :g :b :a]; 84 ); 85 } 86 87 void SetSize(void* inctx, int width, int height) { 88 WailsContext *ctx = (__bridge WailsContext*) inctx; 89 ON_MAIN_THREAD( 90 [ctx SetSize:width :height]; 91 ); 92 } 93 94 void SetAlwaysOnTop(void* inctx, int onTop) { 95 WailsContext *ctx = (__bridge WailsContext*) inctx; 96 ON_MAIN_THREAD( 97 [ctx SetAlwaysOnTop:onTop]; 98 ); 99 } 100 101 void SetMinSize(void* inctx, int width, int height) { 102 WailsContext *ctx = (__bridge WailsContext*) inctx; 103 ON_MAIN_THREAD( 104 [ctx SetMinSize:width :height]; 105 ); 106 } 107 108 void SetMaxSize(void* inctx, int width, int height) { 109 WailsContext *ctx = (__bridge WailsContext*) inctx; 110 ON_MAIN_THREAD( 111 [ctx SetMaxSize:width :height]; 112 ); 113 } 114 115 void SetPosition(void* inctx, int x, int y) { 116 WailsContext *ctx = (__bridge WailsContext*) inctx; 117 ON_MAIN_THREAD( 118 [ctx SetPosition:x :y]; 119 ); 120 } 121 122 void Center(void* inctx) { 123 WailsContext *ctx = (__bridge WailsContext*) inctx; 124 ON_MAIN_THREAD( 125 [ctx Center]; 126 ); 127 } 128 129 void Fullscreen(void* inctx) { 130 WailsContext *ctx = (__bridge WailsContext*) inctx; 131 ON_MAIN_THREAD( 132 [ctx Fullscreen]; 133 ); 134 } 135 136 void UnFullscreen(void* inctx) { 137 WailsContext *ctx = (__bridge WailsContext*) inctx; 138 ON_MAIN_THREAD( 139 [ctx UnFullscreen]; 140 ); 141 } 142 143 void Minimise(void* inctx) { 144 WailsContext *ctx = (__bridge WailsContext*) inctx; 145 ON_MAIN_THREAD( 146 [ctx Minimise]; 147 ); 148 } 149 150 void UnMinimise(void* inctx) { 151 WailsContext *ctx = (__bridge WailsContext*) inctx; 152 ON_MAIN_THREAD( 153 [ctx UnMinimise]; 154 ); 155 } 156 157 void Maximise(void* inctx) { 158 WailsContext *ctx = (__bridge WailsContext*) inctx; 159 ON_MAIN_THREAD( 160 [ctx Maximise]; 161 ); 162 } 163 164 void ToggleMaximise(void* inctx) { 165 WailsContext *ctx = (__bridge WailsContext*) inctx; 166 ON_MAIN_THREAD( 167 [ctx ToggleMaximise]; 168 ); 169 } 170 171 const char* GetSize(void *inctx) { 172 WailsContext *ctx = (__bridge WailsContext*) inctx; 173 NSRect frame = [ctx.mainWindow frame]; 174 NSString *result = [NSString stringWithFormat:@"%d,%d", (int)frame.size.width, (int)frame.size.height]; 175 return [result UTF8String]; 176 } 177 178 const char* GetPosition(void *inctx) { 179 WailsContext *ctx = (__bridge WailsContext*) inctx; 180 NSScreen* screen = [ctx getCurrentScreen]; 181 NSRect windowFrame = [ctx.mainWindow frame]; 182 NSRect screenFrame = [screen visibleFrame]; 183 int x = windowFrame.origin.x - screenFrame.origin.x; 184 int y = windowFrame.origin.y - screenFrame.origin.y; 185 y = screenFrame.size.height - y - windowFrame.size.height; 186 NSString *result = [NSString stringWithFormat:@"%d,%d",x,y]; 187 return [result UTF8String]; 188 } 189 190 const bool IsFullScreen(void *inctx) { 191 WailsContext *ctx = (__bridge WailsContext*) inctx; 192 return [ctx IsFullScreen]; 193 } 194 195 const bool IsMinimised(void *inctx) { 196 WailsContext *ctx = (__bridge WailsContext*) inctx; 197 return [ctx IsMinimised]; 198 } 199 200 const bool IsMaximised(void *inctx) { 201 WailsContext *ctx = (__bridge WailsContext*) inctx; 202 return [ctx IsMaximised]; 203 } 204 205 void UnMaximise(void* inctx) { 206 WailsContext *ctx = (__bridge WailsContext*) inctx; 207 ON_MAIN_THREAD( 208 [ctx UnMaximise]; 209 ); 210 } 211 212 void Quit(void *inctx) { 213 WailsContext *ctx = (__bridge WailsContext*) inctx; 214 [NSApp stop:ctx]; 215 [NSApp abortModal]; 216 } 217 218 void Hide(void *inctx) { 219 WailsContext *ctx = (__bridge WailsContext*) inctx; 220 ON_MAIN_THREAD( 221 [ctx Hide]; 222 ); 223 } 224 225 void Show(void *inctx) { 226 WailsContext *ctx = (__bridge WailsContext*) inctx; 227 ON_MAIN_THREAD( 228 [ctx Show]; 229 ); 230 } 231 232 233 void HideApplication(void *inctx) { 234 WailsContext *ctx = (__bridge WailsContext*) inctx; 235 ON_MAIN_THREAD( 236 [ctx HideApplication]; 237 ); 238 } 239 240 void ShowApplication(void *inctx) { 241 WailsContext *ctx = (__bridge WailsContext*) inctx; 242 ON_MAIN_THREAD( 243 [ctx ShowApplication]; 244 ); 245 } 246 247 NSString* safeInit(const char* input) { 248 NSString *result = nil; 249 if (input != nil) { 250 result = [NSString stringWithUTF8String:input]; 251 } 252 return result; 253 } 254 255 void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton, void* iconData, int iconDataLength) { 256 WailsContext *ctx = (__bridge WailsContext*) inctx; 257 258 NSString *_dialogType = safeInit(dialogType); 259 NSString *_title = safeInit(title); 260 NSString *_message = safeInit(message); 261 NSString *_button1 = safeInit(button1); 262 NSString *_button2 = safeInit(button2); 263 NSString *_button3 = safeInit(button3); 264 NSString *_button4 = safeInit(button4); 265 NSString *_defaultButton = safeInit(defaultButton); 266 NSString *_cancelButton = safeInit(cancelButton); 267 268 ON_MAIN_THREAD( 269 [ctx MessageDialog:_dialogType :_title :_message :_button1 :_button2 :_button3 :_button4 :_defaultButton :_cancelButton :iconData :iconDataLength]; 270 ) 271 } 272 273 void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters) { 274 275 WailsContext *ctx = (__bridge WailsContext*) inctx; 276 NSString *_title = safeInit(title); 277 NSString *_defaultFilename = safeInit(defaultFilename); 278 NSString *_defaultDirectory = safeInit(defaultDirectory); 279 NSString *_filters = safeInit(filters); 280 281 ON_MAIN_THREAD( 282 [ctx OpenFileDialog:_title :_defaultFilename :_defaultDirectory :allowDirectories :allowFiles :canCreateDirectories :treatPackagesAsDirectories :resolveAliases :showHiddenFiles :allowMultipleSelection :_filters]; 283 ) 284 } 285 286 void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters) { 287 288 WailsContext *ctx = (__bridge WailsContext*) inctx; 289 NSString *_title = safeInit(title); 290 NSString *_defaultFilename = safeInit(defaultFilename); 291 NSString *_defaultDirectory = safeInit(defaultDirectory); 292 NSString *_filters = safeInit(filters); 293 294 ON_MAIN_THREAD( 295 [ctx SaveFileDialog:_title :_defaultFilename :_defaultDirectory :canCreateDirectories :treatPackagesAsDirectories :showHiddenFiles :_filters]; 296 ) 297 } 298 299 void AppendRole(void *inctx, void *inMenu, int role) { 300 WailsContext *ctx = (__bridge WailsContext*) inctx; 301 WailsMenu *menu = (__bridge WailsMenu*) inMenu; 302 [menu appendRole :ctx :role]; 303 } 304 305 void* NewMenu(const char *name) { 306 NSString *title = @""; 307 if (name != nil) { 308 title = [NSString stringWithUTF8String:name]; 309 } 310 WailsMenu *result = [[WailsMenu new] initWithNSTitle:title]; 311 return result; 312 } 313 314 void AppendSubmenu(void* inparent, void* inchild) { 315 WailsMenu *parent = (__bridge WailsMenu*) inparent; 316 WailsMenu *child = (__bridge WailsMenu*) inchild; 317 [parent appendSubmenu:child]; 318 } 319 320 void SetAsApplicationMenu(void *inctx, void *inMenu) { 321 WailsContext *ctx = (__bridge WailsContext*) inctx; 322 WailsMenu *menu = (__bridge WailsMenu*) inMenu; 323 ctx.applicationMenu = menu; 324 } 325 326 void UpdateApplicationMenu(void *inctx) { 327 WailsContext *ctx = (__bridge WailsContext*) inctx; 328 ON_MAIN_THREAD( 329 NSApplication *app = [NSApplication sharedApplication]; 330 [app setMainMenu:ctx.applicationMenu]; 331 ) 332 } 333 334 void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen) { 335 WailsContext *ctx = (__bridge WailsContext*) inctx; 336 NSString *_title = safeInit(title); 337 NSString *_description = safeInit(description); 338 339 [ctx SetAbout :_title :_description :imagedata :datalen]; 340 } 341 342 void* AppendMenuItem(void* inctx, void* inMenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID) { 343 WailsContext *ctx = (__bridge WailsContext*) inctx; 344 WailsMenu *menu = (__bridge WailsMenu*) inMenu; 345 NSString *_label = safeInit(label); 346 NSString *_shortcutKey = safeInit(shortcutKey); 347 348 return [menu AppendMenuItem:ctx :_label :_shortcutKey :modifiers :disabled :checked :menuItemID]; 349 } 350 351 void UpdateMenuItem(void* nsmenuitem, int checked) { 352 ON_MAIN_THREAD( 353 WailsMenuItem *menuItem = (__bridge WailsMenuItem*) nsmenuitem; 354 [menuItem setState:(checked == 1?NSControlStateValueOn:NSControlStateValueOff)]; 355 ) 356 } 357 358 359 void AppendSeparator(void* inMenu) { 360 WailsMenu *menu = (__bridge WailsMenu*) inMenu; 361 [menu AppendSeparator]; 362 } 363 364 365 366 void Run(void *inctx, const char* url) { 367 WailsContext *ctx = (__bridge WailsContext*) inctx; 368 NSApplication *app = [NSApplication sharedApplication]; 369 ApDelegate* delegate = [ApDelegate new]; 370 [app setDelegate:(id)delegate]; 371 ctx.appdelegate = delegate; 372 delegate.mainWindow = ctx.mainWindow; 373 delegate.alwaysOnTop = ctx.alwaysOnTop; 374 delegate.startHidden = ctx.startHidden; 375 delegate.singleInstanceLockEnabled = ctx.singleInstanceLockEnabled; 376 delegate.singleInstanceUniqueId = ctx.singleInstanceUniqueId; 377 delegate.startFullscreen = ctx.startFullscreen; 378 379 NSString *_url = safeInit(url); 380 [ctx loadRequest:_url]; 381 [_url release]; 382 383 [app setMainMenu:ctx.applicationMenu]; 384 } 385 386 void RunMainLoop(void) { 387 NSApplication *app = [NSApplication sharedApplication]; 388 [app run]; 389 } 390 391 void ReleaseContext(void *inctx) { 392 WailsContext *ctx = (__bridge WailsContext*) inctx; 393 [ctx release]; 394 } 395 396 // Credit: https://stackoverflow.com/q/33319295 397 void WindowPrint(void *inctx) { 398 399 // Check if macOS 11.0 or newer 400 if (@available(macOS 11.0, *)) { 401 ON_MAIN_THREAD( 402 WailsContext *ctx = (__bridge WailsContext*) inctx; 403 WKWebView* webView = ctx.webview; 404 405 // I think this should be exposed as a config 406 // It directly affects the printed output/PDF 407 NSPrintInfo *pInfo = [NSPrintInfo sharedPrintInfo]; 408 pInfo.horizontalPagination = NSPrintingPaginationModeAutomatic; 409 pInfo.verticalPagination = NSPrintingPaginationModeAutomatic; 410 pInfo.verticallyCentered = YES; 411 pInfo.horizontallyCentered = YES; 412 pInfo.orientation = NSPaperOrientationLandscape; 413 pInfo.leftMargin = 0; 414 pInfo.rightMargin = 0; 415 pInfo.topMargin = 0; 416 pInfo.bottomMargin = 0; 417 418 NSPrintOperation *po = [webView printOperationWithPrintInfo:pInfo]; 419 po.showsPrintPanel = YES; 420 po.showsProgressPanel = YES; 421 422 po.view.frame = webView.bounds; 423 424 [po runOperationModalForWindow:ctx.mainWindow delegate:ctx.mainWindow.delegate didRunSelector:nil contextInfo:nil]; 425 ) 426 } 427 }