github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/darwin/WailsContext.m (about) 1 //go:build darwin 2 // 3 // WailsContext.m 4 // test 5 // 6 // Created by Lea Anthony on 10/10/21. 7 // 8 9 #import <Foundation/Foundation.h> 10 #import <WebKit/WebKit.h> 11 #import "WailsContext.h" 12 #import "WailsAlert.h" 13 #import "WailsMenu.h" 14 #import "WindowDelegate.h" 15 #import "message.h" 16 #import "Role.h" 17 18 typedef void (^schemeTaskCaller)(id<WKURLSchemeTask>); 19 20 @implementation WailsWindow 21 22 - (BOOL)canBecomeKeyWindow 23 { 24 return YES; 25 } 26 27 - (void) applyWindowConstraints { 28 [self setMinSize:self.userMinSize]; 29 [self setMaxSize:self.userMaxSize]; 30 } 31 32 - (void) disableWindowConstraints { 33 [self setMinSize:NSMakeSize(0, 0)]; 34 [self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)]; 35 } 36 37 @end 38 39 @implementation WailsContext 40 41 - (void) SetSize:(int)width :(int)height { 42 43 if (self.shuttingDown) return; 44 45 NSRect frame = [self.mainWindow frame]; 46 frame.origin.y += frame.size.height - height; 47 frame.size.width = width; 48 frame.size.height = height; 49 [self.mainWindow setFrame:frame display:TRUE animate:FALSE]; 50 } 51 52 - (void) SetPosition:(int)x :(int)y { 53 54 if (self.shuttingDown) return; 55 56 NSScreen* screen = [self getCurrentScreen]; 57 NSRect windowFrame = [self.mainWindow frame]; 58 NSRect screenFrame = [screen frame]; 59 windowFrame.origin.x = screenFrame.origin.x + (float)x; 60 windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y; 61 62 [self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE]; 63 } 64 65 - (void) SetMinSize:(int)minWidth :(int)minHeight { 66 67 if (self.shuttingDown) return; 68 69 NSSize size = { minWidth, minHeight }; 70 self.mainWindow.userMinSize = size; 71 [self.mainWindow setMinSize:size]; 72 [self adjustWindowSize]; 73 } 74 75 76 - (void) SetMaxSize:(int)maxWidth :(int)maxHeight { 77 78 if (self.shuttingDown) return; 79 80 NSSize size = { FLT_MAX, FLT_MAX }; 81 82 size.width = maxWidth > 0 ? maxWidth : FLT_MAX; 83 size.height = maxHeight > 0 ? maxHeight : FLT_MAX; 84 85 self.mainWindow.userMaxSize = size; 86 [self.mainWindow setMaxSize:size]; 87 [self adjustWindowSize]; 88 } 89 90 91 - (void) adjustWindowSize { 92 93 if (self.shuttingDown) return; 94 95 NSRect currentFrame = [self.mainWindow frame]; 96 97 if ( currentFrame.size.width > self.mainWindow.userMaxSize.width ) currentFrame.size.width = self.mainWindow.userMaxSize.width; 98 if ( currentFrame.size.width < self.mainWindow.userMinSize.width ) currentFrame.size.width = self.mainWindow.userMinSize.width; 99 if ( currentFrame.size.height > self.mainWindow.userMaxSize.height ) currentFrame.size.height = self.mainWindow.userMaxSize.height; 100 if ( currentFrame.size.height < self.mainWindow.userMinSize.height ) currentFrame.size.height = self.mainWindow.userMinSize.height; 101 102 [self.mainWindow setFrame:currentFrame display:YES animate:FALSE]; 103 104 } 105 106 - (void) dealloc { 107 [self.appdelegate release]; 108 [self.mainWindow release]; 109 [self.mouseEvent release]; 110 [self.userContentController release]; 111 [self.applicationMenu release]; 112 [super dealloc]; 113 } 114 115 - (NSScreen*) getCurrentScreen { 116 NSScreen* screen = [self.mainWindow screen]; 117 if( screen == NULL ) { 118 screen = [NSScreen mainScreen]; 119 } 120 return screen; 121 } 122 123 - (void) SetTitle:(NSString*)title { 124 [self.mainWindow setTitle:title]; 125 } 126 127 - (void) Center { 128 [self.mainWindow center]; 129 } 130 131 - (BOOL) isFullscreen { 132 NSWindowStyleMask masks = [self.mainWindow styleMask]; 133 if ( masks & NSWindowStyleMaskFullScreen ) { 134 return YES; 135 } 136 return NO; 137 } 138 139 - (void) CreateWindow:(int)width :(int)height :(bool)frameless :(bool)resizable :(bool)fullscreen :(bool)fullSizeContent :(bool)hideTitleBar :(bool)titlebarAppearsTransparent :(bool)hideTitle :(bool)useToolbar :(bool)hideToolbarSeparator :(bool)webviewIsTransparent :(bool)hideWindowOnClose :(NSString*)appearance :(bool)windowIsTranslucent :(int)minWidth :(int)minHeight :(int)maxWidth :(int)maxHeight :(bool)fraudulentWebsiteWarningEnabled :(struct Preferences)preferences { 140 NSWindowStyleMask styleMask = 0; 141 142 if( !frameless ) { 143 if (!hideTitleBar) { 144 styleMask |= NSWindowStyleMaskTitled; 145 } 146 styleMask |= NSWindowStyleMaskClosable; 147 } 148 149 styleMask |= NSWindowStyleMaskMiniaturizable; 150 151 if( fullSizeContent || frameless || titlebarAppearsTransparent ) { 152 styleMask |= NSWindowStyleMaskFullSizeContentView; 153 } 154 155 if (resizable) { 156 styleMask |= NSWindowStyleMaskResizable; 157 } 158 159 self.mainWindow = [[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height) 160 styleMask:styleMask backing:NSBackingStoreBuffered defer:NO]; 161 162 if (!frameless && useToolbar) { 163 id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"]; 164 [toolbar autorelease]; 165 [toolbar setShowsBaselineSeparator:!hideToolbarSeparator]; 166 [self.mainWindow setToolbar:toolbar]; 167 168 } 169 170 [self.mainWindow setTitleVisibility:hideTitle]; 171 [self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent]; 172 173 // [self.mainWindow canBecomeKeyWindow]; 174 175 id contentView = [self.mainWindow contentView]; 176 if (windowIsTranslucent) { 177 NSVisualEffectView *effectView = [NSVisualEffectView alloc]; 178 NSRect bounds = [contentView bounds]; 179 [effectView initWithFrame:bounds]; 180 [effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 181 [effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow]; 182 [effectView setState:NSVisualEffectStateActive]; 183 [contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil]; 184 } 185 186 if (appearance != nil) { 187 NSAppearance *nsAppearance = [NSAppearance appearanceNamed:appearance]; 188 [self.mainWindow setAppearance:nsAppearance]; 189 } 190 191 192 NSSize minSize = { minWidth, minHeight }; 193 NSSize maxSize = { maxWidth, maxHeight }; 194 if (maxSize.width == 0) { 195 maxSize.width = FLT_MAX; 196 } 197 if (maxSize.height == 0) { 198 maxSize.height = FLT_MAX; 199 } 200 self.mainWindow.userMaxSize = maxSize; 201 self.mainWindow.userMinSize = minSize; 202 203 if( !fullscreen ) { 204 [self.mainWindow applyWindowConstraints]; 205 } 206 207 WindowDelegate *windowDelegate = [WindowDelegate new]; 208 windowDelegate.hideOnClose = hideWindowOnClose; 209 windowDelegate.ctx = self; 210 [self.mainWindow setDelegate:windowDelegate]; 211 212 // Webview stuff here! 213 WKWebViewConfiguration *config = [WKWebViewConfiguration new]; 214 config.suppressesIncrementalRendering = true; 215 config.applicationNameForUserAgent = @"wails.io"; 216 [config setURLSchemeHandler:self forURLScheme:@"wails"]; 217 218 if (preferences.tabFocusesLinks != NULL) { 219 config.preferences.tabFocusesLinks = *preferences.tabFocusesLinks; 220 } 221 222 if (@available(macOS 11.3, *)) { 223 if (preferences.textInteractionEnabled != NULL) { 224 config.preferences.textInteractionEnabled = *preferences.textInteractionEnabled; 225 } 226 } 227 228 if (@available(macOS 12.3, *)) { 229 if (preferences.fullscreenEnabled != NULL) { 230 config.preferences.elementFullscreenEnabled = *preferences.fullscreenEnabled; 231 } 232 } 233 234 // [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"]; 235 236 if (@available(macOS 10.15, *)) { 237 config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; 238 } 239 240 WKUserContentController* userContentController = [WKUserContentController new]; 241 [userContentController addScriptMessageHandler:self name:@"external"]; 242 config.userContentController = userContentController; 243 self.userContentController = userContentController; 244 245 if (self.devtoolsEnabled) { 246 [config.preferences setValue:@YES forKey:@"developerExtrasEnabled"]; 247 } 248 249 if (!self.defaultContextMenuEnabled) { 250 // Disable default context menus 251 WKUserScript *initScript = [WKUserScript new]; 252 [initScript initWithSource:@"window.wails.flags.disableDefaultContextMenu = true;" 253 injectionTime:WKUserScriptInjectionTimeAtDocumentEnd 254 forMainFrameOnly:false]; 255 [userContentController addUserScript:initScript]; 256 } 257 258 self.webview = [WKWebView alloc]; 259 CGRect init = { 0,0,0,0 }; 260 [self.webview initWithFrame:init configuration:config]; 261 [contentView addSubview:self.webview]; 262 [self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; 263 CGRect contentViewBounds = [contentView bounds]; 264 [self.webview setFrame:contentViewBounds]; 265 266 if (webviewIsTransparent) { 267 [self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"]; 268 } 269 270 [self.webview setNavigationDelegate:self]; 271 self.webview.UIDelegate = self; 272 273 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 274 [defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"]; 275 276 // Mouse monitors 277 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { 278 id window = [event window]; 279 if (window == self.mainWindow) { 280 self.mouseEvent = event; 281 } 282 return event; 283 }]; 284 285 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { 286 id window = [event window]; 287 if (window == self.mainWindow) { 288 self.mouseEvent = nil; 289 [self ShowMouse]; 290 } 291 return event; 292 }]; 293 294 self.applicationMenu = [NSMenu new]; 295 296 } 297 298 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { 299 NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease]; 300 if( flags != 0 ) { 301 [result setKeyEquivalentModifierMask:flags]; 302 } 303 return result; 304 } 305 306 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key { 307 return [self newMenuItem :title :selector :key :0]; 308 } 309 310 - (NSMenu*) newMenu :(NSString*)title { 311 WailsMenu *result = [[WailsMenu new] initWithTitle:title]; 312 [result setAutoenablesItems:NO]; 313 return result; 314 } 315 316 - (void) Quit { 317 processMessage("Q"); 318 } 319 320 - (void) loadRequest :(NSString*)url { 321 NSURL *wkUrl = [NSURL URLWithString:url]; 322 NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl]; 323 [self.webview loadRequest:wkRequest]; 324 } 325 326 - (void) SetBackgroundColour:(int)r :(int)g :(int)b :(int)a { 327 float red = r/255.0; 328 float green = g/255.0; 329 float blue = b/255.0; 330 float alpha = a/255.0; 331 332 id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ]; 333 334 [self.mainWindow setBackgroundColor:colour]; 335 } 336 337 - (void) HideMouse { 338 [NSCursor hide]; 339 } 340 341 - (void) ShowMouse { 342 [NSCursor unhide]; 343 } 344 345 - (bool) IsFullScreen { 346 long mask = [self.mainWindow styleMask]; 347 return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; 348 } 349 350 // Fullscreen sets the main window to be fullscreen 351 - (void) Fullscreen { 352 if( ! [self IsFullScreen] ) { 353 [self.mainWindow disableWindowConstraints]; 354 [self.mainWindow toggleFullScreen:nil]; 355 } 356 } 357 358 // UnFullscreen resets the main window after a fullscreen 359 - (void) UnFullscreen { 360 if( [self IsFullScreen] ) { 361 [self.mainWindow applyWindowConstraints]; 362 [self.mainWindow toggleFullScreen:nil]; 363 } 364 } 365 366 - (void) Minimise { 367 [self.mainWindow miniaturize:nil]; 368 } 369 370 - (void) UnMinimise { 371 [self.mainWindow deminiaturize:nil]; 372 } 373 374 - (bool) IsMinimised { 375 return [self.mainWindow isMiniaturized]; 376 } 377 378 - (void) Hide { 379 [self.mainWindow orderOut:nil]; 380 } 381 382 - (void) Show { 383 [self.mainWindow makeKeyAndOrderFront:nil]; 384 [NSApp activateIgnoringOtherApps:YES]; 385 } 386 387 - (void) HideApplication { 388 [[NSApplication sharedApplication] hide:self]; 389 } 390 391 - (void) ShowApplication { 392 [[NSApplication sharedApplication] unhide:self]; 393 [[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE]; 394 395 } 396 397 - (void) Maximise { 398 if (![self.mainWindow isZoomed]) { 399 [self.mainWindow zoom:nil]; 400 } 401 } 402 403 - (void) ToggleMaximise { 404 [self.mainWindow zoom:nil]; 405 } 406 407 - (void) UnMaximise { 408 if ([self.mainWindow isZoomed]) { 409 [self.mainWindow zoom:nil]; 410 } 411 } 412 413 - (void) SetAlwaysOnTop:(int)onTop { 414 if (onTop) { 415 [self.mainWindow setLevel:NSFloatingWindowLevel]; 416 } else { 417 [self.mainWindow setLevel:NSNormalWindowLevel]; 418 } 419 } 420 421 - (bool) IsMaximised { 422 return [self.mainWindow isZoomed]; 423 } 424 425 - (void) ExecJS:(NSString*)script { 426 [self.webview evaluateJavaScript:script completionHandler:nil]; 427 } 428 429 - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters 430 initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler { 431 432 NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 433 openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection; 434 if (@available(macOS 10.14, *)) { 435 openPanel.canChooseDirectories = parameters.allowsDirectories; 436 } 437 438 [openPanel 439 beginSheetModalForWindow:webView.window 440 completionHandler:^(NSInteger result) { 441 if (result == NSModalResponseOK) 442 completionHandler(openPanel.URLs); 443 else 444 completionHandler(nil); 445 }]; 446 } 447 448 - (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask { 449 // This callback is run with an autorelease pool 450 processURLRequest(self, urlSchemeTask); 451 } 452 453 - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask { 454 NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream; 455 if (stream) { 456 NSStreamStatus status = stream.streamStatus; 457 if (status != NSStreamStatusClosed && status != NSStreamStatusNotOpen) { 458 [stream close]; 459 } 460 } 461 } 462 463 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 464 processMessage("DomReady"); 465 } 466 467 - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { 468 NSString *m = message.body; 469 470 // Check for drag 471 if ( [m isEqualToString:@"drag"] ) { 472 if( [self IsFullScreen] ) { 473 return; 474 } 475 if( self.mouseEvent != nil ) { 476 [self.mainWindow performWindowDragWithEvent:self.mouseEvent]; 477 } 478 return; 479 } 480 481 const char *_m = [m UTF8String]; 482 483 processMessage(_m); 484 } 485 486 487 /***** Dialogs ******/ 488 -(void) MessageDialog :(NSString*)dialogType :(NSString*)title :(NSString*)message :(NSString*)button1 :(NSString*)button2 :(NSString*)button3 :(NSString*)button4 :(NSString*)defaultButton :(NSString*)cancelButton :(void*)iconData :(int)iconDataLength { 489 490 WailsAlert *alert = [WailsAlert new]; 491 492 int style = NSAlertStyleInformational; 493 if (dialogType != nil ) { 494 if( [dialogType isEqualToString:@"warning"] ) { 495 style = NSAlertStyleWarning; 496 } 497 if( [dialogType isEqualToString:@"error"] ) { 498 style = NSAlertStyleCritical; 499 } 500 } 501 [alert setAlertStyle:style]; 502 if( title != nil ) { 503 [alert setMessageText:title]; 504 } 505 if( message != nil ) { 506 [alert setInformativeText:message]; 507 } 508 509 [alert addButton:button1 :defaultButton :cancelButton]; 510 [alert addButton:button2 :defaultButton :cancelButton]; 511 [alert addButton:button3 :defaultButton :cancelButton]; 512 [alert addButton:button4 :defaultButton :cancelButton]; 513 514 NSImage *icon = nil; 515 if (iconData != nil) { 516 NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength]; 517 icon = [[NSImage alloc] initWithData:imageData]; 518 } 519 if( icon != nil) { 520 [alert setIcon:icon]; 521 } 522 [alert.window setLevel:NSFloatingWindowLevel]; 523 524 long response = [alert runModal]; 525 int result; 526 527 if( response == NSAlertFirstButtonReturn ) { 528 result = 0; 529 } 530 else if( response == NSAlertSecondButtonReturn ) { 531 result = 1; 532 } 533 else if( response == NSAlertThirdButtonReturn ) { 534 result = 2; 535 } else { 536 result = 3; 537 } 538 processMessageDialogResponse(result); 539 } 540 541 -(void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters { 542 543 544 // Create the dialog 545 NSOpenPanel *dialog = [NSOpenPanel openPanel]; 546 547 // Valid but appears to do nothing.... :/ 548 if( title != nil ) { 549 [dialog setTitle:title]; 550 } 551 552 // Filters - semicolon delimited list of file extensions 553 if( allowFiles ) { 554 if( filters != nil && [filters length] > 0) { 555 filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""]; 556 filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""]; 557 NSArray *filterList = [filters componentsSeparatedByString:@";"]; 558 #ifdef USE_NEW_FILTERS 559 NSMutableArray *contentTypes = [[NSMutableArray new] autorelease]; 560 for (NSString *filter in filterList) { 561 if (@available(macOS 11.0, *)) { 562 UTType *t = [UTType typeWithFilenameExtension:filter]; 563 [contentTypes addObject:t]; 564 } 565 } 566 if (@available(macOS 11.0, *)) { 567 [dialog setAllowedContentTypes:contentTypes]; 568 } 569 #else 570 [dialog setAllowedFileTypes:filterList]; 571 #endif 572 } else { 573 [dialog setAllowsOtherFileTypes:true]; 574 } 575 // Default Filename 576 if( defaultFilename != nil ) { 577 [dialog setNameFieldStringValue:defaultFilename]; 578 } 579 580 [dialog setAllowsMultipleSelection: allowMultipleSelection]; 581 [dialog setShowsHiddenFiles: showHiddenFiles]; 582 583 } 584 585 // Default Directory 586 if( defaultDirectory != nil ) { 587 NSURL *url = [NSURL fileURLWithPath:defaultDirectory]; 588 [dialog setDirectoryURL:url]; 589 } 590 591 592 // Setup Options 593 [dialog setCanChooseFiles: allowFiles]; 594 [dialog setCanChooseDirectories: allowDirectories]; 595 [dialog setCanCreateDirectories: canCreateDirectories]; 596 [dialog setResolvesAliases: resolveAliases]; 597 [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; 598 599 // Setup callback handler 600 [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { 601 if ( returnCode != NSModalResponseOK) { 602 processOpenFileDialogResponse("[]"); 603 return; 604 } 605 NSMutableArray *arr = [NSMutableArray new]; 606 for (NSURL *url in [dialog URLs]) { 607 [arr addObject:[url path]]; 608 } 609 NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil]; 610 NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 611 processOpenFileDialogResponse([nsjson UTF8String]); 612 [nsjson release]; 613 [arr release]; 614 }]; 615 616 } 617 618 619 -(void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters; { 620 621 622 // Create the dialog 623 NSSavePanel *dialog = [NSSavePanel savePanel]; 624 625 // Do not hide extension 626 [dialog setExtensionHidden:false]; 627 628 // Valid but appears to do nothing.... :/ 629 if( title != nil ) { 630 [dialog setTitle:title]; 631 } 632 633 // Filters - semicolon delimited list of file extensions 634 if( filters != nil && [filters length] > 0) { 635 filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""]; 636 filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""]; 637 NSArray *filterList = [filters componentsSeparatedByString:@";"]; 638 #ifdef USE_NEW_FILTERS 639 NSMutableArray *contentTypes = [[NSMutableArray new] autorelease]; 640 for (NSString *filter in filterList) { 641 if (@available(macOS 11.0, *)) { 642 UTType *t = [UTType typeWithFilenameExtension:filter]; 643 [contentTypes addObject:t]; 644 } 645 } 646 if( contentTypes.count == 0) { 647 [dialog setAllowsOtherFileTypes:true]; 648 } else { 649 if (@available(macOS 11.0, *)) { 650 [dialog setAllowedContentTypes:contentTypes]; 651 } 652 } 653 654 #else 655 [dialog setAllowedFileTypes:filterList]; 656 #endif 657 } else { 658 [dialog setAllowsOtherFileTypes:true]; 659 } 660 // Default Filename 661 if( defaultFilename != nil ) { 662 [dialog setNameFieldStringValue:defaultFilename]; 663 } 664 665 // Default Directory 666 if( defaultDirectory != nil ) { 667 NSURL *url = [NSURL fileURLWithPath:defaultDirectory]; 668 [dialog setDirectoryURL:url]; 669 } 670 671 // Setup Options 672 [dialog setCanSelectHiddenExtension:true]; 673 // dialog.isExtensionHidden = false; 674 [dialog setCanCreateDirectories: canCreateDirectories]; 675 [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; 676 [dialog setShowsHiddenFiles: showHiddenFiles]; 677 678 // Setup callback handler 679 [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { 680 if ( returnCode == NSModalResponseOK ) { 681 NSURL *url = [dialog URL]; 682 if ( url != nil ) { 683 processSaveFileDialogResponse([url.path UTF8String]); 684 return; 685 } 686 } 687 processSaveFileDialogResponse(""); 688 }]; 689 690 } 691 692 - (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen { 693 self.aboutTitle = title; 694 self.aboutDescription = description; 695 696 NSData *imageData = [NSData dataWithBytes:imagedata length:datalen]; 697 self.aboutImage = [[NSImage alloc] initWithData:imageData]; 698 } 699 700 -(void) About { 701 702 WailsAlert *alert = [WailsAlert new]; 703 [alert setAlertStyle:NSAlertStyleInformational]; 704 if( self.aboutTitle != nil ) { 705 [alert setMessageText:self.aboutTitle]; 706 } 707 if( self.aboutDescription != nil ) { 708 [alert setInformativeText:self.aboutDescription]; 709 } 710 711 712 [alert.window setLevel:NSFloatingWindowLevel]; 713 if ( self.aboutImage != nil) { 714 [alert setIcon:self.aboutImage]; 715 } 716 717 [alert runModal]; 718 } 719 720 @end 721