mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 20:21:01 +08:00

This will show a message on the console "+[CATransaction synchronize] called within transaction" but there's seem no way to mitigate that. Still everything works as expected.
758 lines
24 KiB
Objective-C
758 lines
24 KiB
Objective-C
//go:build darwin
|
|
//
|
|
// WailsContext.m
|
|
// test
|
|
//
|
|
// Created by Lea Anthony on 10/10/21.
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <WebKit/WebKit.h>
|
|
#import "WailsContext.h"
|
|
#import "WailsAlert.h"
|
|
#import "WailsMenu.h"
|
|
#import "WindowDelegate.h"
|
|
#import "message.h"
|
|
#import "Role.h"
|
|
|
|
@implementation WailsWindow
|
|
|
|
- (BOOL)canBecomeKeyWindow
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void) applyWindowConstraints {
|
|
[self setMinSize:self.userMinSize];
|
|
[self setMaxSize:self.userMaxSize];
|
|
}
|
|
|
|
- (void) disableWindowConstraints {
|
|
[self setMinSize:NSMakeSize(0, 0)];
|
|
[self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation WailsContext
|
|
|
|
- (void) SetSize:(int)width :(int)height {
|
|
|
|
if (self.shuttingDown) return;
|
|
|
|
NSRect frame = [self.mainWindow frame];
|
|
frame.origin.y += frame.size.height - height;
|
|
frame.size.width = width;
|
|
frame.size.height = height;
|
|
[self.mainWindow setFrame:frame display:TRUE animate:FALSE];
|
|
}
|
|
|
|
- (void) SetPosition:(int)x :(int)y {
|
|
|
|
if (self.shuttingDown) return;
|
|
|
|
NSScreen* screen = [self getCurrentScreen];
|
|
NSRect windowFrame = [self.mainWindow frame];
|
|
NSRect screenFrame = [screen frame];
|
|
windowFrame.origin.x = screenFrame.origin.x + (float)x;
|
|
windowFrame.origin.y = (screenFrame.origin.y + screenFrame.size.height) - windowFrame.size.height - (float)y;
|
|
|
|
[self.mainWindow setFrame:windowFrame display:TRUE animate:FALSE];
|
|
}
|
|
|
|
- (void) SetMinSize:(int)minWidth :(int)minHeight {
|
|
|
|
if (self.shuttingDown) return;
|
|
|
|
NSSize size = { minWidth, minHeight };
|
|
self.mainWindow.userMinSize = size;
|
|
[self.mainWindow setMinSize:size];
|
|
[self adjustWindowSize];
|
|
}
|
|
|
|
|
|
- (void) SetMaxSize:(int)maxWidth :(int)maxHeight {
|
|
|
|
if (self.shuttingDown) return;
|
|
|
|
NSSize size = { FLT_MAX, FLT_MAX };
|
|
|
|
size.width = maxWidth > 0 ? maxWidth : FLT_MAX;
|
|
size.height = maxHeight > 0 ? maxHeight : FLT_MAX;
|
|
|
|
self.mainWindow.userMaxSize = size;
|
|
[self.mainWindow setMaxSize:size];
|
|
[self adjustWindowSize];
|
|
}
|
|
|
|
|
|
- (void) adjustWindowSize {
|
|
|
|
if (self.shuttingDown) return;
|
|
|
|
NSRect currentFrame = [self.mainWindow frame];
|
|
|
|
if ( currentFrame.size.width > self.mainWindow.userMaxSize.width ) currentFrame.size.width = self.mainWindow.userMaxSize.width;
|
|
if ( currentFrame.size.width < self.mainWindow.userMinSize.width ) currentFrame.size.width = self.mainWindow.userMinSize.width;
|
|
if ( currentFrame.size.height > self.mainWindow.userMaxSize.height ) currentFrame.size.height = self.mainWindow.userMaxSize.height;
|
|
if ( currentFrame.size.height < self.mainWindow.userMinSize.height ) currentFrame.size.height = self.mainWindow.userMinSize.height;
|
|
|
|
[self.mainWindow setFrame:currentFrame display:YES animate:FALSE];
|
|
|
|
}
|
|
|
|
- (void) dealloc {
|
|
[self.appdelegate release];
|
|
[self.mainWindow release];
|
|
[self.mouseEvent release];
|
|
[self.userContentController release];
|
|
[self.urlRequests release];
|
|
[self.urlRequestsLock release];
|
|
[self.applicationMenu release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (NSScreen*) getCurrentScreen {
|
|
NSScreen* screen = [self.mainWindow screen];
|
|
if( screen == NULL ) {
|
|
screen = [NSScreen mainScreen];
|
|
}
|
|
return screen;
|
|
}
|
|
|
|
- (void) SetTitle:(NSString*)title {
|
|
[self.mainWindow setTitle:title];
|
|
}
|
|
|
|
- (void) Center {
|
|
[self.mainWindow center];
|
|
}
|
|
|
|
- (BOOL) isFullscreen {
|
|
NSWindowStyleMask masks = [self.mainWindow styleMask];
|
|
if ( masks & NSWindowStyleMaskFullScreen ) {
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (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 {
|
|
self.urlRequestsId = 0;
|
|
self.urlRequestsLock = [NSLock new];
|
|
self.urlRequests = [NSMutableDictionary new];
|
|
|
|
NSWindowStyleMask styleMask = 0;
|
|
|
|
if( !frameless ) {
|
|
if (!hideTitleBar) {
|
|
styleMask |= NSWindowStyleMaskTitled;
|
|
}
|
|
styleMask |= NSWindowStyleMaskClosable;
|
|
}
|
|
|
|
styleMask |= NSWindowStyleMaskMiniaturizable;
|
|
|
|
if( fullSizeContent || frameless || titlebarAppearsTransparent ) {
|
|
styleMask |= NSWindowStyleMaskFullSizeContentView;
|
|
}
|
|
|
|
if (resizable) {
|
|
styleMask |= NSWindowStyleMaskResizable;
|
|
}
|
|
|
|
self.mainWindow = [[WailsWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height)
|
|
styleMask:styleMask backing:NSBackingStoreBuffered defer:NO];
|
|
|
|
if (!frameless && useToolbar) {
|
|
id toolbar = [[NSToolbar alloc] initWithIdentifier:@"wails.toolbar"];
|
|
[toolbar autorelease];
|
|
[toolbar setShowsBaselineSeparator:!hideToolbarSeparator];
|
|
[self.mainWindow setToolbar:toolbar];
|
|
|
|
}
|
|
|
|
[self.mainWindow setTitleVisibility:hideTitle];
|
|
[self.mainWindow setTitlebarAppearsTransparent:titlebarAppearsTransparent];
|
|
|
|
// [self.mainWindow canBecomeKeyWindow];
|
|
|
|
id contentView = [self.mainWindow contentView];
|
|
if (windowIsTranslucent) {
|
|
NSVisualEffectView *effectView = [NSVisualEffectView alloc];
|
|
NSRect bounds = [contentView bounds];
|
|
[effectView initWithFrame:bounds];
|
|
[effectView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
|
|
[effectView setBlendingMode:NSVisualEffectBlendingModeBehindWindow];
|
|
[effectView setState:NSVisualEffectStateActive];
|
|
[contentView addSubview:effectView positioned:NSWindowBelow relativeTo:nil];
|
|
}
|
|
|
|
if (appearance != nil) {
|
|
NSAppearance *nsAppearance = [NSAppearance appearanceNamed:appearance];
|
|
[self.mainWindow setAppearance:nsAppearance];
|
|
}
|
|
|
|
|
|
NSSize minSize = { minWidth, minHeight };
|
|
NSSize maxSize = { maxWidth, maxHeight };
|
|
if (maxSize.width == 0) {
|
|
maxSize.width = FLT_MAX;
|
|
}
|
|
if (maxSize.height == 0) {
|
|
maxSize.height = FLT_MAX;
|
|
}
|
|
self.mainWindow.userMaxSize = maxSize;
|
|
self.mainWindow.userMinSize = minSize;
|
|
|
|
if( !fullscreen ) {
|
|
[self.mainWindow applyWindowConstraints];
|
|
}
|
|
|
|
WindowDelegate *windowDelegate = [WindowDelegate new];
|
|
windowDelegate.hideOnClose = hideWindowOnClose;
|
|
windowDelegate.ctx = self;
|
|
[self.mainWindow setDelegate:windowDelegate];
|
|
|
|
// Webview stuff here!
|
|
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
|
config.suppressesIncrementalRendering = true;
|
|
config.applicationNameForUserAgent = @"wails.io";
|
|
[config setURLSchemeHandler:self forURLScheme:@"wails"];
|
|
|
|
// [config.preferences setValue:[NSNumber numberWithBool:true] forKey:@"developerExtrasEnabled"];
|
|
|
|
WKUserContentController* userContentController = [WKUserContentController new];
|
|
[userContentController addScriptMessageHandler:self name:@"external"];
|
|
config.userContentController = userContentController;
|
|
self.userContentController = userContentController;
|
|
if (self.debug) {
|
|
[config.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
|
|
} else {
|
|
// Disable default context menus
|
|
WKUserScript *initScript = [WKUserScript new];
|
|
[initScript initWithSource:@"window.wails.flags.disableWailsDefaultContextMenu = true;"
|
|
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
|
|
forMainFrameOnly:false];
|
|
[userContentController addUserScript:initScript];
|
|
|
|
}
|
|
|
|
self.webview = [WKWebView alloc];
|
|
CGRect init = { 0,0,0,0 };
|
|
[self.webview initWithFrame:init configuration:config];
|
|
[contentView addSubview:self.webview];
|
|
[self.webview setAutoresizingMask: NSViewWidthSizable|NSViewHeightSizable];
|
|
CGRect contentViewBounds = [contentView bounds];
|
|
[self.webview setFrame:contentViewBounds];
|
|
|
|
if (webviewIsTransparent) {
|
|
[self.webview setValue:[NSNumber numberWithBool:!webviewIsTransparent] forKey:@"drawsBackground"];
|
|
}
|
|
|
|
[self.webview setNavigationDelegate:self];
|
|
self.webview.UIDelegate = self;
|
|
|
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
[defaults setBool:FALSE forKey:@"NSAutomaticQuoteSubstitutionEnabled"];
|
|
|
|
// Mouse monitors
|
|
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
|
id window = [event window];
|
|
if (window == self.mainWindow) {
|
|
self.mouseEvent = event;
|
|
}
|
|
return event;
|
|
}];
|
|
|
|
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseUp handler:^NSEvent * _Nullable(NSEvent * _Nonnull event) {
|
|
id window = [event window];
|
|
if (window == self.mainWindow) {
|
|
self.mouseEvent = nil;
|
|
[self ShowMouse];
|
|
}
|
|
return event;
|
|
}];
|
|
|
|
self.applicationMenu = [NSMenu new];
|
|
|
|
}
|
|
|
|
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key :(NSEventModifierFlags)flags {
|
|
NSMenuItem *result = [[[NSMenuItem alloc] initWithTitle:title action:selector keyEquivalent:key] autorelease];
|
|
if( flags != 0 ) {
|
|
[result setKeyEquivalentModifierMask:flags];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
- (NSMenuItem*) newMenuItem :(NSString*)title :(SEL)selector :(NSString*)key {
|
|
return [self newMenuItem :title :selector :key :0];
|
|
}
|
|
|
|
- (NSMenu*) newMenu :(NSString*)title {
|
|
WailsMenu *result = [[WailsMenu new] initWithTitle:title];
|
|
[result setAutoenablesItems:NO];
|
|
return result;
|
|
}
|
|
|
|
- (void) Quit {
|
|
processMessage("Q");
|
|
}
|
|
|
|
- (void) loadRequest :(NSString*)url {
|
|
NSURL *wkUrl = [NSURL URLWithString:url];
|
|
NSURLRequest *wkRequest = [NSURLRequest requestWithURL:wkUrl];
|
|
[self.webview loadRequest:wkRequest];
|
|
}
|
|
|
|
- (void) SetBackgroundColour:(int)r :(int)g :(int)b :(int)a {
|
|
float red = r/255.0;
|
|
float green = g/255.0;
|
|
float blue = b/255.0;
|
|
float alpha = a/255.0;
|
|
|
|
id colour = [NSColor colorWithCalibratedRed:red green:green blue:blue alpha:alpha ];
|
|
|
|
[self.mainWindow setBackgroundColor:colour];
|
|
}
|
|
|
|
- (void) HideMouse {
|
|
[NSCursor hide];
|
|
}
|
|
|
|
- (void) ShowMouse {
|
|
[NSCursor unhide];
|
|
}
|
|
|
|
- (bool) IsFullScreen {
|
|
long mask = [self.mainWindow styleMask];
|
|
return (mask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
|
|
}
|
|
|
|
// Fullscreen sets the main window to be fullscreen
|
|
- (void) Fullscreen {
|
|
if( ! [self IsFullScreen] ) {
|
|
[self.mainWindow disableWindowConstraints];
|
|
[self.mainWindow toggleFullScreen:nil];
|
|
}
|
|
}
|
|
|
|
// UnFullscreen resets the main window after a fullscreen
|
|
- (void) UnFullscreen {
|
|
if( [self IsFullScreen] ) {
|
|
[self.mainWindow applyWindowConstraints];
|
|
[self.mainWindow toggleFullScreen:nil];
|
|
}
|
|
}
|
|
|
|
- (void) Minimise {
|
|
[self.mainWindow miniaturize:nil];
|
|
}
|
|
|
|
- (void) UnMinimise {
|
|
[self.mainWindow deminiaturize:nil];
|
|
}
|
|
|
|
- (bool) IsMinimised {
|
|
return [self.mainWindow isMiniaturized];
|
|
}
|
|
|
|
- (void) Hide {
|
|
[self.mainWindow orderOut:nil];
|
|
}
|
|
|
|
- (void) Show {
|
|
[self.mainWindow makeKeyAndOrderFront:nil];
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
}
|
|
|
|
- (void) HideApplication {
|
|
[[NSApplication sharedApplication] hide:self];
|
|
}
|
|
|
|
- (void) ShowApplication {
|
|
[[NSApplication sharedApplication] unhide:self];
|
|
[[NSApplication sharedApplication] activateIgnoringOtherApps:TRUE];
|
|
|
|
}
|
|
|
|
- (void) Maximise {
|
|
if (![self.mainWindow isZoomed]) {
|
|
[self.mainWindow zoom:nil];
|
|
}
|
|
}
|
|
|
|
- (void) ToggleMaximise {
|
|
[self.mainWindow zoom:nil];
|
|
}
|
|
|
|
- (void) UnMaximise {
|
|
if ([self.mainWindow isZoomed]) {
|
|
[self.mainWindow zoom:nil];
|
|
}
|
|
}
|
|
|
|
- (void) SetAlwaysOnTop:(int)onTop {
|
|
if (onTop) {
|
|
[self.mainWindow setLevel:NSStatusWindowLevel];
|
|
} else {
|
|
[self.mainWindow setLevel:NSNormalWindowLevel];
|
|
}
|
|
}
|
|
|
|
- (bool) IsMaximised {
|
|
return [self.mainWindow isZoomed];
|
|
}
|
|
|
|
- (void) ExecJS:(NSString*)script {
|
|
[self.webview evaluateJavaScript:script completionHandler:nil];
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
|
|
initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * URLs))completionHandler {
|
|
|
|
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
|
openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
|
|
if (@available(macOS 10.14, *)) {
|
|
openPanel.canChooseDirectories = parameters.allowsDirectories;
|
|
}
|
|
|
|
[openPanel
|
|
beginSheetModalForWindow:webView.window
|
|
completionHandler:^(NSInteger result) {
|
|
if (result == NSModalResponseOK)
|
|
completionHandler(openPanel.URLs);
|
|
else
|
|
completionHandler(nil);
|
|
}];
|
|
}
|
|
|
|
- (void) processURLResponse:(unsigned long long)requestId :(int)statusCode :(NSData *)headersJSON :(NSData *)data {
|
|
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
|
|
|
[self.urlRequestsLock lock];
|
|
id<WKURLSchemeTask> urlSchemeTask = self.urlRequests[key];
|
|
[self.urlRequestsLock unlock];
|
|
|
|
@try {
|
|
if (urlSchemeTask == nil) {
|
|
return;
|
|
}
|
|
|
|
NSDictionary *headerFields = [NSJSONSerialization JSONObjectWithData: headersJSON options: NSJSONReadingMutableContainers error: nil];
|
|
NSHTTPURLResponse *response = [[[NSHTTPURLResponse alloc] initWithURL:urlSchemeTask.request.URL statusCode:statusCode HTTPVersion:@"HTTP/1.1" headerFields:headerFields] autorelease];
|
|
|
|
@try {
|
|
[urlSchemeTask didReceiveResponse:response];
|
|
[urlSchemeTask didReceiveData:data];
|
|
[urlSchemeTask didFinish];
|
|
} @catch (NSException *exception) {
|
|
// This is very bad to detect a stopped schemeTask this should be implemented in a better way
|
|
// See todo in stopURLSchemeTask...
|
|
if (![exception.reason isEqualToString: @"This task has already been stopped"]) {
|
|
@throw exception;
|
|
}
|
|
}
|
|
} @finally {
|
|
[self.urlRequestsLock lock];
|
|
[self.urlRequests removeObjectForKey:key]; // This will release the urlSchemeTask which was retained from the dictionary
|
|
[self.urlRequestsLock unlock];
|
|
}
|
|
}
|
|
|
|
- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
|
// This callback is run with an autorelease pool
|
|
const char *url = [urlSchemeTask.request.URL.absoluteString UTF8String];
|
|
const char *method = [urlSchemeTask.request.HTTPMethod UTF8String];
|
|
const char *headerJSON = "";
|
|
const void *body = nil;
|
|
int bodyLen = 0;
|
|
|
|
NSData *headers = [NSJSONSerialization dataWithJSONObject: urlSchemeTask.request.allHTTPHeaderFields options:0 error: nil];
|
|
if (headers) {
|
|
NSString* headerString = [[[NSString alloc] initWithData:headers encoding:NSUTF8StringEncoding] autorelease];
|
|
headerJSON = [headerString UTF8String];
|
|
}
|
|
|
|
if (urlSchemeTask.request.HTTPBody) {
|
|
body = urlSchemeTask.request.HTTPBody.bytes;
|
|
bodyLen = urlSchemeTask.request.HTTPBody.length;
|
|
} else {
|
|
// TODO handle HTTPBodyStream
|
|
}
|
|
|
|
[self.urlRequestsLock lock];
|
|
self.urlRequestsId++;
|
|
unsigned long long requestId = self.urlRequestsId;
|
|
NSNumber *key = [NSNumber numberWithUnsignedLongLong:requestId];
|
|
self.urlRequests[key] = urlSchemeTask;
|
|
[self.urlRequestsLock unlock];
|
|
|
|
processURLRequest(self, requestId, url, method, headerJSON, body, bodyLen);
|
|
}
|
|
|
|
- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
|
|
// TODO implement the stopping process here in a better way...
|
|
// As soon as we introduce response body streaming we need to rewrite this nevertheless.
|
|
}
|
|
|
|
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
|
|
processMessage("DomReady");
|
|
}
|
|
|
|
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
|
|
NSString *m = message.body;
|
|
|
|
// Check for drag
|
|
if ( [m isEqualToString:@"drag"] ) {
|
|
if( [self IsFullScreen] ) {
|
|
return;
|
|
}
|
|
if( self.mouseEvent != nil ) {
|
|
[self.mainWindow performWindowDragWithEvent:self.mouseEvent];
|
|
}
|
|
return;
|
|
}
|
|
|
|
const char *_m = [m UTF8String];
|
|
|
|
processMessage(_m);
|
|
}
|
|
|
|
|
|
/***** Dialogs ******/
|
|
-(void) MessageDialog :(NSString*)dialogType :(NSString*)title :(NSString*)message :(NSString*)button1 :(NSString*)button2 :(NSString*)button3 :(NSString*)button4 :(NSString*)defaultButton :(NSString*)cancelButton :(void*)iconData :(int)iconDataLength {
|
|
|
|
WailsAlert *alert = [WailsAlert new];
|
|
|
|
int style = NSAlertStyleInformational;
|
|
if (dialogType != nil ) {
|
|
if( [dialogType isEqualToString:@"warning"] ) {
|
|
style = NSAlertStyleWarning;
|
|
}
|
|
if( [dialogType isEqualToString:@"error"] ) {
|
|
style = NSAlertStyleCritical;
|
|
}
|
|
}
|
|
[alert setAlertStyle:style];
|
|
if( title != nil ) {
|
|
[alert setMessageText:title];
|
|
}
|
|
if( message != nil ) {
|
|
[alert setInformativeText:message];
|
|
}
|
|
|
|
[alert addButton:button1 :defaultButton :cancelButton];
|
|
[alert addButton:button2 :defaultButton :cancelButton];
|
|
[alert addButton:button3 :defaultButton :cancelButton];
|
|
[alert addButton:button4 :defaultButton :cancelButton];
|
|
|
|
NSImage *icon = nil;
|
|
if (iconData != nil) {
|
|
NSData *imageData = [NSData dataWithBytes:iconData length:iconDataLength];
|
|
icon = [[NSImage alloc] initWithData:imageData];
|
|
}
|
|
if( icon != nil) {
|
|
[alert setIcon:icon];
|
|
}
|
|
[alert.window setLevel:NSFloatingWindowLevel];
|
|
|
|
long response = [alert runModal];
|
|
int result;
|
|
|
|
if( response == NSAlertFirstButtonReturn ) {
|
|
result = 0;
|
|
}
|
|
else if( response == NSAlertSecondButtonReturn ) {
|
|
result = 1;
|
|
}
|
|
else if( response == NSAlertThirdButtonReturn ) {
|
|
result = 2;
|
|
} else {
|
|
result = 3;
|
|
}
|
|
processMessageDialogResponse(result);
|
|
}
|
|
|
|
-(void) OpenFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)allowDirectories :(bool)allowFiles :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)resolveAliases :(bool)showHiddenFiles :(bool)allowMultipleSelection :(NSString*)filters {
|
|
|
|
|
|
// Create the dialog
|
|
NSOpenPanel *dialog = [NSOpenPanel openPanel];
|
|
|
|
// Valid but appears to do nothing.... :/
|
|
if( title != nil ) {
|
|
[dialog setTitle:title];
|
|
}
|
|
|
|
// Filters - semicolon delimited list of file extensions
|
|
if( allowFiles ) {
|
|
if( filters != nil && [filters length] > 0) {
|
|
filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
|
filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""];
|
|
NSArray *filterList = [filters componentsSeparatedByString:@";"];
|
|
#ifdef USE_NEW_FILTERS
|
|
NSMutableArray *contentTypes = [[NSMutableArray new] autorelease];
|
|
for (NSString *filter in filterList) {
|
|
if (@available(macOS 11.0, *)) {
|
|
UTType *t = [UTType typeWithFilenameExtension:filter];
|
|
[contentTypes addObject:t];
|
|
}
|
|
}
|
|
if (@available(macOS 11.0, *)) {
|
|
[dialog setAllowedContentTypes:contentTypes];
|
|
}
|
|
#else
|
|
[dialog setAllowedFileTypes:filterList];
|
|
#endif
|
|
} else {
|
|
[dialog setAllowsOtherFileTypes:true];
|
|
}
|
|
// Default Filename
|
|
if( defaultFilename != nil ) {
|
|
[dialog setNameFieldStringValue:defaultFilename];
|
|
}
|
|
|
|
[dialog setAllowsMultipleSelection: allowMultipleSelection];
|
|
[dialog setShowsHiddenFiles: showHiddenFiles];
|
|
|
|
}
|
|
|
|
// Default Directory
|
|
if( defaultDirectory != nil ) {
|
|
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
|
[dialog setDirectoryURL:url];
|
|
}
|
|
|
|
|
|
// Setup Options
|
|
[dialog setCanChooseFiles: allowFiles];
|
|
[dialog setCanChooseDirectories: allowDirectories];
|
|
[dialog setCanCreateDirectories: canCreateDirectories];
|
|
[dialog setResolvesAliases: resolveAliases];
|
|
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
|
|
|
// Setup callback handler
|
|
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
|
if ( returnCode != NSModalResponseOK) {
|
|
processOpenFileDialogResponse("[]");
|
|
return;
|
|
}
|
|
NSMutableArray *arr = [NSMutableArray new];
|
|
for (NSURL *url in [dialog URLs]) {
|
|
[arr addObject:[url path]];
|
|
}
|
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:arr options:0 error:nil];
|
|
NSString *nsjson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
|
processOpenFileDialogResponse([nsjson UTF8String]);
|
|
[nsjson release];
|
|
[arr release];
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
-(void) SaveFileDialog :(NSString*)title :(NSString*)defaultFilename :(NSString*)defaultDirectory :(bool)canCreateDirectories :(bool)treatPackagesAsDirectories :(bool)showHiddenFiles :(NSString*)filters; {
|
|
|
|
|
|
// Create the dialog
|
|
NSSavePanel *dialog = [NSSavePanel savePanel];
|
|
|
|
// Do not hide extension
|
|
[dialog setExtensionHidden:false];
|
|
|
|
// Valid but appears to do nothing.... :/
|
|
if( title != nil ) {
|
|
[dialog setTitle:title];
|
|
}
|
|
|
|
// Filters - semicolon delimited list of file extensions
|
|
if( filters != nil && [filters length] > 0) {
|
|
filters = [filters stringByReplacingOccurrencesOfString:@"*." withString:@""];
|
|
filters = [filters stringByReplacingOccurrencesOfString:@" " withString:@""];
|
|
NSArray *filterList = [filters componentsSeparatedByString:@";"];
|
|
#ifdef USE_NEW_FILTERS
|
|
NSMutableArray *contentTypes = [[NSMutableArray new] autorelease];
|
|
for (NSString *filter in filterList) {
|
|
if (@available(macOS 11.0, *)) {
|
|
UTType *t = [UTType typeWithFilenameExtension:filter];
|
|
[contentTypes addObject:t];
|
|
}
|
|
}
|
|
if( contentTypes.count == 0) {
|
|
[dialog setAllowsOtherFileTypes:true];
|
|
} else {
|
|
if (@available(macOS 11.0, *)) {
|
|
[dialog setAllowedContentTypes:contentTypes];
|
|
}
|
|
}
|
|
|
|
#else
|
|
[dialog setAllowedFileTypes:filterList];
|
|
#endif
|
|
} else {
|
|
[dialog setAllowsOtherFileTypes:true];
|
|
}
|
|
// Default Filename
|
|
if( defaultFilename != nil ) {
|
|
[dialog setNameFieldStringValue:defaultFilename];
|
|
}
|
|
|
|
// Default Directory
|
|
if( defaultDirectory != nil ) {
|
|
NSURL *url = [NSURL fileURLWithPath:defaultDirectory];
|
|
[dialog setDirectoryURL:url];
|
|
}
|
|
|
|
// Setup Options
|
|
[dialog setCanSelectHiddenExtension:true];
|
|
// dialog.isExtensionHidden = false;
|
|
[dialog setCanCreateDirectories: canCreateDirectories];
|
|
[dialog setTreatsFilePackagesAsDirectories: treatPackagesAsDirectories];
|
|
[dialog setShowsHiddenFiles: showHiddenFiles];
|
|
|
|
// Setup callback handler
|
|
[dialog beginSheetModalForWindow:self.mainWindow completionHandler:^(NSModalResponse returnCode) {
|
|
if ( returnCode == NSModalResponseOK ) {
|
|
NSURL *url = [dialog URL];
|
|
if ( url != nil ) {
|
|
processSaveFileDialogResponse([url.path UTF8String]);
|
|
return;
|
|
}
|
|
}
|
|
processSaveFileDialogResponse("");
|
|
}];
|
|
|
|
}
|
|
|
|
- (void) SetAbout :(NSString*)title :(NSString*)description :(void*)imagedata :(int)datalen {
|
|
self.aboutTitle = title;
|
|
self.aboutDescription = description;
|
|
|
|
NSData *imageData = [NSData dataWithBytes:imagedata length:datalen];
|
|
self.aboutImage = [[NSImage alloc] initWithData:imageData];
|
|
}
|
|
|
|
-(void) About {
|
|
|
|
WailsAlert *alert = [WailsAlert new];
|
|
[alert setAlertStyle:NSAlertStyleInformational];
|
|
if( self.aboutTitle != nil ) {
|
|
[alert setMessageText:self.aboutTitle];
|
|
}
|
|
if( self.aboutDescription != nil ) {
|
|
[alert setInformativeText:self.aboutDescription];
|
|
}
|
|
|
|
|
|
[alert.window setLevel:NSFloatingWindowLevel];
|
|
if ( self.aboutImage != nil) {
|
|
[alert setIcon:self.aboutImage];
|
|
}
|
|
|
|
[alert runModal];
|
|
}
|
|
|
|
@end
|
|
|