From 62a37f34a66bfe63653c3f5f00908d757d248af8 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 4 May 2024 13:29:02 +0200 Subject: [PATCH] support dialogs in macos webview Signed-off-by: falkTX --- src/CHOC | 2 +- src/DPF | 2 +- src/PawPaw | 2 +- src/plugin/Makefile | 28 +-- src/plugin/WebView.mm | 78 ------- .../{WebViewWin32.cpp => WebViewCHOC.cpp} | 42 +++- src/plugin/WebViewWK.mm | 205 ++++++++++++++++++ src/plugin/utils.cpp | 4 +- src/systray/utils.cpp | 2 +- 9 files changed, 258 insertions(+), 107 deletions(-) delete mode 100644 src/plugin/WebView.mm rename src/plugin/{WebViewWin32.cpp => WebViewCHOC.cpp} (58%) create mode 100644 src/plugin/WebViewWK.mm diff --git a/src/CHOC b/src/CHOC index 337d39f..2512542 160000 --- a/src/CHOC +++ b/src/CHOC @@ -1 +1 @@ -Subproject commit 337d39fa32cd201f64874a06864ff1afc1e47295 +Subproject commit 2512542b2d65f3e92df7f2f1f7eeb712fa41a0de diff --git a/src/DPF b/src/DPF index a05231b..a887787 160000 --- a/src/DPF +++ b/src/DPF @@ -1 +1 @@ -Subproject commit a05231b01dd2376f9cf80019e4e6c00e716647e1 +Subproject commit a887787bcc0f7dc09d8f66d10990e5c194364171 diff --git a/src/PawPaw b/src/PawPaw index 0edef21..579d144 160000 --- a/src/PawPaw +++ b/src/PawPaw @@ -1 +1 @@ -Subproject commit 0edef2120e7fd493493e66967438c0b2ea8c8552 +Subproject commit 579d1441d0ea01bd0404e70bbbd2d889eb7f1064 diff --git a/src/plugin/Makefile b/src/plugin/Makefile index 61bde78..d843c24 100644 --- a/src/plugin/Makefile +++ b/src/plugin/Makefile @@ -24,9 +24,9 @@ FILES_DSP = \ FILES_UI = DesktopUI.cpp NanoButton.cpp utils.cpp ifeq ($(MACOS),true) -FILES_UI += WebView.mm +FILES_UI += WebViewWK.mm else ifeq ($(WINDOWS),true) -FILES_UI += WebViewWin32.cpp +FILES_UI += WebViewCHOC.cpp else FILES_UI += WebViewX11.cpp endif @@ -40,29 +40,31 @@ USING_WEBVIEW = true include ../DPF/Makefile.plugins.mk -BUILD_CXX_FLAGS += -DVERSION='"$(shell cat ../../VERSION)"' +TARGETS = features au clap lv2_sep vst2 vst3 +BUILD_CXX_FLAGS += -DVERSION='"$(shell cat ../../VERSION)"' BUILD_CXX_FLAGS += -pthread -LINK_FLAGS += -pthread ifeq ($(MACOS),true) -LINK_FLAGS += -framework CoreFoundation -framework IOKit -framework WebKit else ifeq ($(WINDOWS),true) -LINK_FLAGS += -lwinmm -else ifeq ($(LINUX),true) +BUILD_CXX_FLAGS += -I../CHOC +else BUILD_CXX_FLAGS += $(shell $(PKG_CONFIG) --cflags Qt5Core) # -std=gnu++14 -LINK_FLAGS += -ldl -lrt endif -TARGETS = features au clap lv2_sep vst2 vst3 +LINK_FLAGS += -pthread -ifeq ($(WINDOWS),true) -BUILD_CXX_FLAGS += -I../CHOC -LINK_FLAGS += -lole32 -$(BUILD_DIR)/WebViewWin32.cpp.o: BUILD_CXX_FLAGS += -std=gnu++17 +ifeq ($(MACOS),true) +LINK_FLAGS += -framework CoreFoundation -framework IOKit -framework WebKit +else ifeq ($(WINDOWS),true) +LINK_FLAGS += -lole32 -lwinmm +else +LINK_FLAGS += -ldl -lrt endif +$(BUILD_DIR)/WebViewCHOC.cpp.o: BUILD_CXX_FLAGS += -std=gnu++17 + # --------------------------------------------------------------------------------------------------------------------- all: $(TARGETS) diff --git a/src/plugin/WebView.mm b/src/plugin/WebView.mm deleted file mode 100644 index e1ab108..0000000 --- a/src/plugin/WebView.mm +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-FileCopyrightText: 2023-2024 MOD Audio UG -// SPDX-License-Identifier: AGPL-3.0-or-later - -#include "WebView.hpp" - -#include "DistrhoPluginInfo.h" - -#import -#import - -START_NAMESPACE_DISTRHO - -// ----------------------------------------------------------------------------------------------------------- - -struct WebViewImpl { - NSView* const view; - WKWebView* const webview; - NSURLRequest* const urlreq; -}; - -// ----------------------------------------------------------------------------------------------------------- - -void* addWebView(const uintptr_t parentWinId, double, const uint port) -{ - NSView* const view = reinterpret_cast(parentWinId); - - const CGRect rect = CGRectMake(0, - kVerticalOffset, - DISTRHO_UI_DEFAULT_WIDTH, - DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset); - - WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect]; - [[[webview configuration] preferences] setValue:@(true) forKey:@"developerExtrasEnabled"]; - [view addSubview:webview]; - - char url[32]; - std::snprintf(url, 31, "http://127.0.0.1:%u/", port); - NSString* const nsurl = [[NSString alloc] - initWithBytes:url - length:std::strlen(url) - encoding:NSUTF8StringEncoding]; - NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]]; - [nsurl release]; - - [webview loadRequest:urlreq]; - [webview setHidden:NO]; - - return new WebViewImpl{view, webview, urlreq}; -} - -void destroyWebView(void* const webview) -{ - WebViewImpl* const impl = static_cast(webview); - - [impl->webview setHidden:YES]; - [impl->webview removeFromSuperview]; - [impl->urlreq release]; - - delete impl; -} - -void reloadWebView(void* const webview, uint) -{ - WebViewImpl* const impl = static_cast(webview); - - [impl->webview loadRequest:impl->urlreq]; -} - -void resizeWebView(void* const webview, const uint offset, const uint width, const uint height) -{ - WebViewImpl* const impl = static_cast(webview); - - [impl->webview setFrame:CGRectMake(0, offset, width, height)]; -} - -// ----------------------------------------------------------------------------------------------------------- - -END_NAMESPACE_DISTRHO diff --git a/src/plugin/WebViewWin32.cpp b/src/plugin/WebViewCHOC.cpp similarity index 58% rename from src/plugin/WebViewWin32.cpp rename to src/plugin/WebViewCHOC.cpp index 690c0eb..d271235 100644 --- a/src/plugin/WebViewWin32.cpp +++ b/src/plugin/WebViewCHOC.cpp @@ -7,33 +7,50 @@ #define WC_ERR_INVALID_CHARS 0 #include "gui/choc_WebView.h" +#ifdef __APPLE__ +# import +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- void* addWebView(const uintptr_t parentWinId, const double scaleFactor, const uint port) { - std::unique_ptr webview = std::make_unique(choc::ui::WebView()); + std::unique_ptr webview = std::make_unique(); DISTRHO_SAFE_ASSERT_RETURN(webview->loadedOK(), nullptr); - const HWND handle = static_cast(webview->getViewHandle()); + void* const handle = webview->getViewHandle(); DISTRHO_SAFE_ASSERT_RETURN(handle != nullptr, nullptr); char url[32] = {}; std::snprintf(url, 31, "http://127.0.0.1:%u/", port); webview->navigate(url); - LONG_PTR flags = GetWindowLongPtr(handle, -16); + #if defined(__APPLE__) + NSView* const view = static_cast(handle); + + [reinterpret_cast(parentWinId) addSubview:view]; + [view setFrame:NSMakeRect(0, + kVerticalOffset, + DISTRHO_UI_DEFAULT_WIDTH, + DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset)]; + #elif defined(_WIN32) + const HWND hwnd = static_cast(handle); + + LONG_PTR flags = GetWindowLongPtr(hwnd, -16); flags = (flags & ~WS_POPUP) | WS_CHILD; - SetWindowLongPtr(handle, -16, flags); + SetWindowLongPtr(hwnd, -16, flags); - SetParent(handle, reinterpret_cast(parentWinId)); - SetWindowPos(handle, nullptr, - 0, kVerticalOffset * scaleFactor, + SetParent(hwnd, reinterpret_cast(parentWinId)); + SetWindowPos(hwnd, nullptr, + 0, + kVerticalOffset * scaleFactor, DISTRHO_UI_DEFAULT_WIDTH * scaleFactor, (DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset) * scaleFactor, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); - ShowWindow(handle, SW_SHOW); + ShowWindow(hwnd, SW_SHOW); + #endif return webview.release(); } @@ -56,8 +73,13 @@ void resizeWebView(void* const webviewptr, const uint offset, const uint width, { choc::ui::WebView* const webview = static_cast(webviewptr); - const HWND handle = static_cast(webview->getViewHandle()); - SetWindowPos(handle, nullptr, 0, offset, width, height, SWP_NOZORDER | SWP_NOACTIVATE); + #if defined(__APPLE__) + NSView* const view = static_cast(webview->getViewHandle()); + [view setFrame:NSMakeRect(0, offset, width, height)]; + #elif defined(_WIN32) + const HWND hwnd = static_cast(webview->getViewHandle()); + SetWindowPos(hwnd, nullptr, 0, offset, width, height, SWP_NOZORDER | SWP_NOACTIVATE); + #endif } // ----------------------------------------------------------------------------------------------------------- diff --git a/src/plugin/WebViewWK.mm b/src/plugin/WebViewWK.mm new file mode 100644 index 0000000..d5a9583 --- /dev/null +++ b/src/plugin/WebViewWK.mm @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: 2023-2024 MOD Audio UG +// SPDX-License-Identifier: AGPL-3.0-or-later + +#include "WebView.hpp" + +#include "DistrhoPluginInfo.h" + +#import +#import + +// ----------------------------------------------------------------------------------------------------------- + +#define MACRO_NAME2(a, b, c) a ## b ## c +#define MACRO_NAME(a, b, c) MACRO_NAME2(a, b, c) + +#define WEBVIEW_DELEGATE_CLASS_NAME \ + MACRO_NAME(WebViewDelegate_, _, DISTRHO_NAMESPACE) + +@interface WEBVIEW_DELEGATE_CLASS_NAME : NSObject +@end + +@implementation WEBVIEW_DELEGATE_CLASS_NAME + +- (void)webView:(WKWebView*)webview + runJavaScriptAlertPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(void))completionHandler +{ + NSAlert* const alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert setInformativeText:message]; + [alert setMessageText:@"Alert"]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [alert beginSheetModalForWindow:[webview window] + completionHandler:^(NSModalResponse) + { + completionHandler(); + [alert release]; + }]; + }); +} + +- (void)webView:(WKWebView*)webview + runJavaScriptConfirmPanelWithMessage:(NSString*)message + initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(BOOL))completionHandler +{ + NSAlert* const alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + [alert setInformativeText:message]; + [alert setMessageText:@"Confirm"]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [alert beginSheetModalForWindow:[webview window] + completionHandler:^(NSModalResponse result) + { + completionHandler(result == NSAlertFirstButtonReturn); + [alert release]; + }]; + }); +} + +- (void)webView:(WKWebView*)webview + runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt + defaultText:(NSString*)defaultText + initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(NSString*))completionHandler +{ + NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)]; + [input setStringValue:defaultText]; + + NSAlert* const alert = [[NSAlert alloc] init]; + [alert setAccessoryView:input]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; + [alert setInformativeText:prompt]; + [alert setMessageText: @"Prompt"]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [alert beginSheetModalForWindow:[webview window] + completionHandler:^(NSModalResponse result) + { + [input validateEditing]; + completionHandler(result == NSAlertFirstButtonReturn ? [input stringValue] : nil); + [alert release]; + }]; + }); +} + +- (void)webView:(WKWebView*)webview + runOpenPanelWithParameters:(WKOpenPanelParameters*)params + initiatedByFrame:(WKFrameInfo*)frame + completionHandler:(void (^)(NSArray*))completionHandler +{ + NSOpenPanel* const panel = [[NSOpenPanel alloc] init]; + + [panel setAllowsMultipleSelection:[params allowsMultipleSelection]]; + // [panel setAllowedFileTypes:(NSArray*)[params _allowedFileExtensions]]; + [panel setCanChooseDirectories:[params allowsDirectories]]; + [panel setCanChooseFiles:![params allowsDirectories]]; + + dispatch_async(dispatch_get_main_queue(), ^ + { + [panel beginSheetModalForWindow:[webview window] + completionHandler:^(NSModalResponse result) + { + completionHandler(result == NSModalResponseOK ? [panel URLs] : nil); + [panel release]; + }]; + }); +} + +@end + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +struct WebViewImpl { + NSView* const view; + WKWebView* const webview; + NSURLRequest* const urlreq; + WEBVIEW_DELEGATE_CLASS_NAME* const delegate; +}; + +// ----------------------------------------------------------------------------------------------------------- + +void* addWebView(const uintptr_t parentWinId, double, const uint port) +{ + NSView* const view = reinterpret_cast(parentWinId); + + const CGRect rect = CGRectMake(0, + kVerticalOffset, + DISTRHO_UI_DEFAULT_WIDTH, + DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset); + + WKPreferences* const prefs = [[WKPreferences alloc] init]; + [prefs setValue:@YES forKey:@"developerExtrasEnabled"]; + + WKWebViewConfiguration* const config = [[WKWebViewConfiguration alloc] init]; + config.preferences = prefs; + + WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect + configuration:config]; + [view addSubview:webview]; + + WEBVIEW_DELEGATE_CLASS_NAME* const delegate = [[WEBVIEW_DELEGATE_CLASS_NAME alloc] init]; + webview.UIDelegate = delegate; + + char url[32]; + std::snprintf(url, 31, "http://127.0.0.1:%u/", port); + NSString* const nsurl = [[NSString alloc] initWithBytes:url + length:std::strlen(url) + encoding:NSUTF8StringEncoding]; + NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]]; + + [webview loadRequest:urlreq]; + [webview setHidden:NO]; + + [nsurl release]; + [config release]; + [prefs release]; + + return new WebViewImpl{view, webview, urlreq, delegate}; +} + +void destroyWebView(void* const webview) +{ + WebViewImpl* const impl = static_cast(webview); + + [impl->webview setHidden:YES]; + [impl->webview removeFromSuperview]; + [impl->urlreq release]; + [impl->delegate release]; + + delete impl; +} + +void reloadWebView(void* const webview, uint) +{ + WebViewImpl* const impl = static_cast(webview); + + [impl->webview loadRequest:impl->urlreq]; +} + +void resizeWebView(void* const webview, const uint offset, const uint width, const uint height) +{ + WebViewImpl* const impl = static_cast(webview); + + [impl->webview setFrame:CGRectMake(0, offset, width, height)]; +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +#undef MACRO_NAME +#undef MACRO_NAME2 diff --git a/src/plugin/utils.cpp b/src/plugin/utils.cpp index 31d9613..1dd5b3d 100644 --- a/src/plugin/utils.cpp +++ b/src/plugin/utils.cpp @@ -112,7 +112,7 @@ static const wchar_t* getAppDirW() const char* getAppDir() { #ifdef DISTRHO_OS_MAC - // return "/Users/falktx/Source/MOD/mod-app/build/mod-desktop.app/Contents/MacOS"; + return "/Users/falktx/Source/MOD/mod-app/build/mod-desktop.app/Contents/MacOS"; return "/Applications/MOD Desktop.app/Contents/MacOS"; #else static char appDir[PATH_MAX] = {}; @@ -348,7 +348,7 @@ char* const* getEvironment(const uint portBaseNum) { CFStringGetCString(uuidStr, path, PATH_MAX - 1, kCFStringEncodingUTF8); - for (int i=0, j=0; i+j<36; ++i) + for (int i=0, j=0; i<16 && i+j<36; ++i) { if (path[i*2+j] == '-') ++j; diff --git a/src/systray/utils.cpp b/src/systray/utils.cpp index 1aa6c93..7fe1831 100644 --- a/src/systray/utils.cpp +++ b/src/systray/utils.cpp @@ -177,7 +177,7 @@ void initEvironment() { CFStringGetCString(uuidStr, path, PATH_MAX - 1, kCFStringEncodingUTF8); - for (int i=0, j=0; i+j<36; ++i) + for (int i=0, j=0; i<16 && i+j<36; ++i) { if (path[i*2+j] == '-') ++j;