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

* Feature/1090 native drag and drop for file and folder (#3203) * implement basic dnd for linux * implemented windows * progress changed linux handling and added coordinates to drop * progress fix drop coordinates on windows * progress remove log from windows * progress move js * update js after merge * fix event listener registration * fix segfault on non file drag * remove logs, fix coordinates * minor changes, simplify to drop only * rename EnableWails -> EnableFileDrop * add documentation (PR id missing) * add PR id to changelog * fix remove casting from malloc * fix nil check for OnFileDrop's callback * fix nil check for OnFileDrop skip event when nil * add error message for nil callback in OnFileDrop --------- Co-authored-by: lyimmi <lelvente.zambo@gmail.com> Co-authored-by: Lea Anthony <lea.anthony@gmail.com> * implement native drag and drop for macOS (#3250) * implement native drag and drop for macOS * update docs * add to changelog * update docs (macOS is supported) * Fix windows DragAndDrop options * Fix class unset on dragleave for full frame elements * improve class unset (nested elements and borders case) * Fix runtime drop target detection and CSS class assignment * Edit changelog * Fix drag-and-drop options in references * Update v2/internal/frontend/desktop/darwin/WailsWebView.m * Update v2/internal/frontend/desktop/darwin/WailsWebView.m --------- Co-authored-by: Zámbó, Levente <levente.zambo@gmail.com> Co-authored-by: lyimmi <lelvente.zambo@gmail.com> Co-authored-by: Lea Anthony <lea.anthony@gmail.com> Co-authored-by: Andrey Pshenkin <andrey.pshenkin@gmail.com> Co-authored-by: Pavel Binar <pavel@beamtransfer.io>
429 lines
14 KiB
Objective-C
429 lines
14 KiB
Objective-C
//go:build darwin
|
|
//
|
|
// Application.m
|
|
//
|
|
// Created by Lea Anthony on 10/10/21.
|
|
//
|
|
|
|
#import <Foundation/Foundation.h>
|
|
#import <Cocoa/Cocoa.h>
|
|
#import "WailsContext.h"
|
|
#import "Application.h"
|
|
#import "AppDelegate.h"
|
|
#import "WindowDelegate.h"
|
|
#import "WailsMenu.h"
|
|
#import "WailsMenuItem.h"
|
|
|
|
WailsContext* Create(const char* title, int width, int height, int frameless, int resizable, int zoomable, int fullscreen, int fullSizeContent, int hideTitleBar, int titlebarAppearsTransparent, int hideTitle, int useToolbar, int hideToolbarSeparator, int webviewIsTransparent, int alwaysOnTop, int hideWindowOnClose, const char *appearance, int windowIsTranslucent, int devtoolsEnabled, int defaultContextMenuEnabled, int windowStartState, int startsHidden, int minWidth, int minHeight, int maxWidth, int maxHeight, bool fraudulentWebsiteWarningEnabled, struct Preferences preferences, int singleInstanceLockEnabled, const char* singleInstanceUniqueId, bool enableDragAndDrop, bool disableWebViewDragAndDrop) {
|
|
|
|
[NSApplication sharedApplication];
|
|
|
|
WailsContext *result = [WailsContext new];
|
|
|
|
result.devtoolsEnabled = devtoolsEnabled;
|
|
result.defaultContextMenuEnabled = defaultContextMenuEnabled;
|
|
|
|
if ( windowStartState == WindowStartsFullscreen ) {
|
|
fullscreen = 1;
|
|
}
|
|
|
|
[result CreateWindow:width :height :frameless :resizable :zoomable :fullscreen :fullSizeContent :hideTitleBar :titlebarAppearsTransparent :hideTitle :useToolbar :hideToolbarSeparator :webviewIsTransparent :hideWindowOnClose :safeInit(appearance) :windowIsTranslucent :minWidth :minHeight :maxWidth :maxHeight :fraudulentWebsiteWarningEnabled :preferences :enableDragAndDrop :disableWebViewDragAndDrop];
|
|
[result SetTitle:safeInit(title)];
|
|
[result Center];
|
|
|
|
switch( windowStartState ) {
|
|
case WindowStartsMaximised:
|
|
[result.mainWindow zoom:nil];
|
|
break;
|
|
case WindowStartsMinimised:
|
|
//TODO: Can you start a mac app minimised?
|
|
break;
|
|
}
|
|
|
|
if ( startsHidden == 1 ) {
|
|
result.startHidden = true;
|
|
}
|
|
|
|
if ( fullscreen == 1 ) {
|
|
result.startFullscreen = true;
|
|
}
|
|
|
|
if ( singleInstanceLockEnabled == 1 ) {
|
|
result.singleInstanceLockEnabled = true;
|
|
result.singleInstanceUniqueId = safeInit(singleInstanceUniqueId);
|
|
}
|
|
|
|
result.alwaysOnTop = alwaysOnTop;
|
|
result.hideOnClose = hideWindowOnClose;
|
|
|
|
return result;
|
|
}
|
|
|
|
void ExecJS(void* inctx, const char *script) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSString *nsscript = safeInit(script);
|
|
ON_MAIN_THREAD(
|
|
[ctx ExecJS:nsscript];
|
|
[nsscript release];
|
|
);
|
|
}
|
|
|
|
void SetTitle(void* inctx, const char *title) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSString *_title = safeInit(title);
|
|
ON_MAIN_THREAD(
|
|
[ctx SetTitle:_title];
|
|
);
|
|
}
|
|
|
|
|
|
void SetBackgroundColour(void *inctx, int r, int g, int b, int a) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetBackgroundColour:r :g :b :a];
|
|
);
|
|
}
|
|
|
|
void SetSize(void* inctx, int width, int height) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetSize:width :height];
|
|
);
|
|
}
|
|
|
|
void SetAlwaysOnTop(void* inctx, int onTop) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetAlwaysOnTop:onTop];
|
|
);
|
|
}
|
|
|
|
void SetMinSize(void* inctx, int width, int height) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetMinSize:width :height];
|
|
);
|
|
}
|
|
|
|
void SetMaxSize(void* inctx, int width, int height) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetMaxSize:width :height];
|
|
);
|
|
}
|
|
|
|
void SetPosition(void* inctx, int x, int y) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx SetPosition:x :y];
|
|
);
|
|
}
|
|
|
|
void Center(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Center];
|
|
);
|
|
}
|
|
|
|
void Fullscreen(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Fullscreen];
|
|
);
|
|
}
|
|
|
|
void UnFullscreen(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx UnFullscreen];
|
|
);
|
|
}
|
|
|
|
void Minimise(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Minimise];
|
|
);
|
|
}
|
|
|
|
void UnMinimise(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx UnMinimise];
|
|
);
|
|
}
|
|
|
|
void Maximise(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Maximise];
|
|
);
|
|
}
|
|
|
|
void ToggleMaximise(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx ToggleMaximise];
|
|
);
|
|
}
|
|
|
|
const char* GetSize(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSRect frame = [ctx.mainWindow frame];
|
|
NSString *result = [NSString stringWithFormat:@"%d,%d", (int)frame.size.width, (int)frame.size.height];
|
|
return [result UTF8String];
|
|
}
|
|
|
|
const char* GetPosition(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSScreen* screen = [ctx getCurrentScreen];
|
|
NSRect windowFrame = [ctx.mainWindow frame];
|
|
NSRect screenFrame = [screen visibleFrame];
|
|
int x = windowFrame.origin.x - screenFrame.origin.x;
|
|
int y = windowFrame.origin.y - screenFrame.origin.y;
|
|
y = screenFrame.size.height - y - windowFrame.size.height;
|
|
NSString *result = [NSString stringWithFormat:@"%d,%d",x,y];
|
|
return [result UTF8String];
|
|
}
|
|
|
|
const bool IsFullScreen(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
return [ctx IsFullScreen];
|
|
}
|
|
|
|
const bool IsMinimised(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
return [ctx IsMinimised];
|
|
}
|
|
|
|
const bool IsMaximised(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
return [ctx IsMaximised];
|
|
}
|
|
|
|
void UnMaximise(void* inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx UnMaximise];
|
|
);
|
|
}
|
|
|
|
void Quit(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
[NSApp stop:ctx];
|
|
[NSApp abortModal];
|
|
}
|
|
|
|
void Hide(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Hide];
|
|
);
|
|
}
|
|
|
|
void Show(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx Show];
|
|
);
|
|
}
|
|
|
|
|
|
void HideApplication(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx HideApplication];
|
|
);
|
|
}
|
|
|
|
void ShowApplication(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
[ctx ShowApplication];
|
|
);
|
|
}
|
|
|
|
NSString* safeInit(const char* input) {
|
|
NSString *result = nil;
|
|
if (input != nil) {
|
|
result = [NSString stringWithUTF8String:input];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void MessageDialog(void *inctx, const char* dialogType, const char* title, const char* message, const char* button1, const char* button2, const char* button3, const char* button4, const char* defaultButton, const char* cancelButton, void* iconData, int iconDataLength) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
|
|
NSString *_dialogType = safeInit(dialogType);
|
|
NSString *_title = safeInit(title);
|
|
NSString *_message = safeInit(message);
|
|
NSString *_button1 = safeInit(button1);
|
|
NSString *_button2 = safeInit(button2);
|
|
NSString *_button3 = safeInit(button3);
|
|
NSString *_button4 = safeInit(button4);
|
|
NSString *_defaultButton = safeInit(defaultButton);
|
|
NSString *_cancelButton = safeInit(cancelButton);
|
|
|
|
ON_MAIN_THREAD(
|
|
[ctx MessageDialog:_dialogType :_title :_message :_button1 :_button2 :_button3 :_button4 :_defaultButton :_cancelButton :iconData :iconDataLength];
|
|
)
|
|
}
|
|
|
|
void OpenFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int allowDirectories, int allowFiles, int canCreateDirectories, int treatPackagesAsDirectories, int resolveAliases, int showHiddenFiles, int allowMultipleSelection, const char* filters) {
|
|
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSString *_title = safeInit(title);
|
|
NSString *_defaultFilename = safeInit(defaultFilename);
|
|
NSString *_defaultDirectory = safeInit(defaultDirectory);
|
|
NSString *_filters = safeInit(filters);
|
|
|
|
ON_MAIN_THREAD(
|
|
[ctx OpenFileDialog:_title :_defaultFilename :_defaultDirectory :allowDirectories :allowFiles :canCreateDirectories :treatPackagesAsDirectories :resolveAliases :showHiddenFiles :allowMultipleSelection :_filters];
|
|
)
|
|
}
|
|
|
|
void SaveFileDialog(void *inctx, const char* title, const char* defaultFilename, const char* defaultDirectory, int canCreateDirectories, int treatPackagesAsDirectories, int showHiddenFiles, const char* filters) {
|
|
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSString *_title = safeInit(title);
|
|
NSString *_defaultFilename = safeInit(defaultFilename);
|
|
NSString *_defaultDirectory = safeInit(defaultDirectory);
|
|
NSString *_filters = safeInit(filters);
|
|
|
|
ON_MAIN_THREAD(
|
|
[ctx SaveFileDialog:_title :_defaultFilename :_defaultDirectory :canCreateDirectories :treatPackagesAsDirectories :showHiddenFiles :_filters];
|
|
)
|
|
}
|
|
|
|
void AppendRole(void *inctx, void *inMenu, int role) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
|
[menu appendRole :ctx :role];
|
|
}
|
|
|
|
void* NewMenu(const char *name) {
|
|
NSString *title = @"";
|
|
if (name != nil) {
|
|
title = [NSString stringWithUTF8String:name];
|
|
}
|
|
WailsMenu *result = [[WailsMenu new] initWithNSTitle:title];
|
|
return result;
|
|
}
|
|
|
|
void AppendSubmenu(void* inparent, void* inchild) {
|
|
WailsMenu *parent = (__bridge WailsMenu*) inparent;
|
|
WailsMenu *child = (__bridge WailsMenu*) inchild;
|
|
[parent appendSubmenu:child];
|
|
}
|
|
|
|
void SetAsApplicationMenu(void *inctx, void *inMenu) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
|
ctx.applicationMenu = menu;
|
|
}
|
|
|
|
void UpdateApplicationMenu(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
ON_MAIN_THREAD(
|
|
NSApplication *app = [NSApplication sharedApplication];
|
|
[app setMainMenu:ctx.applicationMenu];
|
|
)
|
|
}
|
|
|
|
void SetAbout(void *inctx, const char* title, const char* description, void* imagedata, int datalen) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSString *_title = safeInit(title);
|
|
NSString *_description = safeInit(description);
|
|
|
|
[ctx SetAbout :_title :_description :imagedata :datalen];
|
|
}
|
|
|
|
void* AppendMenuItem(void* inctx, void* inMenu, const char* label, const char* shortcutKey, int modifiers, int disabled, int checked, int menuItemID) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
|
NSString *_label = safeInit(label);
|
|
NSString *_shortcutKey = safeInit(shortcutKey);
|
|
|
|
return [menu AppendMenuItem:ctx :_label :_shortcutKey :modifiers :disabled :checked :menuItemID];
|
|
}
|
|
|
|
void UpdateMenuItem(void* nsmenuitem, int checked) {
|
|
ON_MAIN_THREAD(
|
|
WailsMenuItem *menuItem = (__bridge WailsMenuItem*) nsmenuitem;
|
|
[menuItem setState:(checked == 1?NSControlStateValueOn:NSControlStateValueOff)];
|
|
)
|
|
}
|
|
|
|
|
|
void AppendSeparator(void* inMenu) {
|
|
WailsMenu *menu = (__bridge WailsMenu*) inMenu;
|
|
[menu AppendSeparator];
|
|
}
|
|
|
|
|
|
|
|
void Run(void *inctx, const char* url) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
NSApplication *app = [NSApplication sharedApplication];
|
|
AppDelegate* delegate = [AppDelegate new];
|
|
[app setDelegate:(id)delegate];
|
|
ctx.appdelegate = delegate;
|
|
delegate.mainWindow = ctx.mainWindow;
|
|
delegate.alwaysOnTop = ctx.alwaysOnTop;
|
|
delegate.startHidden = ctx.startHidden;
|
|
delegate.singleInstanceLockEnabled = ctx.singleInstanceLockEnabled;
|
|
delegate.singleInstanceUniqueId = ctx.singleInstanceUniqueId;
|
|
delegate.startFullscreen = ctx.startFullscreen;
|
|
|
|
NSString *_url = safeInit(url);
|
|
[ctx loadRequest:_url];
|
|
[_url release];
|
|
|
|
[app setMainMenu:ctx.applicationMenu];
|
|
}
|
|
|
|
void RunMainLoop(void) {
|
|
NSApplication *app = [NSApplication sharedApplication];
|
|
[app run];
|
|
}
|
|
|
|
void ReleaseContext(void *inctx) {
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
[ctx release];
|
|
}
|
|
|
|
// Credit: https://stackoverflow.com/q/33319295
|
|
void WindowPrint(void *inctx) {
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 110000
|
|
if (@available(macOS 11.0, *)) {
|
|
ON_MAIN_THREAD(
|
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
|
WKWebView* webView = ctx.webview;
|
|
|
|
// I think this should be exposed as a config
|
|
// It directly affects the printed output/PDF
|
|
NSPrintInfo *pInfo = [NSPrintInfo sharedPrintInfo];
|
|
pInfo.horizontalPagination = NSPrintingPaginationModeAutomatic;
|
|
pInfo.verticalPagination = NSPrintingPaginationModeAutomatic;
|
|
pInfo.verticallyCentered = YES;
|
|
pInfo.horizontallyCentered = YES;
|
|
pInfo.orientation = NSPaperOrientationLandscape;
|
|
pInfo.leftMargin = 0;
|
|
pInfo.rightMargin = 0;
|
|
pInfo.topMargin = 0;
|
|
pInfo.bottomMargin = 0;
|
|
|
|
NSPrintOperation *po = [webView printOperationWithPrintInfo:pInfo];
|
|
po.showsPrintPanel = YES;
|
|
po.showsProgressPanel = YES;
|
|
|
|
po.view.frame = webView.bounds;
|
|
|
|
[po runOperationModalForWindow:ctx.mainWindow delegate:ctx.mainWindow.delegate didRunSelector:nil contextInfo:nil];
|
|
)
|
|
}
|
|
#endif
|
|
}
|