github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/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 MAC_OS_X_VERSION_MAX_ALLOWED >= 110300 223 if (@available(macOS 11.3, *)) { 224 if (preferences.textInteractionEnabled != NULL) { 225 config.preferences.textInteractionEnabled = *preferences.textInteractionEnabled; 226 } 227 } 228 #endif 229 230 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 120300 231 if (@available(macOS 12.3, *)) { 232 if (preferences.fullscreenEnabled != NULL) { 233 config.preferences.elementFullscreenEnabled = *preferences.fullscreenEnabled; 234 } 235 } 236 #endif 237 238 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 239 if (@available(macOS 10.15, *)) { 240 config.preferences.fraudulentWebsiteWarningEnabled = fraudulentWebsiteWarningEnabled; 241 } 242 #endif 243 244 WKUserContentController* userContentController = [WKUserContentController new]; 245 [userContentController addScriptMessageHandler:self name:@"external"]; 246 config.userContentController = userContentController; 247 self.userContentController = userContentController; 248 249 if (self.devtoolsEnabled) { 250 [config.preferences setValue:@YES forKey:@"developerExtrasEnabled"]; 251 } 252 253 if (!self.defaultContextMenuEnabled) { 254 // Disable default context menus 255 WKUserScript *initScript = [WKUserScript new]; 256 [initScript initWithSource:@"window.wails.flags.disableDefaultContextMenu = true;" 257 injectionTime:WKUserScriptInjectionTimeAtDocumentEnd 258 forMainFrameOnly:false]; 259 [userContentController addUserScript:initScript]; 260 } 261 262 self.webview = [WKWebView alloc]; 263 CGRect init = { 0,0,0,0 }; 264 [self.webview initWithFrame:init configuration:config]; 265 [contentView addSubview:self.webview]; 266 [self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable]; 267 CGRect contentViewBounds = [contentView bounds]; 268 [self.webview setFrame:contentViewBounds]; 269 270 if (webviewIsTransparent) { 271 [self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"]; 272 } 273 274 [self.webview setNavigationDelegate:self]; 275 self.webview.UIDelegate = self; 276 277 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 278 [defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"]; 279 280 // Mouse monitors 281 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { 282 id window = [event window]; 283 if (window == self.mainWindow) { 284 self.mouseEvent = event; 285 } 286 return event; 287 }]; 288 289 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) { 290 id window = [event window]; 291 if (window == self.mainWindow) { 292 self.mouseEvent = nil; 293 [self ShowMouse]; 294 } 295 return event; 296 }]; 297 298 self.applicationMenu = [NSMenu new]; 299 300 } 301 302 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags { 303 NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease]; 304 if( flags != 0 ) { 305 [result setKeyEquivalentModifierMask:flags]; 306 } 307 return result; 308 } 309 310 - (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key { 311 return [self newMenuItem :title :selector :key :0]; 312 } 313 314 - (NSMenu*) newMenu :(NSString*)title { 315 WailsMenu *result = [[WailsMenu new] initWithTitle:title]; 316 [result setAutoenablesItems:NO]; 317 return result; 318 } 319 320 - (void) Quit { 321 processMessage("Q"); 322 } 323 324 - (void) loadRequest :(NSString*)url { 325 NSURL *wkUrl = [NSURL URLWithString:url]; 326 NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl]; 327 [self.webview loadRequest:wkRequest]; 328 } 329 330 - (void) SetBackgroundColour:(int)r :(int)g :(int)b :(int)a { 331 float red = r/255.0; 332 float green = g/255.0; 333 float blue = b/255.0; 334 float alpha = a/255.0; 335 336 id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ]; 337 338 [self.mainWindow setBackgroundColor:colour]; 339 } 340 341 - (void) HideMouse { 342 [NSCursor hide]; 343 } 344 345 - (void) ShowMouse { 346 [NSCursor unhide]; 347 } 348 349 - (bool) IsFullScreen { 350 long mask = [self.mainWindow styleMask]; 351 return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen; 352 } 353 354 // Fullscreen sets the main window to be fullscreen 355 - (void) Fullscreen { 356 if( ! [self IsFullScreen] ) { 357 [self.mainWindow disableWindowConstraints]; 358 [self.mainWindow toggleFullScreen:nil]; 359 } 360 } 361 362 // UnFullscreen resets the main window after a fullscreen 363 - (void) UnFullscreen { 364 if( [self IsFullScreen] ) { 365 [self.mainWindow applyWindowConstraints]; 366 [self.mainWindow toggleFullScreen:nil]; 367 } 368 } 369 370 - (void) Minimise { 371 [self.mainWindow miniaturize:nil]; 372 } 373 374 - (void) UnMinimise { 375 [self.mainWindow deminiaturize:nil]; 376 } 377 378 - (bool) IsMinimised { 379 return [self.mainWindow isMiniaturized]; 380 } 381 382 - (void) Hide { 383 [self.mainWindow orderOut:nil]; 384 } 385 386 - (void) Show { 387 [self.mainWindow makeKeyAndOrderFront:nil]; 388 [NSApp activateIgnoringOtherApps:YES]; 389 } 390 391 - (void) HideApplication { 392 [[NSApplication sharedApplication] hide:self]; 393 } 394 395 - (void) ShowApplication { 396 [[NSApplication sharedApplication] unhide:self]; 397 [[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE]; 398 399 } 400 401 - (void) Maximise { 402 if (![self.mainWindow isZoomed]) { 403 [self.mainWindow zoom:nil]; 404 } 405 } 406 407 - (void) ToggleMaximise { 408 [self.mainWindow zoom:nil]; 409 } 410 411 - (void) UnMaximise { 412 if ([self.mainWindow isZoomed]) { 413 [self.mainWindow zoom:nil]; 414 } 415 } 416 417 - (void) SetAlwaysOnTop:(int)onTop { 418 if (onTop) { 419 [self.mainWindow setLevel:NSFloatingWindowLevel]; 420 } else { 421 [self.mainWindow setLevel:NSNormalWindowLevel]; 422 } 423 } 424 425 - (bool) IsMaximised { 426 return [self.mainWindow isZoomed]; 427 } 428 429 - (void) ExecJS:(NSString*)script { 430 [self.webview evaluateJavaScript:script completionHandler:nil]; 431 } 432 433 - (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters 434 initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler { 435 436 NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 437 openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection; 438 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 439 if (@available(macOS 10.14, *)) { 440 openPanel.canChooseDirectories = parameters.allowsDirectories; 441 } 442 #endif 443 [openPanel 444 beginSheetModalForWindow:webView.window 445 completionHandler:^(NSInteger result) { 446 if (result == NSModalResponseOK) 447 completionHandler(openPanel.URLs); 448 else 449 completionHandler(nil); 450 }]; 451 } 452 453 - (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask { 454 // This callback is run with an autorelease pool 455 processURLRequest(self, urlSchemeTask); 456 } 457 458 - (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask { 459 NSInputStream *stream = urlSchemeTask.request.HTTPBodyStream; 460 if (stream) { 461 NSStreamStatus status = stream.streamStatus; 462 if (status != NSStreamStatusClosed && status != NSStreamStatusNotOpen) { 463 [stream close]; 464 } 465 } 466 } 467 468 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { 469 processMessage("DomReady"); 470 } 471 472 - (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message { 473 NSString *m = message.body; 474 475 // Check for drag 476 if ( [m isEqualToString:@"drag"] ) { 477 if( [self IsFullScreen] ) { 478 return; 479 } 480 if( self.mouseEvent != nil ) { 481 [self.mainWindow performWindowDragWithEvent:self.mouseEvent]; 482 } 483 return; 484 } 485 486 const char *_m = [m UTF8String]; 487 488 processMessage(_m); 489 } 490 491 492 /***** Dialogs ******/ 493 -(void) MessageDialog :(NSString*)dialogType :(NSString*)title :(NSString*)message :(NSString*)button1 :(NSString*)button2 :(NSString*)button3 :(NSString*)button4 :(NSString*)defaultButton :(NSString*)cancelButton :(void*)iconData :(int)iconDataLength { 494 495 WailsAlert *alert = [WailsAlert new]; 496 497 int style = NSAlertStyleInformational; 498 if (dialogType != nil ) { 499 if( [dialogType isEqualToString:@"warning"] ) { 500 style = NSAlertStyleWarning; 501 } 502 if( [dialogType isEqualToString:@"error"] ) { 503 style = NSAlertStyleCritical; 504 } 505 } 506 [alert setAlertStyle:style]; 507 if( title != nil ) { 508 [alert setMessageText:title]; 509 } 510 if( message != nil ) { 511 [alert setInformativeText:message]; 512 } 513 514 [alert addButton:button1 :defaultButton :cancelButton]; 515 [alert addButton:button2 :defaultButton :cancelButton]; 516 [alert addButton:button3 :defaultButton :cancelButton]; 517 [alert addButton:button4 :defaultButton :cancelButton]; 518 519 NSImage *icon = nil; 520 if (iconData != nil) { 521 NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength]; 522 icon = [[NSImage alloc] initWithData:imageData]; 523 } 524 if( icon != nil) { 525 [alert setIcon:icon]; 526 } 527 [alert.window setLevel:NSFloatingWindowLevel]; 528 529 long response = [alert runModal]; 530 int result; 531 532 if( response == NSAlertFirstButtonReturn ) { 533 result = 0; 534 } 535 else if( response == NSAlertSecondButtonReturn ) { 536 result = 1; 537 } 538 else if( response == NSAlertThirdButtonReturn ) { 539 result = 2; 540 } else { 541 result = 3; 542 } 543 processMessageDialogResponse(result); 544 } 545 546 -(void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters { 547 548 549 // Create the dialog 550 NSOpenPanel *dialog = [NSOpenPanel openPanel]; 551 552 // Valid but appears to do nothing.... :/ 553 if( title != nil ) { 554 [dialog setTitle:title]; 555 } 556 557 // Filters - semicolon delimited list of file extensions 558 if( allowFiles ) { 559 if( filters != nil && [filters length] > 0) { 560 filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""]; 561 filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""]; 562 NSArray *filterList = [filters componentsSeparatedByString:@";"]; 563 #ifdef USE_NEW_FILTERS 564 NSMutableArray *contentTypes = [[NSMutableArray new] autorelease]; 565 for (NSString *filter in filterList) { 566 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 567 if (@available(macOS 11.0, *)) { 568 UTType *t = [UTType typeWithFilenameExtension:filter]; 569 [contentTypes addObject:t]; 570 } 571 #endif 572 } 573 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 574 if (@available(macOS 11.0, *)) { 575 [dialog setAllowedContentTypes:contentTypes]; 576 } 577 #endif 578 #else 579 [dialog setAllowedFileTypes:filterList]; 580 #endif 581 } else { 582 [dialog setAllowsOtherFileTypes:true]; 583 } 584 // Default Filename 585 if( defaultFilename != nil ) { 586 [dialog setNameFieldStringValue:defaultFilename]; 587 } 588 589 [dialog setAllowsMultipleSelection: allowMultipleSelection]; 590 [dialog setShowsHiddenFiles: showHiddenFiles]; 591 592 } 593 594 // Default Directory 595 if( defaultDirectory != nil ) { 596 NSURL *url = [NSURL fileURLWithPath:defaultDirectory]; 597 [dialog setDirectoryURL:url]; 598 } 599 600 601 // Setup Options 602 [dialog setCanChooseFiles: allowFiles]; 603 [dialog setCanChooseDirectories: allowDirectories]; 604 [dialog setCanCreateDirectories: canCreateDirectories]; 605 [dialog setResolvesAliases: resolveAliases]; 606 [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; 607 608 // Setup callback handler 609 [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { 610 if ( returnCode != NSModalResponseOK) { 611 processOpenFileDialogResponse("[]"); 612 return; 613 } 614 NSMutableArray *arr = [NSMutableArray new]; 615 for (NSURL *url in [dialog URLs]) { 616 [arr addObject:[url path]]; 617 } 618 NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil]; 619 NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; 620 processOpenFileDialogResponse([nsjson UTF8String]); 621 [nsjson release]; 622 [arr release]; 623 }]; 624 625 } 626 627 628 -(void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters; { 629 630 631 // Create the dialog 632 NSSavePanel *dialog = [NSSavePanel savePanel]; 633 634 // Do not hide extension 635 [dialog setExtensionHidden:false]; 636 637 // Valid but appears to do nothing.... :/ 638 if( title != nil ) { 639 [dialog setTitle:title]; 640 } 641 642 // Filters - semicolon delimited list of file extensions 643 if( filters != nil && [filters length] > 0) { 644 filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""]; 645 filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""]; 646 NSArray *filterList = [filters componentsSeparatedByString:@";"]; 647 #ifdef USE_NEW_FILTERS 648 NSMutableArray *contentTypes = [[NSMutableArray new] autorelease]; 649 for (NSString *filter in filterList) { 650 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 651 if (@available(macOS 11.0, *)) { 652 UTType *t = [UTType typeWithFilenameExtension:filter]; 653 [contentTypes addObject:t]; 654 } 655 #endif 656 } 657 if( contentTypes.count == 0) { 658 [dialog setAllowsOtherFileTypes:true]; 659 } else { 660 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 661 if (@available(macOS 11.0, *)) { 662 [dialog setAllowedContentTypes:contentTypes]; 663 } 664 #endif 665 } 666 667 #else 668 [dialog setAllowedFileTypes:filterList]; 669 #endif 670 } else { 671 [dialog setAllowsOtherFileTypes:true]; 672 } 673 // Default Filename 674 if( defaultFilename != nil ) { 675 [dialog setNameFieldStringValue:defaultFilename]; 676 } 677 678 // Default Directory 679 if( defaultDirectory != nil ) { 680 NSURL *url = [NSURL fileURLWithPath:defaultDirectory]; 681 [dialog setDirectoryURL:url]; 682 } 683 684 // Setup Options 685 [dialog setCanSelectHiddenExtension:true]; 686 // dialog.isExtensionHidden = false; 687 [dialog setCanCreateDirectories: canCreateDirectories]; 688 [dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories]; 689 [dialog setShowsHiddenFiles: showHiddenFiles]; 690 691 // Setup callback handler 692 [dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) { 693 if ( returnCode == NSModalResponseOK ) { 694 NSURL *url = [dialog URL]; 695 if ( url != nil ) { 696 processSaveFileDialogResponse([url.path UTF8String]); 697 return; 698 } 699 } 700 processSaveFileDialogResponse(""); 701 }]; 702 703 } 704 705 - (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen { 706 self.aboutTitle = title; 707 self.aboutDescription = description; 708 709 NSData *imageData = [NSData dataWithBytes:imagedata length:datalen]; 710 self.aboutImage = [[NSImage alloc] initWithData:imageData]; 711 } 712 713 -(void) About { 714 715 WailsAlert *alert = [WailsAlert new]; 716 [alert setAlertStyle:NSAlertStyleInformational]; 717 if( self.aboutTitle != nil ) { 718 [alert setMessageText:self.aboutTitle]; 719 } 720 if( self.aboutDescription != nil ) { 721 [alert setInformativeText:self.aboutDescription]; 722 } 723 724 725 [alert.window setLevel:NSFloatingWindowLevel]; 726 if ( self.aboutImage != nil) { 727 [alert setIcon:self.aboutImage]; 728 } 729 730 [alert runModal]; 731 } 732 733 @end 734