|
- diff --git a/choc_DesktopWindow.h b/choc_DesktopWindow.h
- index b948042..55e7305 100644
- --- a/choc_DesktopWindow.h
- +++ b/choc_DesktopWindow.h
- @@ -19,100 +19,7 @@
- #ifndef CHOC_DESKTOPWINDOW_HEADER_INCLUDED
- #define CHOC_DESKTOPWINDOW_HEADER_INCLUDED
-
- -#include "../platform/choc_Platform.h"
- -
- -
- -//==============================================================================
- -namespace choc::ui
- -{
- -
- -/// Represents the position and size of a DesktopWindow or other UI elements.
- -struct Bounds
- -{
- - int x = 0, y = 0, width = 0, height = 0;
- -};
- -
- -/**
- - A very basic desktop window class.
- -
- - The main use-case for this is as a simple way to host other UI elements
- - such as the choc::ui::WebView.
- -
- - Because this is a GUI, it needs a message loop to be running. If you're using
- - it inside an app which already runs a message loop, it should just work,
- - or you can use choc::messageloop::run() and choc::messageloop::stop() for an easy
- - but basic loop.
- -
- - Note that on Linux this uses GTK, so to build it you'll need to:
- - 1. Install the libgtk-3-dev package.
- - 2. Link the gtk+3.0 library in your build.
- - You might want to have a look inside choc/tests/CMakeLists.txt for
- - an example of how to add this packages to your build without too
- - much fuss.
- -
- - For an example of how to use this class, see `choc/tests/main.cpp` where
- - there's a simple demo.
- -*/
- -struct DesktopWindow
- -{
- - DesktopWindow (Bounds);
- - ~DesktopWindow();
- -
- - /// Sets the title of the window that the browser is inside
- - void setWindowTitle (const std::string& newTitle);
- -
- - /// Gives the window a child/content view to display.
- - /// The pointer being passed in will be a platform-specific native handle,
- - /// so a HWND on Windows, an NSView* on OSX, etc.
- - void setContent (void* nativeView);
- -
- - /// Shows or hides the window. It's visible by default when created.
- - void setVisible (bool visible);
- -
- - /// Changes the window's position
- - void setBounds (Bounds);
- -
- - /// Enables/disables user resizing of the window
- - void setResizable (bool);
- -
- - /// Enables/disables the window's close button (if applicable).
- - void setClosable (bool);
- -
- - /// Gives the window a given size and positions it in the middle of the
- - /// default monitor
- - void centreWithSize (int width, int height);
- -
- - /// Sets a minimum size below which the user can't shrink the window
- - void setMinimumSize (int minWidth, int minHeight);
- - /// Sets a maximum size above which the user can't grow the window
- - void setMaximumSize (int maxWidth, int maxHeight);
- -
- - /// Tries to bring this window to the front of the Z-order.
- - void toFront();
- -
- - /// Returns the native OS handle, which may be a HWND on Windows, an
- - /// NSWindow* on OSX or a GtkWidget* on linux.
- - void* getWindowHandle() const;
- -
- - /// An optional callback that will be called when the parent window is resized
- - std::function<void()> windowResized;
- - /// An optional callback that will be called when the parent window is closed
- - std::function<void()> windowClosed;
- -
- -private:
- - struct Pimpl;
- - std::unique_ptr<Pimpl> pimpl;
- -};
- -
- -//==============================================================================
- -/// This Windows-only function turns on high-DPI awareness for the current
- -/// process. On other OSes where no equivalent call is needed, this function is
- -/// just a stub.
- -void setWindowsDPIAwareness();
- -
- -
- -} // namespace choc::ui
- -
- +#include "choc_Platform.h"
-
- //==============================================================================
- // _ _ _ _
- @@ -125,326 +32,6 @@ void setWindowsDPIAwareness();
- //
- //==============================================================================
-
- -#if CHOC_LINUX
- -
- -struct choc::ui::DesktopWindow::Pimpl
- -{
- - Pimpl (DesktopWindow& w, Bounds bounds) : owner (w)
- - {
- - if (! gtk_init_check (nullptr, nullptr))
- - return;
- -
- - window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- - g_object_ref_sink (G_OBJECT (window));
- -
- - destroyHandlerID = g_signal_connect (G_OBJECT (window), "destroy",
- - G_CALLBACK (+[](GtkWidget*, gpointer arg)
- - {
- - static_cast<Pimpl*> (arg)->windowDestroyEvent();
- - }),
- - this);
- - setBounds (bounds);
- - setVisible (true);
- - }
- -
- - ~Pimpl()
- - {
- - if (destroyHandlerID != 0 && window != nullptr)
- - g_signal_handler_disconnect (G_OBJECT (window), destroyHandlerID);
- -
- - g_clear_object (&window);
- - }
- -
- - void windowDestroyEvent()
- - {
- - g_clear_object (&window);
- -
- - if (owner.windowClosed != nullptr)
- - owner.windowClosed();
- - }
- -
- - void* getWindowHandle() const { return (void*) window; }
- -
- - void setWindowTitle (const std::string& newTitle)
- - {
- - gtk_window_set_title (GTK_WINDOW (window), newTitle.c_str());
- - }
- -
- - void setContent (void* view)
- - {
- - if (content != nullptr)
- - gtk_container_remove (GTK_CONTAINER (window), content);
- -
- - content = GTK_WIDGET (view);
- - gtk_container_add (GTK_CONTAINER (window), content);
- - gtk_widget_grab_focus (content);
- - }
- -
- - void setVisible (bool visible)
- - {
- - if (visible)
- - gtk_widget_show_all (window);
- - else
- - gtk_widget_hide (window);
- - }
- -
- - void setResizable (bool b) { gtk_window_set_resizable (GTK_WINDOW (window), b); }
- - void setClosable (bool b) { gtk_window_set_deletable (GTK_WINDOW (window), b); }
- -
- - void setMinimumSize (int w, int h)
- - {
- - GdkGeometry g;
- - g.min_width = w;
- - g.min_height = h;
- - gtk_window_set_geometry_hints (GTK_WINDOW (window), nullptr, &g, GDK_HINT_MIN_SIZE);
- - }
- -
- - void setMaximumSize (int w, int h)
- - {
- - GdkGeometry g;
- - g.max_width = w;
- - g.max_height = h;
- - gtk_window_set_geometry_hints (GTK_WINDOW (window), nullptr, &g, GDK_HINT_MAX_SIZE);
- - }
- -
- - void setBounds (Bounds b)
- - {
- - setSize (b.width, b.height);
- - gtk_window_move (GTK_WINDOW (window), b.x, b.y);
- - }
- -
- - void setSize (int w, int h)
- - {
- - if (gtk_window_get_resizable (GTK_WINDOW (window)))
- - gtk_window_resize (GTK_WINDOW (window), w, h);
- - else
- - gtk_widget_set_size_request (window, w, h);
- - }
- -
- - void centreWithSize (int w, int h)
- - {
- - setSize (w, h);
- - gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
- - }
- -
- - void toFront()
- - {
- - gtk_window_activate_default (GTK_WINDOW (window));
- - }
- -
- - DesktopWindow& owner;
- - GtkWidget* window = {};
- - GtkWidget* content = {};
- - unsigned long destroyHandlerID = 0;
- -};
- -
- -inline void choc::ui::setWindowsDPIAwareness() {}
- -
- -//==============================================================================
- -#elif CHOC_APPLE
- -
- -#include "choc_MessageLoop.h"
- -
- -namespace choc::ui
- -{
- -
- -namespace macos_ui_helpers
- -{
- - // Including CodeGraphics.h can create all kinds of messy C/C++ symbol clashes
- - // with other headers, but all we actually need are these coordinate structs:
- - #if defined (__LP64__) && __LP64__
- - using CGFloat = double;
- - #else
- - using CGFloat = float;
- - #endif
- -
- - struct CGPoint { CGFloat x = 0, y = 0; };
- - struct CGSize { CGFloat width = 0, height = 0; };
- - struct CGRect { CGPoint origin; CGSize size; };
- -
- - inline CGSize createCGSize (double w, double h) { return { (CGFloat) w, (CGFloat) h }; }
- - inline CGRect createCGRect (choc::ui::Bounds b) { return { { (CGFloat) b.x, (CGFloat) b.y }, { (CGFloat) b.width, (CGFloat) b.height } }; }
- -
- - static constexpr long NSWindowStyleMaskTitled = 1;
- - static constexpr long NSWindowStyleMaskMiniaturizable = 4;
- - static constexpr long NSWindowStyleMaskResizable = 8;
- - static constexpr long NSWindowStyleMaskClosable = 2;
- - static constexpr long NSBackingStoreBuffered = 2;
- - static constexpr long NSApplicationActivationPolicyRegular = 0;
- -}
- -
- -using namespace macos_ui_helpers;
- -
- -inline void setWindowsDPIAwareness() {}
- -
- -struct DesktopWindow::Pimpl
- -{
- - Pimpl (DesktopWindow& w, Bounds bounds) : owner (w)
- - {
- - using namespace choc::objc;
- - CHOC_AUTORELEASE_BEGIN
- - call<void> (getSharedNSApplication(), "setActivationPolicy:", NSApplicationActivationPolicyRegular);
- -
- - window = call<id> (call<id> (getClass ("NSWindow"), "alloc"),
- - "initWithContentRect:styleMask:backing:defer:",
- - createCGRect (bounds),
- - NSWindowStyleMaskTitled, NSBackingStoreBuffered, (int) 0);
- -
- - delegate = createDelegate();
- - setStyleBit (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable, true);
- - objc_setAssociatedObject (delegate, "choc_window", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
- - call<void> (window, "setDelegate:", delegate);
- - CHOC_AUTORELEASE_END
- - }
- -
- - ~Pimpl()
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setDelegate:", nullptr);
- - objc::call<void> (window, "close");
- - objc::call<void> (delegate, "release");
- - CHOC_AUTORELEASE_END
- - }
- -
- - void* getWindowHandle() const { return (CHOC_OBJC_CAST_BRIDGED void*) window; }
- -
- - void setWindowTitle (const std::string& newTitle)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setTitle:", objc::getNSString (newTitle));
- - CHOC_AUTORELEASE_END
- - }
- -
- - void setContent (void* view)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setContentView:", (CHOC_OBJC_CAST_BRIDGED id) view);
- - CHOC_AUTORELEASE_END
- - }
- -
- - void setVisible (bool visible)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setIsVisible:", (BOOL) visible);
- - CHOC_AUTORELEASE_END
- - }
- -
- - void setStyleBit (long bit, bool shouldEnable)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - auto style = objc::call<unsigned long> (window, "styleMask");
- - style = shouldEnable ? (style | (unsigned long) bit) : (style & ~(unsigned long) bit);
- - objc::call<void> (window, "setStyleMask:", style);
- - CHOC_AUTORELEASE_END
- - }
- -
- - void setResizable (bool b) { setStyleBit (NSWindowStyleMaskResizable, b); }
- - void setClosable (bool b) { setStyleBit (NSWindowStyleMaskClosable, b); }
- -
- - void setMinimumSize (int w, int h) { CHOC_AUTORELEASE_BEGIN objc::call<void> (window, "setContentMinSize:", createCGSize (w, h)); CHOC_AUTORELEASE_END }
- - void setMaximumSize (int w, int h) { CHOC_AUTORELEASE_BEGIN objc::call<void> (window, "setContentMaxSize:", createCGSize (w, h)); CHOC_AUTORELEASE_END }
- -
- - CGRect getFrameRectForContent (Bounds b)
- - {
- - return objc::call<CGRect> (window, "frameRectForContentRect:", createCGRect (b));
- - }
- -
- - void centreWithSize (int w, int h)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setFrame:display:animate:", getFrameRectForContent ({ 0, 0, w, h }), (BOOL) 1, (BOOL) 0);
- - objc::call<void> (window, "center");
- - CHOC_AUTORELEASE_END
- - }
- -
- - void setBounds (Bounds b)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (window, "setFrame:display:animate:", getFrameRectForContent (b), (BOOL) 1, (BOOL) 0);
- - CHOC_AUTORELEASE_END
- - }
- -
- - void toFront()
- - {
- - CHOC_AUTORELEASE_BEGIN
- - objc::call<void> (objc::getSharedNSApplication(), "activateIgnoringOtherApps:", (BOOL) 1);
- - objc::call<void> (window, "makeKeyAndOrderFront:", (id) nullptr);
- - CHOC_AUTORELEASE_END
- - }
- -
- - static Pimpl& getPimplFromContext (id self)
- - {
- - auto view = (CHOC_OBJC_CAST_BRIDGED Pimpl*) objc_getAssociatedObject (self, "choc_window");
- - CHOC_ASSERT (view != nullptr);
- - return *view;
- - }
- -
- - id createDelegate()
- - {
- - static DelegateClass dc;
- - return objc::call<id> ((id) dc.delegateClass, "new");
- - }
- -
- - DesktopWindow& owner;
- - id window = {}, delegate = {};
- -
- - struct DelegateClass
- - {
- - DelegateClass()
- - {
- - delegateClass = choc::objc::createDelegateClass ("NSResponder", "CHOCDesktopWindowDelegate_");
- -
- - if (auto* p = objc_getProtocol ("NSWindowDelegate"))
- - class_addProtocol (delegateClass, p);
- -
- - class_addMethod (delegateClass, sel_registerName ("windowShouldClose:"),
- - (IMP) (+[](id self, SEL, id) -> BOOL
- - {
- - CHOC_AUTORELEASE_BEGIN
- - auto& p = getPimplFromContext (self);
- - p.window = {};
- -
- - if (auto callback = p.owner.windowClosed)
- - choc::messageloop::postMessage ([callback] { callback(); });
- -
- - CHOC_AUTORELEASE_END
- - return TRUE;
- - }),
- - "c@:@");
- -
- - class_addMethod (delegateClass, sel_registerName ("windowDidResize:"),
- - (IMP) (+[](id self, SEL, id)
- - {
- - CHOC_AUTORELEASE_BEGIN
- -
- - if (auto callback = getPimplFromContext (self).owner.windowResized)
- - callback();
- -
- - CHOC_AUTORELEASE_END
- - }),
- - "v@:@");
- -
- - class_addMethod (delegateClass, sel_registerName ("applicationShouldTerminateAfterLastWindowClosed:"),
- - (IMP) (+[](id, SEL, id) -> BOOL { return 0; }),
- - "c@:@");
- -
- - objc_registerClassPair (delegateClass);
- - }
- -
- - ~DelegateClass()
- - {
- - objc_disposeClassPair (delegateClass);
- - }
- -
- - Class delegateClass = {};
- - };
- -};
- -
- -} // namespace choc::ui
- -
- -//==============================================================================
- -#elif CHOC_WINDOWS
- -
- #undef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #undef NOMINMAX
- @@ -453,27 +40,7 @@ struct DesktopWindow::Pimpl
- #include <windows.h>
- #undef Rectangle
-
- -namespace choc::ui
- -{
- -
- -static RECT boundsToRect (Bounds b)
- -{
- - RECT r;
- - r.left = b.x;
- - r.top = b.y;
- - r.right = b.x + b.width;
- - r.bottom = b.y + b.height;
- - return r;
- -}
- -
- -template <typename FunctionType>
- -FunctionType getUser32Function (const char* name)
- -{
- - if (auto user32 = choc::file::DynamicLibrary ("user32.dll"))
- - return reinterpret_cast<FunctionType> (user32.findFunction (name));
- -
- - return {};
- -}
- +START_NAMESPACE_DISTRHO
-
- struct HWNDHolder
- {
- @@ -516,7 +83,7 @@ struct WindowClass
- wc.lpfnWndProc = wndProc;
-
- classAtom = (LPCWSTR) (uintptr_t) RegisterClassExW (&wc);
- - CHOC_ASSERT (classAtom != 0);
- + DISTRHO_SAFE_ASSERT (classAtom != 0);
- }
-
- ~WindowClass()
- @@ -582,250 +149,6 @@ static std::wstring createUTF16StringFromUTF8 (std::string_view utf8)
- return {};
- }
-
- -inline void setWindowsDPIAwareness()
- -{
- - if (auto setProcessDPIAwarenessContext = getUser32Function<int(__stdcall *)(void*)> ("SetProcessDpiAwarenessContext"))
- - setProcessDPIAwarenessContext (/*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ (void*) -4);
- -}
- -
- -//==============================================================================
- -struct DesktopWindow::Pimpl
- -{
- - Pimpl (DesktopWindow& w, Bounds b) : owner (w)
- - {
- - hwnd = windowClass.createWindow (WS_OVERLAPPEDWINDOW, 640, 480, this);
- -
- - if (hwnd.hwnd == nullptr)
- - return;
- -
- - setBounds (b);
- - ShowWindow (hwnd, SW_SHOW);
- - UpdateWindow (hwnd);
- - SetFocus (hwnd);
- - }
- -
- - ~Pimpl()
- - {
- - hwnd.reset();
- - }
- -
- - void* getWindowHandle() const { return hwnd; }
- -
- - void setWindowTitle (const std::string& newTitle)
- - {
- - SetWindowTextW (hwnd, createUTF16StringFromUTF8 (newTitle).c_str());
- - }
- -
- - void setContent (void* childHandle)
- - {
- - if (auto child = getFirstChildWindow())
- - {
- - ShowWindow (child, SW_HIDE);
- - SetParent (child, nullptr);
- - }
- -
- - auto child = (HWND) childHandle;
- - auto flags = GetWindowLongPtr (child, -16);
- - flags = (flags & ~(decltype (flags)) WS_POPUP) | (decltype (flags)) WS_CHILD;
- - SetWindowLongPtr (child, -16, flags);
- -
- - SetParent (child, hwnd);
- - resizeContentToFit();
- - ShowWindow (child, IsWindowVisible (hwnd) ? SW_SHOW : SW_HIDE);
- - }
- -
- - void setVisible (bool visible)
- - {
- - ShowWindow (hwnd, visible ? SW_SHOW : SW_HIDE);
- -
- - if (visible)
- - InvalidateRect (hwnd, nullptr, 0);
- - }
- -
- - void setResizable (bool b)
- - {
- - auto style = GetWindowLong (hwnd, GWL_STYLE);
- -
- - if (b)
- - style |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
- - else
- - style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
- -
- - SetWindowLong (hwnd, GWL_STYLE, style);
- - }
- -
- - void setClosable (bool closable)
- - {
- - EnableMenuItem (GetSystemMenu (hwnd, FALSE), SC_CLOSE,
- - closable ? (MF_BYCOMMAND | MF_ENABLED)
- - : (MF_BYCOMMAND | MF_DISABLED | MF_GRAYED));
- - }
- -
- - void setMinimumSize (int w, int h)
- - {
- - minimumSize.x = w;
- - minimumSize.y = h;
- - }
- -
- - void setMaximumSize (int w, int h)
- - {
- - maximumSize.x = w;
- - maximumSize.y = h;
- - }
- -
- - void getMinMaxInfo (MINMAXINFO& m) const
- - {
- - if (maximumSize.x > 0 && maximumSize.y > 0)
- - {
- - m.ptMaxSize = maximumSize;
- - m.ptMaxTrackSize = maximumSize;
- - }
- -
- - if (minimumSize.x > 0 && minimumSize.y > 0)
- - m.ptMinTrackSize = minimumSize;
- - }
- -
- - void centreWithSize (int w, int h)
- - {
- - auto dpi = static_cast<int> (getWindowDPI());
- - auto screenW = (GetSystemMetrics(SM_CXSCREEN) * 96) / dpi;
- - auto screenH = (GetSystemMetrics(SM_CYSCREEN) * 96) / dpi;
- - auto x = (screenW - w) / 2;
- - auto y = (screenH - h) / 2;
- - setBounds ({ x, y, w, h }, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
- - }
- -
- - void setBounds (Bounds b)
- - {
- - setBounds (b, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
- - }
- -
- - void setBounds (Bounds b, DWORD flags)
- - {
- - auto r = boundsToRect (scaleBounds (b, getWindowDPI() / 96.0));
- - AdjustWindowRect (&r, WS_OVERLAPPEDWINDOW, 0);
- - SetWindowPos (hwnd, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, flags);
- - resizeContentToFit();
- - }
- -
- - void toFront()
- - {
- - BringWindowToTop (hwnd);
- - }
- -
- -private:
- - DesktopWindow& owner;
- - HWNDHolder hwnd;
- - POINT minimumSize = {}, maximumSize = {};
- - WindowClass windowClass { L"CHOCWindow", (WNDPROC) wndProc };
- -
- - Bounds scaleBounds (Bounds b, double scale)
- - {
- - b.x = static_cast<decltype(b.x)> (b.x * scale);
- - b.y = static_cast<decltype(b.y)> (b.y * scale);
- - b.width = static_cast<decltype(b.width)> (b.width * scale);
- - b.height = static_cast<decltype(b.height)> (b.height * scale);
- -
- - return b;
- - }
- -
- - HWND getFirstChildWindow()
- - {
- - HWND result = {};
- -
- - if (IsWindow (hwnd))
- - {
- - EnumChildWindows (hwnd, +[](HWND w, LPARAM context)
- - {
- - *reinterpret_cast<HWND*> (context) = w;
- - return FALSE;
- - }, (LPARAM) &result);
- - }
- -
- - return result;
- - }
- -
- - void resizeContentToFit()
- - {
- - if (auto child = getFirstChildWindow())
- - {
- - RECT r;
- - GetClientRect (hwnd, &r);
- - SetWindowPos (child, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top,
- - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED);
- - }
- - }
- -
- - void handleClose()
- - {
- - if (owner.windowClosed != nullptr)
- - owner.windowClosed();
- - }
- -
- - void handleSizeChange()
- - {
- - resizeContentToFit();
- -
- - if (owner.windowResized != nullptr)
- - owner.windowResized();
- - }
- -
- - static void enableNonClientDPIScaling (HWND h)
- - {
- - if (auto fn = getUser32Function<BOOL(__stdcall*)(HWND)> ("EnableNonClientDpiScaling"))
- - fn (h);
- - }
- -
- - uint32_t getWindowDPI() const
- - {
- - if (auto getDpiForWindow = getUser32Function<UINT(__stdcall*)(HWND)> ("GetDpiForWindow"))
- - return getDpiForWindow (hwnd);
- -
- - return 96;
- - }
- -
- - static Pimpl* getPimpl (HWND h) { return (Pimpl*) GetWindowLongPtr (h, GWLP_USERDATA); }
- -
- - static LRESULT wndProc (HWND h, UINT msg, WPARAM wp, LPARAM lp)
- - {
- - switch (msg)
- - {
- - case WM_NCCREATE: enableNonClientDPIScaling (h); break;
- - case WM_SIZE: if (auto w = getPimpl (h)) w->handleSizeChange(); break;
- - case WM_CLOSE: if (auto w = getPimpl (h)) w->handleClose(); return 0;
- - case WM_GETMINMAXINFO: if (auto w = getPimpl (h)) w->getMinMaxInfo (*(LPMINMAXINFO) lp); return 0;
- - default: break;
- - }
- -
- - return DefWindowProcW (h, msg, wp, lp);
- - }
- -};
- -
- -} // namespace choc::ui
- -
- -#else
- - #error "choc DesktopWindow only supports OSX, Windows or Linux!"
- -#endif
- -
- -namespace choc::ui
- -{
- -
- -//==============================================================================
- -inline DesktopWindow::DesktopWindow (Bounds b) { pimpl = std::make_unique<Pimpl> (*this, b); }
- -inline DesktopWindow::~DesktopWindow() {}
- -
- -inline void* DesktopWindow::getWindowHandle() const { return pimpl->getWindowHandle(); }
- -inline void DesktopWindow::setContent (void* view) { pimpl->setContent (view); }
- -inline void DesktopWindow::setVisible (bool visible) { pimpl->setVisible (visible); }
- -inline void DesktopWindow::setWindowTitle (const std::string& title) { pimpl->setWindowTitle (title); }
- -inline void DesktopWindow::setMinimumSize (int w, int h) { pimpl->setMinimumSize (w, h); }
- -inline void DesktopWindow::setMaximumSize (int w, int h) { pimpl->setMaximumSize (w, h); }
- -inline void DesktopWindow::setResizable (bool b) { pimpl->setResizable (b); }
- -inline void DesktopWindow::setClosable (bool b) { pimpl->setClosable (b); }
- -inline void DesktopWindow::setBounds (Bounds b) { pimpl->setBounds (b); }
- -inline void DesktopWindow::centreWithSize (int w, int h) { pimpl->centreWithSize (w, h); }
- -inline void DesktopWindow::toFront() { pimpl->toFront(); }
- -
- -} // namespace choc::ui
- +END_NAMESPACE_DISTRHO
-
- #endif // CHOC_DESKTOPWINDOW_HEADER_INCLUDED
- diff --git a/choc_DynamicLibrary.h b/choc_DynamicLibrary.h
- index 85b2cfd..ef8c282 100644
- --- a/choc_DynamicLibrary.h
- +++ b/choc_DynamicLibrary.h
- @@ -21,8 +21,7 @@
-
- #include <string>
-
- -namespace choc::file
- -{
- +START_NAMESPACE_DISTRHO
-
- //==============================================================================
- /**
- @@ -87,38 +86,8 @@ inline DynamicLibrary& DynamicLibrary::operator= (DynamicLibrary&& other)
- return *this;
- }
-
- -} // namespace choc::file
- -
- -#if ! (defined (_WIN32) || defined (_WIN64))
- -
- -#include <dlfcn.h>
- -
- -inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library)
- -{
- - handle = ::dlopen (std::string (library).c_str(), RTLD_LOCAL | RTLD_NOW);
- -}
- -
- -inline void choc::file::DynamicLibrary::close()
- -{
- - if (handle != nullptr)
- - {
- - ::dlclose (handle);
- - handle = nullptr;
- - }
- -}
- -
- -inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
- -{
- - if (handle != nullptr)
- - return ::dlsym (handle, std::string (name).c_str());
- -
- - return {};
- -}
- -
- -#else
- -
- //==============================================================================
- -namespace choc::win32_defs
- +namespace win32_defs
- {
- #if ! (defined (_WINDOWS_) || defined (_APISETLIBLOADER_)) // only use these local definitions if windows.h isn't already included
- using CHOC_HMODULE = void*;
- @@ -145,12 +114,12 @@ namespace choc::win32_defs
- #endif
- }
-
- -inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library)
- +inline DynamicLibrary::DynamicLibrary (std::string_view library)
- {
- handle = (void*) win32_defs::LoadLibraryA (std::string (library).c_str());
- }
-
- -inline void choc::file::DynamicLibrary::close()
- +inline void DynamicLibrary::close()
- {
- if (handle != nullptr)
- {
- @@ -159,7 +128,7 @@ inline void choc::file::DynamicLibrary::close()
- }
- }
-
- -inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
- +inline void* DynamicLibrary::findFunction (std::string_view name)
- {
- if (handle != nullptr)
- return (void*) win32_defs::GetProcAddress ((win32_defs::CHOC_HMODULE) handle, std::string (name).c_str());
- @@ -167,6 +136,6 @@ inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
- return {};
- }
-
- -#endif
- +END_NAMESPACE_DISTRHO
-
- #endif // CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED
- diff --git a/choc_FloatToString.h b/choc_FloatToString.h
- index bf89933..586ecca 100644
- --- a/choc_FloatToString.h
- +++ b/choc_FloatToString.h
- @@ -21,7 +21,7 @@
-
- #include <cstring>
- #include <string>
- -#include "../math/choc_MathHelpers.h"
- +#include "choc_MathHelpers.h"
-
- namespace choc::text
- {
- diff --git a/choc_JSON.h b/choc_JSON.h
- index dbea379..617e2af 100644
- --- a/choc_JSON.h
- +++ b/choc_JSON.h
- @@ -26,7 +26,7 @@
-
- #include "choc_UTF8.h"
- #include "choc_FloatToString.h"
- -#include "../containers/choc_Value.h"
- +#include "choc_Value.h"
-
- #undef max // It's never a smart idea to include any C headers before your C++ ones, as it
- #undef min // risks polluting your namespace with all kinds of dangerous macros like these ones.
- diff --git a/choc_MemoryDLL.h b/choc_MemoryDLL.h
- index 00c3caa..f60eea1 100644
- --- a/choc_MemoryDLL.h
- +++ b/choc_MemoryDLL.h
- @@ -23,8 +23,7 @@
- #include <memory>
- #include <string>
-
- -namespace choc::memory
- -{
- +START_NAMESPACE_DISTRHO
-
- /**
- MemoryDLL is an egregious hack that allows you to load DLL files from a chunk
- @@ -62,7 +61,7 @@ private:
- std::unique_ptr<Pimpl> pimpl;
- };
-
- -} // namespace choc::memory
- +END_NAMESPACE_DISTRHO
-
-
-
- @@ -77,8 +76,6 @@ private:
- //
- //==============================================================================
-
- -#if defined (_WIN32) || defined (_WIN64)
- -
- #include <vector>
- #include <unordered_map>
- #undef WIN32_LEAN_AND_MEAN
- @@ -89,8 +86,7 @@ private:
- #include <windows.h>
- #undef Rectangle
-
- -namespace choc::memory
- -{
- +START_NAMESPACE_DISTRHO
-
- struct MemoryDLL::Pimpl
- {
- @@ -497,20 +493,6 @@ private:
- }
- };
-
- -#else
- -
- -#include "choc_Assert.h"
- -
- -namespace choc::memory
- -{
- -struct MemoryDLL::Pimpl
- -{
- - bool initialise (const void*, size_t) { CHOC_ASSERT (false); return {}; } // Only available on Windows!
- - void* findFunction (std::string_view) { CHOC_ASSERT (false); return {}; } // Only available on Windows!
- -};
- -
- -#endif
- -
- inline MemoryDLL::~MemoryDLL() = default;
-
- inline MemoryDLL::MemoryDLL (const void* data, size_t size) : pimpl (std::make_unique<Pimpl>())
- @@ -524,6 +506,6 @@ inline void* MemoryDLL::findFunction (std::string_view name)
- return pimpl != nullptr ? pimpl->findFunction (name) : nullptr;
- }
-
- -} // namespace choc::memory
- +END_NAMESPACE_DISTRHO
-
- #endif // CHOC_MEMORYDLL_HEADER_INCLUDED
- diff --git a/choc_StringUtilities.h b/choc_StringUtilities.h
- index 3122f83..4cd54c6 100644
- --- a/choc_StringUtilities.h
- +++ b/choc_StringUtilities.h
- @@ -27,125 +27,14 @@
- #include <memory>
- #include <algorithm>
- #include <cwctype>
- -#include "../platform/choc_Assert.h"
-
- -namespace choc::text
- -{
- +START_NAMESPACE_DISTRHO
-
- //==============================================================================
- -inline bool isWhitespace (char c) { return c == ' ' || (c <= 13 && c >= 9); }
- -inline bool isDigit (char c) { return static_cast<uint32_t> (c - '0') < 10; }
- -
- -/// Replaces all occurrences of a one or more substrings.
- -/// The arguments must be a sequence of pairs of strings, where the first of each pair is the string to
- -/// look for, followed by its replacement.
- -template <typename StringType, typename... OtherReplacements>
- -std::string replace (StringType textToSearch,
- - std::string_view firstSubstringToReplace, std::string_view firstReplacement,
- - OtherReplacements&&... otherPairsOfStringsToReplace);
- -
- -/// Returns a string with any whitespace trimmed from its start and end.
- -std::string trim (std::string textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its start and end.
- -std::string_view trim (std::string_view textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its start and end.
- -std::string_view trim (const char* textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its start.
- -std::string trimStart (std::string textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its start.
- -std::string_view trimStart (std::string_view textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its start.
- -std::string_view trimStart (const char* textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its end.
- -std::string trimEnd (std::string textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its end.
- -std::string_view trimEnd (std::string_view textToTrim);
- -
- -/// Returns a string with any whitespace trimmed from its end.
- -std::string_view trimEnd (const char* textToTrim);
- -
- -/// If the string begins with one or more instances of the given character, this
- -/// skips past them, returning the remainder of the string.
- -std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip);
- -
- -/// If the given character is at the start and end of the string, it trims it away.
- -std::string removeOuterCharacter (std::string text, char outerChar);
- -
- -inline std::string removeDoubleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '"'); }
- -inline std::string removeSingleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '\''); }
- -
- -inline std::string addDoubleQuotes (std::string text) { return "\"" + std::move (text) + "\""; }
- -inline std::string addSingleQuotes (std::string text) { return "'" + std::move (text) + "'"; }
- -
- -std::string toLowerCase (std::string);
- -std::string toUpperCase (std::string);
- -
- -template <typename IsDelimiterChar>
- -std::vector<std::string> splitString (std::string_view textToSplit,
- - IsDelimiterChar&& isDelimiterChar,
- - bool includeDelimitersInResult);
- -
- -template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
- -std::vector<std::string> splitString (std::string_view textToSplit,
- - CharStartsDelimiter&& isDelimiterStart,
- - CharIsInDelimiterBody&& isDelimiterBody,
- - bool includeDelimitersInResult);
- -
- -std::vector<std::string> splitString (std::string_view textToSplit,
- - char delimiterCharacter,
- - bool includeDelimitersInResult);
- -
- -std::vector<std::string> splitAtWhitespace (std::string_view text, bool keepDelimiters = false);
- -
- -/// Splits a string at newline characters, returning an array of strings.
- -std::vector<std::string> splitIntoLines (std::string_view text, bool includeNewLinesInResult);
- -
- -/// Joins some kind of array of strings into a single string, adding the given separator
- -/// between them (but not adding it at the start or end)
- -template <typename ArrayOfStrings>
- -std::string joinStrings (const ArrayOfStrings& strings, std::string_view separator);
- -
- -/// Returns true if this text contains the given sub-string.
- -bool contains (std::string_view text, std::string_view possibleSubstring);
- -/// Returns true if this text starts with the given character.
- -bool startsWith (std::string_view text, char possibleStart);
- -/// Returns true if this text starts with the given sub-string.
- -bool startsWith (std::string_view text, std::string_view possibleStart);
- -/// Returns true if this text ends with the given sub-string.
- -bool endsWith (std::string_view text, char possibleEnd);
- -/// Returns true if this text ends with the given sub-string.
- -bool endsWith (std::string_view text, std::string_view possibleEnd);
- -
- -/// Calculates the Levenstein distance between two strings.
- -template <typename StringType>
- -size_t getLevenshteinDistance (const StringType& string1,
- - const StringType& string2);
- -
- -/// Converts a hex character to a number 0-15, or -1 if it's not a valid hex digit.
- -int hexDigitToInt (uint32_t unicodeChar);
- -
- /// Returns a hex string for the given value.
- /// If the minimum number of digits is non-zero, it will be zero-padded to fill this length;
- template <typename IntegerType>
- -std::string createHexString (IntegerType value, int minNumDigits = 0);
- -
- -/// Returns a truncated, easy-to-read version of a time as hours, seconds or milliseconds,
- -/// depending on its magnitude. The use-cases include things like logging or console app output.
- -std::string getDurationDescription (std::chrono::duration<double, std::micro>);
- -
- -/// Returns an easy-to-read description of a size in bytes. Depending on the magnitude,
- -/// it might choose different units such as GB, MB, KB or just bytes.
- -std::string getByteSizeDescription (uint64_t sizeInBytes);
- -
- -/// Encodes a string as a legal URI, using percent-encoding (aka URL encoding)
- -std::string percentEncodeURI (std::string_view text);
- +std::string createHexString (IntegerType value);
-
-
- //==============================================================================
- @@ -159,20 +48,11 @@ std::string percentEncodeURI (std::string_view text);
- //
- //==============================================================================
-
- -inline int hexDigitToInt (uint32_t c)
- -{
- - auto d1 = c - static_cast<uint32_t> ('0'); if (d1 < 10u) return static_cast<int> (d1);
- - auto d2 = d1 + static_cast<uint32_t> ('0' - 'a'); if (d2 < 6u) return static_cast<int> (d2 + 10);
- - auto d3 = d2 + static_cast<uint32_t> ('a' - 'A'); if (d3 < 6u) return static_cast<int> (d3 + 10);
- - return -1;
- -}
- -
- template <typename IntegerType>
- -std::string createHexString (IntegerType v, int minNumDigits)
- +std::string createHexString (IntegerType v)
- {
- static_assert (std::is_integral<IntegerType>::value, "Need to pass integers into this method");
- auto value = static_cast<typename std::make_unsigned<IntegerType>::type> (v);
- - CHOC_ASSERT (minNumDigits <= 32);
-
- char hex[40];
- const auto end = hex + sizeof (hex) - 1;
- @@ -183,415 +63,12 @@ std::string createHexString (IntegerType v, int minNumDigits)
- {
- *--d = "0123456789abcdef"[static_cast<uint32_t> (value) & 15u];
- value = static_cast<decltype (value)> (value >> 4);
- - --minNumDigits;
-
- - if (value == 0 && minNumDigits <= 0)
- + if (value == 0)
- return std::string (d, end);
- }
- }
-
- -template <typename StringType, typename... OtherReplacements>
- -std::string replace (StringType textToSearch, std::string_view firstToReplace, std::string_view firstReplacement,
- - OtherReplacements&&... otherPairsOfStringsToReplace)
- -{
- - static_assert ((sizeof... (otherPairsOfStringsToReplace) & 1u) == 0,
- - "This function expects a list of pairs of strings as its arguments");
- -
- - if constexpr (std::is_same<const StringType, const std::string_view>::value || std::is_same<const StringType, const char* const>::value)
- - {
- - return replace (std::string (textToSearch), firstToReplace, firstReplacement,
- - std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
- - }
- - else if constexpr (sizeof... (otherPairsOfStringsToReplace) == 0)
- - {
- - size_t pos = 0;
- -
- - for (;;)
- - {
- - pos = textToSearch.find (firstToReplace, pos);
- -
- - if (pos == std::string::npos)
- - return textToSearch;
- -
- - textToSearch.replace (pos, firstToReplace.length(), firstReplacement);
- - pos += firstReplacement.length();
- - }
- - }
- - else
- - {
- - return replace (replace (std::move (textToSearch), firstToReplace, firstReplacement),
- - std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
- - }
- -}
- -
- -inline std::string trim (std::string text) { return trimStart (trimEnd (std::move (text))); }
- -inline std::string_view trim (std::string_view text) { return trimStart (trimEnd (std::move (text))); }
- -
- -inline std::string_view trim (const char* text) { return trim (std::string_view (text)); }
- -inline std::string_view trimStart (const char* text) { return trimStart (std::string_view (text)); }
- -inline std::string_view trimEnd (const char* text) { return trimEnd (std::string_view (text)); }
- -
- -inline std::string trimStart (std::string text)
- -{
- - auto i = text.begin();
- -
- - if (i == text.end()) return {};
- - if (! isWhitespace (*i)) return text;
- -
- - for (;;)
- - {
- - ++i;
- -
- - if (i == text.end()) return {};
- - if (! isWhitespace (*i)) return { i, text.end() };
- - }
- -}
- -
- -inline std::string_view trimStart (std::string_view text)
- -{
- - size_t i = 0;
- -
- - for (auto c : text)
- - {
- - if (! isWhitespace (c))
- - {
- - text.remove_prefix (i);
- - return text;
- - }
- -
- - ++i;
- - }
- -
- - return {};
- -}
- -
- -inline std::string trimEnd (std::string text)
- -{
- - for (auto i = text.end();;)
- - {
- - if (i == text.begin())
- - return {};
- -
- - --i;
- -
- - if (! isWhitespace (*i))
- - {
- - text.erase (i + 1, text.end());
- - return text;
- - }
- - }
- -}
- -
- -inline std::string_view trimEnd (std::string_view text)
- -{
- - for (auto i = text.length(); i != 0; --i)
- - if (! isWhitespace (text[i - 1]))
- - return text.substr (0, i);
- -
- - return {};
- -}
- -
- -inline std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip)
- -{
- - for (size_t i = 0; i < textToTrim.length(); ++i)
- - if (textToTrim[i] != characterToSkip)
- - return textToTrim.substr (i);
- -
- - return {};
- -}
- -
- -inline std::string removeOuterCharacter (std::string t, char outerChar)
- -{
- - if (t.length() >= 2 && t.front() == outerChar && t.back() == outerChar)
- - return t.substr (1, t.length() - 2);
- -
- - return t;
- -}
- -
- -inline std::string toLowerCase (std::string s)
- -{
- - std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::tolower (static_cast<unsigned char> (c))); });
- - return s;
- -}
- -
- -inline std::string toUpperCase (std::string s)
- -{
- - std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::toupper (static_cast<unsigned char> (c))); });
- - return s;
- -}
- -
- -template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
- -std::vector<std::string> splitString (std::string_view source,
- - CharStartsDelimiter&& isDelimiterStart,
- - CharIsInDelimiterBody&& isDelimiterBody,
- - bool keepDelimiters)
- -{
- - std::vector<std::string> tokens;
- - auto tokenStart = source.begin();
- - auto pos = tokenStart;
- -
- - while (pos != source.end())
- - {
- - if (isDelimiterStart (*pos))
- - {
- - auto delimiterStart = pos++;
- -
- - while (pos != source.end() && isDelimiterBody (*pos))
- - ++pos;
- -
- - if (pos != source.begin())
- - tokens.push_back ({ tokenStart, keepDelimiters ? pos : delimiterStart });
- -
- - tokenStart = pos;
- - }
- - else
- - {
- - ++pos;
- - }
- - }
- -
- - if (pos != source.begin())
- - tokens.push_back ({ tokenStart, pos });
- -
- - return tokens;
- -}
- -
- -template <typename IsDelimiterChar>
- -std::vector<std::string> splitString (std::string_view source, IsDelimiterChar&& isDelimiterChar, bool keepDelimiters)
- -{
- - std::vector<std::string> tokens;
- - auto tokenStart = source.begin();
- - auto pos = tokenStart;
- -
- - while (pos != source.end())
- - {
- - if (isDelimiterChar (*pos))
- - {
- - tokens.push_back ({ tokenStart, keepDelimiters ? pos + 1 : pos });
- - tokenStart = ++pos;
- - }
- - else
- - {
- - ++pos;
- - }
- - }
- -
- - if (pos != source.begin())
- - tokens.push_back ({ tokenStart, pos });
- -
- - return tokens;
- -}
- -
- -inline std::vector<std::string> splitString (std::string_view text, char delimiterCharacter, bool keepDelimiters)
- -{
- - return splitString (text, [=] (char c) { return c == delimiterCharacter; }, keepDelimiters);
- -}
- -
- -inline std::vector<std::string> splitAtWhitespace (std::string_view text, bool keepDelimiters)
- -{
- - return splitString (text,
- - [] (char c) { return isWhitespace (c); },
- - [] (char c) { return isWhitespace (c); },
- - keepDelimiters);
- -}
- -
- -inline std::vector<std::string> splitIntoLines (std::string_view text, bool includeNewLinesInResult)
- -{
- - return splitString (text, '\n', includeNewLinesInResult);
- -}
- -
- -template <typename ArrayOfStrings>
- -inline std::string joinStrings (const ArrayOfStrings& strings, std::string_view sep)
- -{
- - if (strings.empty())
- - return {};
- -
- - auto spaceNeeded = sep.length() * strings.size();
- -
- - for (auto& s : strings)
- - spaceNeeded += s.length();
- -
- - std::string result (strings.front());
- - result.reserve (spaceNeeded);
- -
- - for (size_t i = 1; i < strings.size(); ++i)
- - {
- - result += sep;
- - result += strings[i];
- - }
- -
- - return result;
- -}
- -
- -inline bool contains (std::string_view t, std::string_view s) { return t.find (s) != std::string::npos; }
- -inline bool startsWith (std::string_view t, char s) { return ! t.empty() && t.front() == s; }
- -inline bool endsWith (std::string_view t, char s) { return ! t.empty() && t.back() == s; }
- -
- -inline bool startsWith (std::string_view t, std::string_view s)
- -{
- - auto len = s.length();
- - return t.length() >= len && t.substr (0, len) == s;
- -}
- -
- -inline bool endsWith (std::string_view t, std::string_view s)
- -{
- - auto len1 = t.length(), len2 = s.length();
- - return len1 >= len2 && t.substr (len1 - len2) == s;
- -}
- -
- -inline std::string getDurationDescription (std::chrono::duration<double, std::micro> d)
- -{
- - auto microseconds = std::chrono::duration_cast<std::chrono::microseconds> (d).count();
- -
- - if (microseconds < 0) return "-" + getDurationDescription (-d);
- - if (microseconds == 0) return "0 sec";
- -
- - std::string result;
- -
- - auto addLevel = [&] (int64_t size, std::string_view units, int64_t decimalScale, int64_t modulo) -> bool
- - {
- - if (microseconds < size)
- - return false;
- -
- - if (! result.empty())
- - result += ' ';
- -
- - auto scaled = (microseconds * decimalScale + size / 2) / size;
- - auto whole = scaled / decimalScale;
- -
- - if (modulo != 0)
- - whole = whole % modulo;
- -
- - result += std::to_string (whole);
- -
- - if (auto fraction = scaled % decimalScale)
- - {
- - result += '.';
- - result += static_cast<char> ('0' + (fraction / 10));
- -
- - if (fraction % 10 != 0)
- - result += static_cast<char> ('0' + (fraction % 10));
- - }
- -
- - result += (whole == 1 && units.length() > 3 && units.back() == 's') ? units.substr (0, units.length() - 1) : units;
- - return true;
- - };
- -
- - bool hours = addLevel (60000000ll * 60ll, " hours", 1, 0);
- - bool mins = addLevel (60000000ll, " min", 1, hours ? 60 : 0);
- -
- - if (hours)
- - return result;
- -
- - if (mins)
- - {
- - addLevel (1000000, " sec", 1, 60);
- - }
- - else
- - {
- - if (! addLevel (1000000, " sec", 100, 0))
- - if (! addLevel (1000, " ms", 100, 0))
- - addLevel (1, " microseconds", 100, 0);
- - }
- -
- - return result;
- -}
- -
- -template <typename StringType>
- -size_t getLevenshteinDistance (const StringType& string1, const StringType& string2)
- -{
- - if (string1.empty()) return string2.length();
- - if (string2.empty()) return string1.length();
- -
- - auto calculate = [] (size_t* costs, size_t numCosts, const StringType& s1, const StringType& s2) -> size_t
- - {
- - for (size_t i = 0; i < numCosts; ++i)
- - costs[i] = i;
- -
- - size_t p1 = 0;
- -
- - for (auto c1 : s1)
- - {
- - auto corner = p1;
- - *costs = p1 + 1;
- - size_t p2 = 0;
- -
- - for (auto c2 : s2)
- - {
- - auto upper = costs[p2 + 1];
- - costs[p2 + 1] = c1 == c2 ? corner : (std::min (costs[p2], std::min (upper, corner)) + 1);
- - ++p2;
- - corner = upper;
- - }
- -
- - ++p1;
- - }
- -
- - return costs[numCosts - 1];
- - };
- -
- - auto sizeNeeded = string2.length() + 1;
- - constexpr size_t maxStackSize = 96;
- -
- - if (sizeNeeded <= maxStackSize)
- - {
- - size_t costs[maxStackSize];
- - return calculate (costs, sizeNeeded, string1, string2);
- - }
- -
- - std::unique_ptr<size_t[]> costs (new size_t[sizeNeeded]);
- - return calculate (costs.get(), sizeNeeded, string1, string2);
- -}
- -
- -inline std::string getByteSizeDescription (uint64_t size)
- -{
- - auto intToStringWith1DecPlace = [] (uint64_t n, uint64_t divisor) -> std::string
- - {
- - auto scaled = (n * 10 + divisor / 2) / divisor;
- - auto result = std::to_string (scaled / 10);
- -
- - if (auto fraction = scaled % 10)
- - {
- - result += '.';
- - result += static_cast<char> ('0' + fraction);
- - }
- -
- - return result;
- - };
- -
- - static constexpr uint64_t maxValue = std::numeric_limits<uint64_t>::max() / 10;
- -
- - if (size >= 0x40000000) return intToStringWith1DecPlace (std::min (maxValue, size), 0x40000000) + " GB";
- - if (size >= 0x100000) return intToStringWith1DecPlace (size, 0x100000) + " MB";
- - if (size >= 0x400) return intToStringWith1DecPlace (size, 0x400) + " KB";
- - if (size != 1) return std::to_string (size) + " bytes";
- -
- - return "1 byte";
- -}
- -
- -inline std::string percentEncodeURI (std::string_view text)
- -{
- - std::string result;
- - result.reserve (text.length());
- -
- - for (auto c : text)
- - {
- - if (std::string_view ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.~").find (c) != std::string_view::npos)
- - {
- - result += c;
- - }
- - else
- - {
- - result += '%';
- - result += "0123456789abcdef"[static_cast<uint8_t> (c) >> 4];
- - result += "0123456789abcdef"[static_cast<uint8_t> (c) & 15u];
- - }
- - }
- -
- - return result;
- -}
- -
- -
- -} // namespace choc::text
- +END_NAMESPACE_DISTRHO
-
- #endif
- diff --git a/choc_Value.h b/choc_Value.h
- index bd27f7a..5df22c4 100644
- --- a/choc_Value.h
- +++ b/choc_Value.h
- @@ -25,7 +25,7 @@
- #include <algorithm>
- #include <memory>
- #include <exception>
- -#include "../platform/choc_Assert.h"
- +#include "choc_Assert.h"
-
- namespace choc::value
- {
- diff --git a/choc_WebView.h b/choc_WebView.h
- index 3ddf610..935dce1 100644
- --- a/choc_WebView.h
- +++ b/choc_WebView.h
- @@ -23,46 +23,18 @@
- #include <unordered_map>
- #include <vector>
- #include <functional>
- -#include "../platform/choc_Platform.h"
- -#include "../text/choc_JSON.h"
- +#include <memory>
- +#include <string>
- +#include "choc_Platform.h"
- +#include "choc_StringUtilities.h"
-
- -//==============================================================================
- -namespace choc::ui
- -{
- +START_NAMESPACE_DISTRHO
-
- /**
- Creates an embedded browser which can be placed inside some kind of parent window.
-
- After creating a WebView object, its getViewHandle() returns a platform-specific
- - handle that can be added to whatever kind of window is appropriate. The
- - choc::ui::DesktopWindow class is an example of a window that can have the
- - webview added to it via its choc::ui::DesktopWindow::setContent() method.
- -
- - There are unfortunately a few extra build steps needed for using WebView
- - in your projects:
- -
- - - On OSX, you'll need to add the `WebKit` framework to your project
- -
- - - On Linux, you'll need to:
- - 1. Install the libgtk-3-dev and libwebkit2gtk-4.0-dev packages.
- - 2. Link the gtk+3.0 and webkit2gtk-4.0 libraries in your build.
- - You might want to have a look inside choc/tests/CMakeLists.txt for
- - an example of how to add those packages to your build without too
- - much fuss.
- -
- - - On Windows, no extra build steps needed!! This is a bigger deal than it
- - sounds, because normally to use an embedded Edge browser on Windows is a
- - total PITA, involving downloading a special Microsoft SDK with redistributable
- - DLLs, etc, but I've jumped through many ugly hoops to make this class
- - fully self-contained.
- -
- - Because this is a GUI, it needs a message loop to be running. If you're using
- - it inside an app which already runs a message loop, it should just work,
- - or you can use choc::messageloop::run() and choc::messageloop::stop() for an easy
- - but basic loop.
- -
- - For an example of how to use this class, see `choc/tests/main.cpp` where
- - there's a simple demo.
- + handle that can be added to whatever kind of window is appropriate.
- */
- class WebView
- {
- @@ -82,34 +54,6 @@ public:
- // this empty for default behaviour.
- std::string customUserAgent;
-
- - /// If you provide a fetchResource function, it is expected to return this
- - /// object, which is simply the raw content of the resource, and its MIME type.
- - struct Resource
- - {
- - Resource() = default;
- - Resource (std::string_view content, std::string mimeType);
- -
- - std::vector<uint8_t> data;
- - std::string mimeType;
- - };
- -
- - using FetchResource = std::function<std::optional<Resource>(const std::string& path)>;
- -
- - /// Serve resources to the browser from a C++ callback function.
- - /// This can effectively be used to implement a basic web server,
- - /// serving resources to the browser in any way the client code chooses.
- - /// Given the path URL component (i.e. starting from "/"), the client should
- - /// return some bytes, and the associated MIME type, for that resource.
- - /// When provided, this function will initially be called with the root path
- - /// ("/") in order to serve the initial content for the page (or if the
- - /// customSchemeURI property is also set, it will navigate to that URI).
- - /// When this happens, the client should return some HTML with a "text/html"
- - /// MIME type.
- - /// Subsequent relative requests made from the page (e.g. via `img` tags,
- - /// `fetch` calls from javascript etc) will all invoke calls to this callback
- - /// with the requested path as their argument.
- - FetchResource fetchResource;
- -
- /// If fetchResource is being used to serve custom data, you can choose to
- /// override the default URI scheme by providing a home URI here, e.g. if
- /// you wanted a scheme called `foo:`, you might set this to `foo://myname.com`
- @@ -132,31 +76,18 @@ public:
- /// fail on some systems if the OS doesn't provide a suitable component.
- bool loadedOK() const;
-
- - /// Directly sets the HTML content of the browser
- - bool setHTML (const std::string& html);
- -
- - /// This function type is used by evaluateJavascript().
- - using CompletionHandler = std::function<void(const std::string& error,
- - const choc::value::ValueView& result)>;
- -
- /// Asynchronously evaluates some javascript.
- - /// If you want to find out the result of the expression (or whether there
- - /// was a compile error etc), then provide a callback function which will be
- - /// invoked when the script is complete.
- /// This will return true if the webview is in a state that lets it run code, or
- /// false if something prevents that.
- - bool evaluateJavascript (const std::string& script, CompletionHandler completionHandler = {});
- + bool evaluateJavascript (const std::string& script);
-
- /// Sends the browser to this URL
- bool navigate (const std::string& url);
-
- /// A callback function which can be passed to bind().
- - using CallbackFn = std::function<choc::value::Value(const choc::value::ValueView& args)>;
- - /// Binds a C++ function to a named javascript function that can be called
- - /// by code running in the browser.
- - bool bind (const std::string& functionName, CallbackFn&& function);
- - /// Removes a previously-bound function.
- - bool unbind (const std::string& functionName);
- + using CallbackFn = std::function<void(const std::string& msg)>;
- + /// Set the C++ callback function.
- + bool bind (CallbackFn&& function);
-
- /// Adds a script to run when the browser loads a page
- bool addInitScript (const std::string& script);
- @@ -169,14 +100,11 @@ private:
- struct Pimpl;
- std::unique_ptr<Pimpl> pimpl;
-
- - std::unordered_map<std::string, CallbackFn> bindings;
- + CallbackFn binding {};
- void invokeBinding (const std::string&);
- - static std::string getURIScheme (const Options&);
- - static std::string getURIHome (const Options&);
- - struct DeletionChecker { bool deleted = false; };
- };
-
- -} // namespace choc::ui
- +END_NAMESPACE_DISTRHO
-
-
- //==============================================================================
- @@ -190,737 +118,14 @@ private:
- //
- //==============================================================================
-
- -#if CHOC_LINUX
- -
- -#include "../platform/choc_DisableAllWarnings.h"
- -#include <JavaScriptCore/JavaScript.h>
- -#include <webkit2/webkit2.h>
- -#include "../platform/choc_ReenableAllWarnings.h"
- -
- -struct choc::ui::WebView::Pimpl
- -{
- - Pimpl (WebView& v, const Options& options)
- - : owner (v), fetchResource (options.fetchResource)
- - {
- - if (! gtk_init_check (nullptr, nullptr))
- - return;
- -
- - defaultURI = getURIHome (options);
- - webviewContext = webkit_web_context_new();
- - g_object_ref_sink (G_OBJECT (webviewContext));
- - webview = webkit_web_view_new_with_context (webviewContext);
- - g_object_ref_sink (G_OBJECT (webview));
- - manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (webview));
- -
- - signalHandlerID = g_signal_connect (manager, "script-message-received::external",
- - G_CALLBACK (+[] (WebKitUserContentManager*, WebKitJavascriptResult* r, gpointer arg)
- - {
- - static_cast<Pimpl*> (arg)->invokeCallback (r);
- - }),
- - this);
- -
- - webkit_user_content_manager_register_script_message_handler (manager, "external");
- -
- - WebKitSettings* settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (webview));
- - webkit_settings_set_javascript_can_access_clipboard (settings, true);
- -
- - if (options.enableDebugMode)
- - {
- - webkit_settings_set_enable_write_console_messages_to_stdout (settings, true);
- - webkit_settings_set_enable_developer_extras (settings, true);
- -
- - if (auto inspector = WEBKIT_WEB_INSPECTOR (webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (webview))))
- - webkit_web_inspector_show (inspector);
- - }
- -
- - if (! options.customUserAgent.empty())
- - webkit_settings_set_user_agent (settings, options.customUserAgent.c_str());
- -
- - if (options.fetchResource)
- - {
- - const auto onResourceRequested = [] (auto* request, auto* context)
- - {
- - try
- - {
- - const auto* path = webkit_uri_scheme_request_get_path (request);
- -
- - if (const auto resource = static_cast<Pimpl*> (context)->fetchResource (path))
- - {
- - const auto& [bytes, mimeType] = *resource;
- -
- - auto* streamBytes = g_bytes_new (bytes.data(), static_cast<gsize> (bytes.size()));
- - auto* stream = g_memory_input_stream_new_from_bytes (streamBytes);
- - g_bytes_unref (streamBytes);
- -
- - auto* response = webkit_uri_scheme_response_new (stream, static_cast<gint64> (bytes.size()));
- - webkit_uri_scheme_response_set_status (response, 200, nullptr);
- - webkit_uri_scheme_response_set_content_type (response, mimeType.c_str());
- -
- - auto* headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
- - soup_message_headers_append (headers, "Cache-Control", "no-store");
- - soup_message_headers_append (headers, "Access-Control-Allow-Origin", "*");
- - webkit_uri_scheme_response_set_http_headers (response, headers); // response takes ownership of the headers
- -
- - webkit_uri_scheme_request_finish_with_response (request, response);
- -
- - g_object_unref (stream);
- - g_object_unref (response);
- - }
- - else
- - {
- - auto* stream = g_memory_input_stream_new();
- - auto* response = webkit_uri_scheme_response_new (stream, -1);
- - webkit_uri_scheme_response_set_status (response, 404, nullptr);
- -
- - webkit_uri_scheme_request_finish_with_response (request, response);
- -
- - g_object_unref (stream);
- - g_object_unref (response);
- - }
- - }
- - catch (...)
- - {
- - auto* error = g_error_new (WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_FAILED, "Something went wrong");
- - webkit_uri_scheme_request_finish_error (request, error);
- - g_error_free (error);
- - }
- - };
- -
- - webkit_web_context_register_uri_scheme (webviewContext, getURIScheme (options).c_str(), onResourceRequested, this, nullptr);
- - navigate ({});
- - }
- -
- - gtk_widget_show_all (webview);
- - }
- -
- - ~Pimpl()
- - {
- - deletionChecker->deleted = true;
- -
- - if (signalHandlerID != 0 && webview != nullptr)
- - g_signal_handler_disconnect (manager, signalHandlerID);
- -
- - g_clear_object (&webview);
- - g_clear_object (&webviewContext);
- - }
- -
- - static constexpr const char* postMessageFn = "window.webkit.messageHandlers.external.postMessage";
- -
- - bool loadedOK() const { return getViewHandle() != nullptr; }
- - void* getViewHandle() const { return (void*) webview; }
- -
- - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
- -
- - bool evaluateJavascript (const std::string& js, CompletionHandler&& completionHandler)
- - {
- - GAsyncReadyCallback callback = {};
- - gpointer userData = {};
- -
- - if (completionHandler)
- - {
- - callback = evaluationCompleteCallback;
- - userData = new CompletionHandler (std::move (completionHandler));
- - }
- -
- - #if WEBKIT_CHECK_VERSION (2, 40, 0)
- - webkit_web_view_evaluate_javascript (WEBKIT_WEB_VIEW (webview), js.c_str(), static_cast<gssize> (js.length()),
- - nullptr, nullptr, nullptr, callback, userData);
- - #else
- - webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (webview), js.c_str(), nullptr, callback, userData);
- - #endif
- - return true;
- - }
- -
- - static void evaluationCompleteCallback (GObject* object, GAsyncResult* result, gpointer userData)
- - {
- - std::unique_ptr<CompletionHandler> completionHandler (reinterpret_cast<CompletionHandler*> (userData));
- - choc::value::Value value;
- - std::string errorMessage;
- - GError* error = {};
- -
- - #if WEBKIT_CHECK_VERSION (2, 40, 0)
- - if (auto js_result = webkit_web_view_evaluate_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error))
- - #else
- - if (auto js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error))
- - #endif
- - {
- - if (error != nullptr)
- - {
- - errorMessage = error->message;
- - g_error_free (error);
- - }
- -
- - #if WEBKIT_CHECK_VERSION (2, 40, 0)
- - if (auto js_value = js_result)
- - #else
- - if (auto js_value = webkit_javascript_result_get_js_value (js_result))
- - #endif
- - {
- - if (auto json = jsc_value_to_json (js_value, 0))
- - {
- - try
- - {
- - auto jsonView = std::string_view (json);
- -
- - if (! jsonView.empty())
- - value = choc::json::parseValue (jsonView);
- - }
- - catch (const std::exception& e)
- - {
- - if (errorMessage.empty())
- - errorMessage = e.what();
- - }
- -
- - g_free (json);
- - }
- - else
- - {
- - if (errorMessage.empty())
- - errorMessage = "Failed to convert result to JSON";
- - }
- -
- - #if WEBKIT_CHECK_VERSION (2, 40, 0)
- - g_object_unref (js_value);
- - #endif
- - }
- - else
- - {
- - if (errorMessage.empty())
- - errorMessage = "Failed to fetch result";
- - }
- -
- - #if ! WEBKIT_CHECK_VERSION (2, 40, 0)
- - webkit_javascript_result_unref (js_result);
- - #endif
- - }
- - else
- - {
- - errorMessage = "Failed to fetch result";
- - }
- -
- - (*completionHandler) (errorMessage, value);
- - }
- -
- - bool navigate (const std::string& url)
- - {
- - if (url.empty())
- - return navigate (defaultURI);
- -
- - webkit_web_view_load_uri (WEBKIT_WEB_VIEW (webview), url.c_str());
- - return true;
- - }
- -
- - bool setHTML (const std::string& html)
- - {
- - webkit_web_view_load_html (WEBKIT_WEB_VIEW (webview), html.c_str(), nullptr);
- - return true;
- - }
- -
- - bool addInitScript (const std::string& js)
- - {
- - if (manager != nullptr)
- - {
- - webkit_user_content_manager_add_script (manager, webkit_user_script_new (js.c_str(),
- - WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
- - WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
- - nullptr, nullptr));
- - return true;
- - }
- -
- - return false;
- - }
- -
- - void invokeCallback (WebKitJavascriptResult* r)
- - {
- - auto s = jsc_value_to_string (webkit_javascript_result_get_js_value (r));
- - owner.invokeBinding (s);
- - g_free (s);
- - }
- -
- - WebView& owner;
- - Options::FetchResource fetchResource;
- - WebKitWebContext* webviewContext = {};
- - GtkWidget* webview = {};
- - WebKitUserContentManager* manager = {};
- - std::string defaultURI;
- - unsigned long signalHandlerID = 0;
- -};
- -
- -//==============================================================================
- -#elif CHOC_APPLE
- -
- -#include "choc_MessageLoop.h"
- -
- -struct choc::ui::WebView::Pimpl
- -{
- - Pimpl (WebView& v, const Options& optionsToUse)
- - : owner (v), options (std::make_unique<Options> (optionsToUse))
- - {
- - using namespace choc::objc;
- - CHOC_AUTORELEASE_BEGIN
- -
- - defaultURI = getURIHome (*options);
- -
- - id config = call<id> (getClass ("WKWebViewConfiguration"), "new");
- -
- - id prefs = call<id> (config, "preferences");
- - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("fullScreenEnabled"));
- - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("DOMPasteAllowed"));
- - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("javaScriptCanAccessClipboard"));
- -
- - if (options->enableDebugMode)
- - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("developerExtrasEnabled"));
- -
- - delegate = createDelegate();
- - objc_setAssociatedObject (delegate, "choc_webview", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
- -
- - manager = call<id> (config, "userContentController");
- - call<void> (manager, "retain");
- - call<void> (manager, "addScriptMessageHandler:name:", delegate, getNSString ("external"));
- -
- - if (options->fetchResource)
- - call<void> (config, "setURLSchemeHandler:forURLScheme:", delegate, getNSString (getURIScheme (*options)));
- -
- - webview = call<id> (allocateWebview(), "initWithFrame:configuration:", CGRect(), config);
- - objc_setAssociatedObject (webview, "choc_webview", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
- -
- - if (! options->customUserAgent.empty())
- - call<void> (webview, "setValue:forKey:", getNSString (options->customUserAgent), getNSString ("customUserAgent"));
- -
- - call<void> (webview, "setUIDelegate:", delegate);
- - call<void> (webview, "setNavigationDelegate:", delegate);
- -
- - call<void> (config, "release");
- -
- - if (options->fetchResource)
- - navigate ({});
- -
- - CHOC_AUTORELEASE_END
- - }
- -
- - ~Pimpl()
- - {
- - CHOC_AUTORELEASE_BEGIN
- - deletionChecker->deleted = true;
- - objc_setAssociatedObject (delegate, "choc_webview", nil, OBJC_ASSOCIATION_ASSIGN);
- - objc_setAssociatedObject (webview, "choc_webview", nil, OBJC_ASSOCIATION_ASSIGN);
- - objc::call<void> (webview, "release");
- - webview = {};
- - objc::call<void> (manager, "removeScriptMessageHandlerForName:", objc::getNSString ("external"));
- - objc::call<void> (manager, "release");
- - manager = {};
- - objc::call<void> (delegate, "release");
- - delegate = {};
- - CHOC_AUTORELEASE_END
- - }
- -
- - static constexpr const char* postMessageFn = "window.webkit.messageHandlers.external.postMessage";
- -
- - bool loadedOK() const { return getViewHandle() != nullptr; }
- - void* getViewHandle() const { return (CHOC_OBJC_CAST_BRIDGED void*) webview; }
- -
- - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
- -
- - bool addInitScript (const std::string& script)
- - {
- - using namespace choc::objc;
- - CHOC_AUTORELEASE_BEGIN
- -
- - if (id s = call<id> (call<id> (getClass ("WKUserScript"), "alloc"), "initWithSource:injectionTime:forMainFrameOnly:",
- - getNSString (script), WKUserScriptInjectionTimeAtDocumentStart, (BOOL) 1))
- - {
- - call<void> (manager, "addUserScript:", s);
- - call<void> (s, "release");
- - return true;
- - }
- -
- - CHOC_AUTORELEASE_END
- - return false;
- - }
- -
- - bool navigate (const std::string& url)
- - {
- - if (url.empty())
- - return navigate (defaultURI);
- -
- - using namespace choc::objc;
- - CHOC_AUTORELEASE_BEGIN
- -
- - if (id nsURL = call<id> (getClass ("NSURL"), "URLWithString:", getNSString (url)))
- - return call<id> (webview, "loadRequest:", call<id> (getClass ("NSURLRequest"), "requestWithURL:", nsURL)) != nullptr;
- -
- - CHOC_AUTORELEASE_END
- - return false;
- - }
- -
- - bool setHTML (const std::string& html)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - return objc::call<id> (webview, "loadHTMLString:baseURL:", objc::getNSString (html), (id) nullptr) != nullptr;
- - CHOC_AUTORELEASE_END
- - }
- -
- - bool evaluateJavascript (const std::string& script, CompletionHandler completionHandler)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - auto s = objc::getNSString (script);
- -
- - if (completionHandler)
- - {
- - objc::call<void> (webview, "evaluateJavaScript:completionHandler:", s,
- - ^(id result, id error)
- - {
- - CHOC_AUTORELEASE_BEGIN
- -
- - auto errorMessage = getMessageFromNSError (error);
- - choc::value::Value value;
- -
- - try
- - {
- - if (auto json = convertNSObjectToJSON (result); ! json.empty())
- - value = choc::json::parseValue (json);
- - }
- - catch (const std::exception& e)
- - {
- - errorMessage = e.what();
- - }
- -
- - completionHandler (errorMessage, value);
- - CHOC_AUTORELEASE_END
- - });
- - }
- - else
- - {
- - objc::call<void> (webview, "evaluateJavaScript:completionHandler:", s, (id) nullptr);
- - }
- -
- - return true;
- - CHOC_AUTORELEASE_END
- - }
- -
- - static std::string convertNSObjectToJSON (id value)
- - {
- - if (value)
- - {
- - if (id nsData = objc::call<id> (objc::getClass ("NSJSONSerialization"), "dataWithJSONObject:options:error:",
- - value, 12, (id) nullptr))
- - {
- - auto data = objc::call<void*> (nsData, "bytes");
- - auto length = objc::call<unsigned long> (nsData, "length");
- - return std::string (static_cast<const char*> (data), static_cast<size_t> (length));
- - }
- - }
- -
- - return {};
- - }
- -
- -private:
- - id createDelegate()
- - {
- - static DelegateClass dc;
- - return objc::call<id> ((id) dc.delegateClass, "new");
- - }
- -
- - id allocateWebview()
- - {
- - static WebviewClass c;
- - return objc::call<id> ((id) c.webviewClass, "alloc");
- - }
- -
- - static std::string getMessageFromNSError (id nsError)
- - {
- - if (nsError)
- - {
- - if (id userInfo = objc::call<id> (nsError, "userInfo"))
- - if (id message = objc::call<id> (userInfo, "objectForKey:", objc::getNSString ("WKJavaScriptExceptionMessage")))
- - if (auto s = objc::getString (message); ! s.empty())
- - return s;
- -
- - return objc::getString (objc::call<id> (nsError, "localizedDescription"));
- - }
- -
- - return {};
- - }
- -
- - void onResourceRequested (id task)
- - {
- - using namespace choc::objc;
- - CHOC_AUTORELEASE_BEGIN
- -
- - try
- - {
- - id requestUrl = call<id> (call<id> (task, "request"), "URL");
- -
- - auto makeResponse = [&] (auto responseCode, id headerFields)
- - {
- - return call<id> (call<id> (call<id> (getClass ("NSHTTPURLResponse"), "alloc"),
- - "initWithURL:statusCode:HTTPVersion:headerFields:",
- - requestUrl,
- - responseCode,
- - getNSString ("HTTP/1.1"),
- - headerFields),
- - "autorelease");
- - };
- -
- - auto path = objc::getString (call<id> (requestUrl, "path"));
- -
- - if (auto resource = options->fetchResource (path))
- - {
- - const auto& [bytes, mimeType] = *resource;
- -
- - auto contentLength = std::to_string (bytes.size());
- - id headerKeys[] = { getNSString ("Content-Length"), getNSString ("Content-Type"), getNSString ("Cache-Control"), getNSString ("Access-Control-Allow-Origin") };
- - id headerObjects[] = { getNSString (contentLength), getNSString (mimeType), getNSString ("no-store") , getNSString ("*") };
- -
- - id headerFields = call<id> (getClass ("NSDictionary"), "dictionaryWithObjects:forKeys:count:",
- - headerObjects, headerKeys, sizeof (headerObjects) / sizeof (id));
- -
- - call<void> (task, "didReceiveResponse:", makeResponse (200, headerFields));
- -
- - id data = call<id> (getClass ("NSData"), "dataWithBytes:length:", bytes.data(), bytes.size());
- - call<void> (task, "didReceiveData:", data);
- - }
- - else
- - {
- - call<void> (task, "didReceiveResponse:", makeResponse (404, nullptr));
- - }
- -
- - call<void> (task, "didFinish");
- - }
- - catch (...)
- - {
- - id error = call<id> (getClass ("NSError"), "errorWithDomain:code:userInfo:",
- - getNSString ("NSURLErrorDomain"), -1, nullptr);
- -
- - call<void> (task, "didFailWithError:", error);
- - }
- -
- - CHOC_AUTORELEASE_END
- - }
- -
- - BOOL sendAppAction (id self, const char* action)
- - {
- - objc::call<void> (objc::getSharedNSApplication(), "sendAction:to:from:",
- - sel_registerName (action), (id) nullptr, self);
- - return true;
- - }
- -
- - BOOL performKeyEquivalent (id self, id e)
- - {
- - enum
- - {
- - NSEventTypeKeyDown = 10,
- -
- - NSEventModifierFlagShift = 1 << 17,
- - NSEventModifierFlagControl = 1 << 18,
- - NSEventModifierFlagOption = 1 << 19,
- - NSEventModifierFlagCommand = 1 << 20
- - };
- -
- - if (objc::call<int> (e, "type") == NSEventTypeKeyDown)
- - {
- - auto flags = objc::call<int> (e, "modifierFlags") & (NSEventModifierFlagShift | NSEventModifierFlagCommand
- - | NSEventModifierFlagControl | NSEventModifierFlagOption);
- -
- - auto path = objc::getString (objc::call<id> (e, "charactersIgnoringModifiers"));
- -
- - if (flags == NSEventModifierFlagCommand)
- - {
- - if (path == "c") return sendAppAction (self, "copy:");
- - if (path == "x") return sendAppAction (self, "cut:");
- - if (path == "v") return sendAppAction (self, "paste:");
- - if (path == "z") return sendAppAction (self, "undo:");
- - if (path == "a") return sendAppAction (self, "selectAll:");
- - }
- - else if (flags == (NSEventModifierFlagShift | NSEventModifierFlagCommand))
- - {
- - if (path == "Z") return sendAppAction (self, "redo:");
- - }
- - }
- -
- - return false;
- - }
- -
- - void handleError (id error)
- - {
- - static constexpr int NSURLErrorCancelled = -999;
- -
- - if (objc::call<int> (error, "code") == NSURLErrorCancelled)
- - return;
- -
- - setHTML ("<!DOCTYPE html><html><head><title>Error</title></head>"
- - "<body><h2>" + getMessageFromNSError (error) + "</h2></body></html>");
- - }
- -
- - static Pimpl* getPimpl (id self)
- - {
- - return (CHOC_OBJC_CAST_BRIDGED Pimpl*) (objc_getAssociatedObject (self, "choc_webview"));
- - }
- -
- - WebView& owner;
- - // NB: this is a pointer because making it a member forces the alignment of this Pimpl class
- - // to 16, which then conflicts with obj-C pointer alignment...
- - std::unique_ptr<Options> options;
- - id webview = {}, manager = {}, delegate = {};
- - std::string defaultURI;
- -
- - struct WebviewClass
- - {
- - WebviewClass()
- - {
- - webviewClass = choc::objc::createDelegateClass ("WKWebView", "CHOCWebView_");
- -
- - class_addMethod (webviewClass, sel_registerName ("acceptsFirstMouse:"),
- - (IMP) (+[](id self, SEL, id) -> BOOL
- - {
- - if (auto p = getPimpl (self))
- - return p->options->acceptsFirstMouseClick;
- -
- - return false;
- - }), "B@:@");
- -
- - class_addMethod (webviewClass, sel_registerName ("performKeyEquivalent:"),
- - (IMP) (+[](id self, SEL, id e) -> BOOL
- - {
- - if (auto p = getPimpl (self))
- - return p->performKeyEquivalent (self, e);
- -
- - return false;
- - }), "B@:@");
- -
- - objc_registerClassPair (webviewClass);
- - }
- -
- - ~WebviewClass()
- - {
- - // NB: it doesn't seem possible to dispose of this class late enough to avoid a warning on shutdown
- - // about the KVO system still using it, so unfortunately the only option seems to be to let it leak..
- - // objc_disposeClassPair (webviewClass);
- - }
- -
- - Class webviewClass = {};
- - };
- -
- - struct DelegateClass
- - {
- - DelegateClass()
- - {
- - using namespace choc::objc;
- - delegateClass = createDelegateClass ("NSObject", "CHOCWebViewDelegate_");
- -
- - class_addMethod (delegateClass, sel_registerName ("userContentController:didReceiveScriptMessage:"),
- - (IMP) (+[](id self, SEL, id, id msg)
- - {
- - if (auto p = getPimpl (self))
- - p->owner.invokeBinding (objc::getString (call<id> (msg, "body")));
- - }),
- - "v@:@@");
- -
- - class_addMethod (delegateClass, sel_registerName ("webView:startURLSchemeTask:"),
- - (IMP) (+[](id self, SEL, id, id task)
- - {
- - if (auto p = getPimpl (self))
- - p->onResourceRequested (task);
- - }),
- - "v@:@@");
- -
- - class_addMethod (delegateClass, sel_registerName ("webView:didFailProvisionalNavigation:withError:"),
- - (IMP) (+[](id self, SEL, id, id, id error)
- - {
- - if (auto p = getPimpl (self))
- - p->handleError (error);
- - }),
- - "v@:@@@");
- -
- - class_addMethod (delegateClass, sel_registerName ("webView:didFailNavigation:withError:"),
- - (IMP) (+[](id self, SEL, id, id, id error)
- - {
- - if (auto p = getPimpl (self))
- - p->handleError (error);
- - }),
- - "v@:@@@");
- -
- - class_addMethod (delegateClass, sel_registerName ("webView:stopURLSchemeTask:"), (IMP) (+[](id, SEL, id, id) {}), "v@:@@");
- -
- - class_addMethod (delegateClass, sel_registerName ("webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"),
- - (IMP) (+[](id, SEL, id wkwebview, id params, id /*frame*/, void (^completionHandler)(id))
- - {
- - CHOC_AUTORELEASE_BEGIN
- - id panel = call<id> (getClass ("NSOpenPanel"), "openPanel");
- -
- - auto allowsMultipleSelection = call<BOOL> (params, "allowsMultipleSelection");
- - id allowedFileExtensions = call<id> (params, "_allowedFileExtensions");
- - id window = call<id> (wkwebview, "window");
- -
- - call<void> (panel, "setAllowsMultipleSelection:", allowsMultipleSelection);
- - call<void> (panel, "setAllowedFileTypes:", allowedFileExtensions);
- -
- - call<void> (panel, "beginSheetModalForWindow:completionHandler:", window,
- - ^(long result)
- - {
- - CHOC_AUTORELEASE_BEGIN
- - if (result == 1) // NSModalResponseOK
- - completionHandler (call<id> (panel, "URLs"));
- - else
- - completionHandler (nil);
- - CHOC_AUTORELEASE_END
- - });
- - CHOC_AUTORELEASE_END
- - }), "v@:@@@@");
- -
- - objc_registerClassPair (delegateClass);
- - }
- -
- - ~DelegateClass()
- - {
- - objc_disposeClassPair (delegateClass);
- - }
- -
- - Class delegateClass = {};
- - };
- -
- - static constexpr long WKUserScriptInjectionTimeAtDocumentStart = 0;
- -
- - // Including CodeGraphics.h can create all kinds of messy C/C++ symbol clashes
- - // with other headers, but all we actually need are these coordinate structs:
- - #if defined (__LP64__) && __LP64__
- - using CGFloat = double;
- - #else
- - using CGFloat = float;
- - #endif
- -
- - struct CGPoint { CGFloat x = 0, y = 0; };
- - struct CGSize { CGFloat width = 0, height = 0; };
- - struct CGRect { CGPoint origin; CGSize size; };
- -};
- -
- -//==============================================================================
- -#elif CHOC_WINDOWS
- -
- -#include "../platform/choc_DynamicLibrary.h"
- -
- -// If you want to supply your own mechanism for finding the Microsoft
- -// Webview2Loader.dll file, then define the CHOC_FIND_WEBVIEW2LOADER_DLL
- -// macro, which must evaluate to a choc::file::DynamicLibrary object
- -// which points to the DLL.
- -#ifndef CHOC_FIND_WEBVIEW2LOADER_DLL
- - #define CHOC_USE_INTERNAL_WEBVIEW_DLL 1
- - #define CHOC_FIND_WEBVIEW2LOADER_DLL choc::ui::getWebview2LoaderDLL()
- - #include "../platform/choc_MemoryDLL.h"
- - namespace choc::ui
- - {
- - using WebViewDLL = choc::memory::MemoryDLL;
- - static WebViewDLL getWebview2LoaderDLL();
- - }
- -#else
- - namespace choc::ui
- - {
- - using WebViewDLL = choc::file::DynamicLibrary;
- - }
- -#endif
- -
- +#include "choc_DynamicLibrary.h"
- +#include "choc_MemoryDLL.h"
- #include "choc_DesktopWindow.h"
-
- +START_NAMESPACE_DISTRHO
- +static MemoryDLL getWebview2LoaderDLL();
- +END_NAMESPACE_DISTRHO
- +
- #ifndef __webview2_h__
- #define __webview2_h__
-
- @@ -1268,8 +473,7 @@ public:
-
- #endif // __webview2_h__
-
- -namespace choc::ui
- -{
- +START_NAMESPACE_DISTRHO
-
- //==============================================================================
- struct WebView::Pimpl
- @@ -1277,10 +481,7 @@ struct WebView::Pimpl
- Pimpl (WebView& v, const Options& opts)
- : owner (v), options (opts)
- {
- - // You cam define this macro to provide a custom way of getting a
- - // choc::file::DynamicLibrary that contains the redistributable
- - // Microsoft WebView2Loader.dll
- - webviewDLL = CHOC_FIND_WEBVIEW2LOADER_DLL;
- + webviewDLL = getWebview2LoaderDLL();
-
- if (! webviewDLL)
- return;
- @@ -1290,9 +491,6 @@ struct WebView::Pimpl
- if (hwnd.hwnd == nullptr)
- return;
-
- - defaultURI = getURIHome (options);
- - setHTMLURI = defaultURI + "getHTMLInternal";
- -
- SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) this);
-
- if (createEmbeddedWebView())
- @@ -1304,8 +502,6 @@ struct WebView::Pimpl
-
- ~Pimpl()
- {
- - deletionChecker->deleted = true;
- -
- if (coreWebView != nullptr)
- {
- coreWebView->Release();
- @@ -1327,52 +523,30 @@ struct WebView::Pimpl
- hwnd.reset();
- }
-
- - static constexpr const char* postMessageFn = "window.chrome.webview.postMessage";
- -
- bool loadedOK() const { return coreWebView != nullptr; }
- void* getViewHandle() const { return (void*) hwnd.hwnd; }
-
- - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
- -
- bool navigate (const std::string& url)
- {
- - if (url.empty())
- - return navigate (defaultURI);
- -
- - CHOC_ASSERT (coreWebView != nullptr);
- + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
- return coreWebView->Navigate (createUTF16StringFromUTF8 (url).c_str()) == S_OK;
- }
-
- bool addInitScript (const std::string& script)
- {
- - CHOC_ASSERT (coreWebView != nullptr);
- + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
- return coreWebView->AddScriptToExecuteOnDocumentCreated (createUTF16StringFromUTF8 (script).c_str(), nullptr) == S_OK;
- }
-
- - bool evaluateJavascript (const std::string& script, CompletionHandler&& ch)
- - {
- - if (coreWebView == nullptr)
- - return false;
- -
- - COMPtr<ExecuteScriptCompletedCallback> callback (ch ? new ExecuteScriptCompletedCallback (std::move (ch))
- - : nullptr);
- -
- - return coreWebView->ExecuteScript (createUTF16StringFromUTF8 (script).c_str(), callback) == S_OK;
- - }
- -
- - bool setHTML (const std::string& html)
- + bool evaluateJavascript (const std::string& script)
- {
- - CHOC_ASSERT (coreWebView != nullptr);
- - pageHTML = { html, "text/html" };
- - navigate (setHTMLURI);
- - return true;
- + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
- + return coreWebView->ExecuteScript (createUTF16StringFromUTF8 (script).c_str(), nullptr) == S_OK;
- }
-
- private:
- WindowClass windowClass { L"CHOCWebView", (WNDPROC) wndProc };
- HWNDHolder hwnd;
- - std::string defaultURI, setHTMLURI;
- - WebView::Options::Resource pageHTML;
-
- //==============================================================================
- template <typename Type>
- @@ -1404,7 +578,7 @@ private:
- return message;
- }
-
- - return choc::text::createHexString (hr);
- + return createHexString (hr);
- }
-
- static Pimpl* getPimpl (HWND h) { return (Pimpl*) GetWindowLongPtr (h, GWLP_USERDATA); }
- @@ -1463,15 +637,9 @@ private:
- if (coreWebView == nullptr)
- return false;
-
- - const auto wildcardFilter = createUTF16StringFromUTF8 (defaultURI + "*");
- - coreWebView->AddWebResourceRequestedFilter (wildcardFilter.c_str(), COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
- -
- EventRegistrationToken token;
- coreWebView->add_WebResourceRequested (handler, std::addressof (token));
-
- - if (options.fetchResource)
- - navigate ({});
- -
- ICoreWebView2Settings* settings = nullptr;
-
- if (coreWebView->get_Settings (std::addressof (settings)) == S_OK
- @@ -1526,14 +694,6 @@ private:
- webviewInitialising.clear();
- }
-
- - std::optional<WebView::Options::Resource> fetchResourceOrPageHTML (const std::string& uri)
- - {
- - if (uri == setHTMLURI)
- - return pageHTML;
- -
- - return options.fetchResource (uri.substr (defaultURI.size() - 1));
- - }
- -
- HRESULT onResourceRequested (ICoreWebView2WebResourceRequestedEventArgs* args)
- {
- struct ScopedExit
- @@ -1579,42 +739,8 @@ private:
- ICoreWebView2WebResourceResponse* response = {};
- ScopedExit cleanupResponse (makeCleanupIUnknown (response));
-
- - if (const auto resource = fetchResourceOrPageHTML (createUTF8FromUTF16 (uri)))
- - {
- - const auto makeMemoryStream = [](const auto* data, auto length) -> IStream*
- - {
- - choc::file::DynamicLibrary lib ("shlwapi.dll");
- - using SHCreateMemStreamFn = IStream*(__stdcall *)(const BYTE*, UINT);
- - auto fn = reinterpret_cast<SHCreateMemStreamFn> (lib.findFunction ("SHCreateMemStream"));
- - return fn ? fn (data, length) : nullptr;
- - };
- -
- - auto* stream = makeMemoryStream (reinterpret_cast<const BYTE*> (resource->data.data()),
- - static_cast<UINT> (resource->data.size()));
- -
- - if (stream == nullptr)
- - return E_FAIL;
- -
- - ScopedExit cleanupStream (makeCleanupIUnknown (stream));
- -
- - std::vector<std::string> headers;
- - headers.emplace_back ("Content-Type: " + resource->mimeType);
- - headers.emplace_back ("Cache-Control: no-store");
- - headers.emplace_back ("Access-Control-Allow-Origin: *");
- -
- - if (! options.customUserAgent.empty())
- - headers.emplace_back ("User-Agent: " + options.customUserAgent);
- -
- - const auto headerString = createUTF16StringFromUTF8 (choc::text::joinStrings (headers, "\n"));
- -
- - if (coreWebViewEnvironment->CreateWebResourceResponse (stream, 200, L"OK", headerString.c_str(), std::addressof (response)) != S_OK)
- - return E_FAIL;
- - }
- - else
- - {
- - if (coreWebViewEnvironment->CreateWebResourceResponse (nullptr, 404, L"Not Found", nullptr, std::addressof (response)) != S_OK)
- - return E_FAIL;
- - }
- + if (coreWebViewEnvironment->CreateWebResourceResponse (nullptr, 404, L"Not Found", nullptr, std::addressof (response)) != S_OK)
- + return E_FAIL;
-
- if (args->put_Response (response) != S_OK)
- return E_FAIL;
- @@ -1708,58 +834,9 @@ private:
- std::atomic<ULONG> refCount { 0 };
- };
-
- - //==============================================================================
- - struct ExecuteScriptCompletedCallback : public ICoreWebView2ExecuteScriptCompletedHandler
- - {
- - ExecuteScriptCompletedCallback (CompletionHandler&& cb) : callback (std::move (cb)) {}
- - virtual ~ExecuteScriptCompletedCallback() {}
- -
- - HRESULT STDMETHODCALLTYPE QueryInterface (REFIID refID, void** result) override
- - {
- - if (refID == IID { 0x49511172, 0xcc67, 0x4bca, { 0x99, 0x23, 0x13, 0x71, 0x12, 0xf4, 0xc4, 0xcc } }
- - || refID == IID_IUnknown)
- - {
- - *result = this;
- - AddRef();
- - return S_OK;
- - }
- -
- - *result = nullptr;
- - return E_NOINTERFACE;
- - }
- -
- - ULONG STDMETHODCALLTYPE AddRef() override { return ++refCount; }
- - ULONG STDMETHODCALLTYPE Release() override { auto newCount = --refCount; if (newCount == 0) delete this; return newCount; }
- -
- - HRESULT STDMETHODCALLTYPE Invoke (HRESULT hr, LPCWSTR resultJSON) override
- - {
- - std::string errorMessage = getMessageFromHRESULT (hr);
- - choc::value::Value value;
- -
- - if (resultJSON != nullptr)
- - {
- - try
- - {
- - if (auto json = createUTF8FromUTF16 (std::wstring (resultJSON)); ! json.empty())
- - value = choc::json::parseValue (json);
- - }
- - catch (const std::exception& e)
- - {
- - errorMessage = e.what();
- - }
- - }
- -
- - callback (errorMessage, value);
- - return S_OK;
- - }
- -
- - CompletionHandler callback;
- - std::atomic<ULONG> refCount { 0 };
- - };
- -
- //==============================================================================
- WebView& owner;
- - WebViewDLL webviewDLL;
- + MemoryDLL webviewDLL;
- Options options;
- ICoreWebView2Environment* coreWebViewEnvironment = nullptr;
- ICoreWebView2* coreWebView = nullptr;
- @@ -1793,15 +870,6 @@ private:
- }
- };
-
- -} // namespace choc::ui
- -
- -#else
- - #error "choc WebView only supports OSX, Windows or Linux!"
- -#endif
- -
- -namespace choc::ui
- -{
- -
- //==============================================================================
- inline WebView::WebView() : WebView (Options()) {}
-
- @@ -1820,137 +888,30 @@ inline WebView::~WebView()
-
- inline bool WebView::loadedOK() const { return pimpl != nullptr; }
- inline bool WebView::navigate (const std::string& url) { return pimpl != nullptr && pimpl->navigate (url); }
- -inline bool WebView::setHTML (const std::string& html) { return pimpl != nullptr && pimpl->setHTML (html); }
- inline bool WebView::addInitScript (const std::string& script) { return pimpl != nullptr && pimpl->addInitScript (script); }
-
- -inline bool WebView::evaluateJavascript (const std::string& script, CompletionHandler completionHandler)
- +inline bool WebView::evaluateJavascript (const std::string& script)
- {
- - return pimpl != nullptr && pimpl->evaluateJavascript (script, std::move (completionHandler));
- + return pimpl != nullptr && pimpl->evaluateJavascript (script);
- }
-
- inline void* WebView::getViewHandle() const { return pimpl != nullptr ? pimpl->getViewHandle() : nullptr; }
-
- -inline bool WebView::bind (const std::string& functionName, CallbackFn&& fn)
- +inline bool WebView::bind (CallbackFn&& fn)
- {
- if (pimpl == nullptr)
- return false;
-
- - static constexpr std::string_view scriptTemplate = R"((function() {
- -const fnBinding = window._fnBindings = (window._fnBindings || { messageID: 1 });
- -
- -window["FUNCTION_NAME"] = function()
- -{
- - const messageID = ++fnBinding.messageID;
- - const promise = new Promise((resolve, reject) => { fnBinding[messageID] = { resolve, reject }; });
- -
- - const args = JSON.stringify ({ id: messageID,
- - fn: "FUNCTION_NAME",
- - params: Array.prototype.slice.call (arguments)
- - },
- - (key, value) => typeof value === 'bigint' ? value.toString() : value);
- - INVOKE_BINDING (args);
- - return promise;
- -}
- -})())";
- -
- - auto script = choc::text::replace (scriptTemplate, "FUNCTION_NAME", functionName,
- - "INVOKE_BINDING", Pimpl::postMessageFn);
- -
- - if (addInitScript (script)
- - && evaluateJavascript (script))
- - {
- - bindings[functionName] = std::move (fn);
- - return true;
- - }
- -
- - return false;
- -}
- -
- -inline bool WebView::unbind (const std::string& functionName)
- -{
- - if (auto found = bindings.find (functionName); found != bindings.end())
- - {
- - evaluateJavascript ("delete window[\"" + functionName + "\"];");
- - bindings.erase (found);
- - return true;
- - }
- -
- - return false;
- + binding = std::move (fn);
- + return true;
- }
-
- inline void WebView::invokeBinding (const std::string& msg)
- {
- - try
- - {
- - auto json = choc::json::parse (msg);
- - auto b = bindings.find (std::string (json["fn"].getString()));
- - auto callbackID = json["id"].getWithDefault<int64_t>(0);
- -
- - if (callbackID == 0 || b == bindings.end())
- - return;
- -
- - auto callbackItem = "window._fnBindings[" + std::to_string (callbackID) + "]";
- -
- - try
- - {
- - auto deletionChecker = pimpl->deletionChecker;
- - auto result = b->second (json["params"]);
- -
- - if (deletionChecker->deleted) // in case the WebView was deleted during the callback
- - return;
- -
- - auto call = callbackItem + ".resolve(" + choc::json::toString (result) + "); delete " + callbackItem + ";";
- - evaluateJavascript (call);
- - return;
- - }
- - catch (const std::exception&)
- - {}
- -
- - auto call = callbackItem + ".reject(); delete " + callbackItem + ";";
- - evaluateJavascript (call);
- - }
- - catch (const std::exception&)
- - {}
- -}
- -
- -inline std::string WebView::getURIHome (const Options& options)
- -{
- - if (! options.customSchemeURI.empty())
- - {
- - if (choc::text::endsWith (options.customSchemeURI, "/"))
- - return options.customSchemeURI;
- -
- - return options.customSchemeURI + "/";
- - }
- -
- - #if CHOC_WINDOWS
- - return "https://choc.localhost/";
- - #else
- - return "choc://choc.choc/";
- - #endif
- -}
- -
- -inline std::string WebView::getURIScheme (const Options& options)
- -{
- - auto uri = getURIHome (options);
- - auto colon = uri.find (":");
- - CHOC_ASSERT (colon != std::string::npos && colon != 0); // need to provide a valid URI with a scheme at the start.
- - return uri.substr (0, colon);
- -}
- -
- -inline WebView::Options::Resource::Resource (std::string_view content, std::string mime)
- -{
- - if (! content.empty())
- - {
- - auto src = content.data();
- - data.insert (data.end(), src, src + content.length());
- - }
- -
- - mimeType = std::move (mime);
- + binding(msg);
- }
-
- -} // namespace choc::ui
- -
- +END_NAMESPACE_DISTRHO
-
- //==============================================================================
- //==============================================================================
- @@ -2035,7 +996,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- )")
- #endif
-
- -inline choc::ui::WebViewDLL choc::ui::getWebview2LoaderDLL()
- +inline DISTRHO_NAMESPACE::MemoryDLL DISTRHO_NAMESPACE::getWebview2LoaderDLL()
- {
- // This is version 1.0.2365.46
-
- @@ -5798,7 +4759,7 @@ inline choc::ui::WebViewDLL choc::ui::getWebview2LoaderDLL()
- };
- #endif
-
- - return choc::memory::MemoryDLL (dllData, sizeof (dllData));
- + return DISTRHO_NAMESPACE::MemoryDLL (dllData, sizeof (dllData));
- }
-
- #endif
|