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