Collection of DPF-based plugins for packaging
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2804 lines
98KB

  1. diff --git a/choc_DesktopWindow.h b/choc_DesktopWindow.h
  2. index b948042..55e7305 100644
  3. --- a/choc_DesktopWindow.h
  4. +++ b/choc_DesktopWindow.h
  5. @@ -19,100 +19,7 @@
  6. #ifndef CHOC_DESKTOPWINDOW_HEADER_INCLUDED
  7. #define CHOC_DESKTOPWINDOW_HEADER_INCLUDED
  8. -#include "../platform/choc_Platform.h"
  9. -
  10. -
  11. -//==============================================================================
  12. -namespace choc::ui
  13. -{
  14. -
  15. -/// Represents the position and size of a DesktopWindow or other UI elements.
  16. -struct Bounds
  17. -{
  18. - int x = 0, y = 0, width = 0, height = 0;
  19. -};
  20. -
  21. -/**
  22. - A very basic desktop window class.
  23. -
  24. - The main use-case for this is as a simple way to host other UI elements
  25. - such as the choc::ui::WebView.
  26. -
  27. - Because this is a GUI, it needs a message loop to be running. If you're using
  28. - it inside an app which already runs a message loop, it should just work,
  29. - or you can use choc::messageloop::run() and choc::messageloop::stop() for an easy
  30. - but basic loop.
  31. -
  32. - Note that on Linux this uses GTK, so to build it you'll need to:
  33. - 1. Install the libgtk-3-dev package.
  34. - 2. Link the gtk+3.0 library in your build.
  35. - You might want to have a look inside choc/tests/CMakeLists.txt for
  36. - an example of how to add this packages to your build without too
  37. - much fuss.
  38. -
  39. - For an example of how to use this class, see `choc/tests/main.cpp` where
  40. - there's a simple demo.
  41. -*/
  42. -struct DesktopWindow
  43. -{
  44. - DesktopWindow (Bounds);
  45. - ~DesktopWindow();
  46. -
  47. - /// Sets the title of the window that the browser is inside
  48. - void setWindowTitle (const std::string& newTitle);
  49. -
  50. - /// Gives the window a child/content view to display.
  51. - /// The pointer being passed in will be a platform-specific native handle,
  52. - /// so a HWND on Windows, an NSView* on OSX, etc.
  53. - void setContent (void* nativeView);
  54. -
  55. - /// Shows or hides the window. It's visible by default when created.
  56. - void setVisible (bool visible);
  57. -
  58. - /// Changes the window's position
  59. - void setBounds (Bounds);
  60. -
  61. - /// Enables/disables user resizing of the window
  62. - void setResizable (bool);
  63. -
  64. - /// Enables/disables the window's close button (if applicable).
  65. - void setClosable (bool);
  66. -
  67. - /// Gives the window a given size and positions it in the middle of the
  68. - /// default monitor
  69. - void centreWithSize (int width, int height);
  70. -
  71. - /// Sets a minimum size below which the user can't shrink the window
  72. - void setMinimumSize (int minWidth, int minHeight);
  73. - /// Sets a maximum size above which the user can't grow the window
  74. - void setMaximumSize (int maxWidth, int maxHeight);
  75. -
  76. - /// Tries to bring this window to the front of the Z-order.
  77. - void toFront();
  78. -
  79. - /// Returns the native OS handle, which may be a HWND on Windows, an
  80. - /// NSWindow* on OSX or a GtkWidget* on linux.
  81. - void* getWindowHandle() const;
  82. -
  83. - /// An optional callback that will be called when the parent window is resized
  84. - std::function<void()> windowResized;
  85. - /// An optional callback that will be called when the parent window is closed
  86. - std::function<void()> windowClosed;
  87. -
  88. -private:
  89. - struct Pimpl;
  90. - std::unique_ptr<Pimpl> pimpl;
  91. -};
  92. -
  93. -//==============================================================================
  94. -/// This Windows-only function turns on high-DPI awareness for the current
  95. -/// process. On other OSes where no equivalent call is needed, this function is
  96. -/// just a stub.
  97. -void setWindowsDPIAwareness();
  98. -
  99. -
  100. -} // namespace choc::ui
  101. -
  102. +#include "choc_Platform.h"
  103. //==============================================================================
  104. // _ _ _ _
  105. @@ -125,326 +32,6 @@ void setWindowsDPIAwareness();
  106. //
  107. //==============================================================================
  108. -#if CHOC_LINUX
  109. -
  110. -struct choc::ui::DesktopWindow::Pimpl
  111. -{
  112. - Pimpl (DesktopWindow& w, Bounds bounds) : owner (w)
  113. - {
  114. - if (! gtk_init_check (nullptr, nullptr))
  115. - return;
  116. -
  117. - window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  118. - g_object_ref_sink (G_OBJECT (window));
  119. -
  120. - destroyHandlerID = g_signal_connect (G_OBJECT (window), "destroy",
  121. - G_CALLBACK (+[](GtkWidget*, gpointer arg)
  122. - {
  123. - static_cast<Pimpl*> (arg)->windowDestroyEvent();
  124. - }),
  125. - this);
  126. - setBounds (bounds);
  127. - setVisible (true);
  128. - }
  129. -
  130. - ~Pimpl()
  131. - {
  132. - if (destroyHandlerID != 0 && window != nullptr)
  133. - g_signal_handler_disconnect (G_OBJECT (window), destroyHandlerID);
  134. -
  135. - g_clear_object (&window);
  136. - }
  137. -
  138. - void windowDestroyEvent()
  139. - {
  140. - g_clear_object (&window);
  141. -
  142. - if (owner.windowClosed != nullptr)
  143. - owner.windowClosed();
  144. - }
  145. -
  146. - void* getWindowHandle() const { return (void*) window; }
  147. -
  148. - void setWindowTitle (const std::string& newTitle)
  149. - {
  150. - gtk_window_set_title (GTK_WINDOW (window), newTitle.c_str());
  151. - }
  152. -
  153. - void setContent (void* view)
  154. - {
  155. - if (content != nullptr)
  156. - gtk_container_remove (GTK_CONTAINER (window), content);
  157. -
  158. - content = GTK_WIDGET (view);
  159. - gtk_container_add (GTK_CONTAINER (window), content);
  160. - gtk_widget_grab_focus (content);
  161. - }
  162. -
  163. - void setVisible (bool visible)
  164. - {
  165. - if (visible)
  166. - gtk_widget_show_all (window);
  167. - else
  168. - gtk_widget_hide (window);
  169. - }
  170. -
  171. - void setResizable (bool b) { gtk_window_set_resizable (GTK_WINDOW (window), b); }
  172. - void setClosable (bool b) { gtk_window_set_deletable (GTK_WINDOW (window), b); }
  173. -
  174. - void setMinimumSize (int w, int h)
  175. - {
  176. - GdkGeometry g;
  177. - g.min_width = w;
  178. - g.min_height = h;
  179. - gtk_window_set_geometry_hints (GTK_WINDOW (window), nullptr, &g, GDK_HINT_MIN_SIZE);
  180. - }
  181. -
  182. - void setMaximumSize (int w, int h)
  183. - {
  184. - GdkGeometry g;
  185. - g.max_width = w;
  186. - g.max_height = h;
  187. - gtk_window_set_geometry_hints (GTK_WINDOW (window), nullptr, &g, GDK_HINT_MAX_SIZE);
  188. - }
  189. -
  190. - void setBounds (Bounds b)
  191. - {
  192. - setSize (b.width, b.height);
  193. - gtk_window_move (GTK_WINDOW (window), b.x, b.y);
  194. - }
  195. -
  196. - void setSize (int w, int h)
  197. - {
  198. - if (gtk_window_get_resizable (GTK_WINDOW (window)))
  199. - gtk_window_resize (GTK_WINDOW (window), w, h);
  200. - else
  201. - gtk_widget_set_size_request (window, w, h);
  202. - }
  203. -
  204. - void centreWithSize (int w, int h)
  205. - {
  206. - setSize (w, h);
  207. - gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
  208. - }
  209. -
  210. - void toFront()
  211. - {
  212. - gtk_window_activate_default (GTK_WINDOW (window));
  213. - }
  214. -
  215. - DesktopWindow& owner;
  216. - GtkWidget* window = {};
  217. - GtkWidget* content = {};
  218. - unsigned long destroyHandlerID = 0;
  219. -};
  220. -
  221. -inline void choc::ui::setWindowsDPIAwareness() {}
  222. -
  223. -//==============================================================================
  224. -#elif CHOC_APPLE
  225. -
  226. -#include "choc_MessageLoop.h"
  227. -
  228. -namespace choc::ui
  229. -{
  230. -
  231. -namespace macos_ui_helpers
  232. -{
  233. - // Including CodeGraphics.h can create all kinds of messy C/C++ symbol clashes
  234. - // with other headers, but all we actually need are these coordinate structs:
  235. - #if defined (__LP64__) && __LP64__
  236. - using CGFloat = double;
  237. - #else
  238. - using CGFloat = float;
  239. - #endif
  240. -
  241. - struct CGPoint { CGFloat x = 0, y = 0; };
  242. - struct CGSize { CGFloat width = 0, height = 0; };
  243. - struct CGRect { CGPoint origin; CGSize size; };
  244. -
  245. - inline CGSize createCGSize (double w, double h) { return { (CGFloat) w, (CGFloat) h }; }
  246. - inline CGRect createCGRect (choc::ui::Bounds b) { return { { (CGFloat) b.x, (CGFloat) b.y }, { (CGFloat) b.width, (CGFloat) b.height } }; }
  247. -
  248. - static constexpr long NSWindowStyleMaskTitled = 1;
  249. - static constexpr long NSWindowStyleMaskMiniaturizable = 4;
  250. - static constexpr long NSWindowStyleMaskResizable = 8;
  251. - static constexpr long NSWindowStyleMaskClosable = 2;
  252. - static constexpr long NSBackingStoreBuffered = 2;
  253. - static constexpr long NSApplicationActivationPolicyRegular = 0;
  254. -}
  255. -
  256. -using namespace macos_ui_helpers;
  257. -
  258. -inline void setWindowsDPIAwareness() {}
  259. -
  260. -struct DesktopWindow::Pimpl
  261. -{
  262. - Pimpl (DesktopWindow& w, Bounds bounds) : owner (w)
  263. - {
  264. - using namespace choc::objc;
  265. - CHOC_AUTORELEASE_BEGIN
  266. - call<void> (getSharedNSApplication(), "setActivationPolicy:", NSApplicationActivationPolicyRegular);
  267. -
  268. - window = call<id> (call<id> (getClass ("NSWindow"), "alloc"),
  269. - "initWithContentRect:styleMask:backing:defer:",
  270. - createCGRect (bounds),
  271. - NSWindowStyleMaskTitled, NSBackingStoreBuffered, (int) 0);
  272. -
  273. - delegate = createDelegate();
  274. - setStyleBit (NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable, true);
  275. - objc_setAssociatedObject (delegate, "choc_window", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
  276. - call<void> (window, "setDelegate:", delegate);
  277. - CHOC_AUTORELEASE_END
  278. - }
  279. -
  280. - ~Pimpl()
  281. - {
  282. - CHOC_AUTORELEASE_BEGIN
  283. - objc::call<void> (window, "setDelegate:", nullptr);
  284. - objc::call<void> (window, "close");
  285. - objc::call<void> (delegate, "release");
  286. - CHOC_AUTORELEASE_END
  287. - }
  288. -
  289. - void* getWindowHandle() const { return (CHOC_OBJC_CAST_BRIDGED void*) window; }
  290. -
  291. - void setWindowTitle (const std::string& newTitle)
  292. - {
  293. - CHOC_AUTORELEASE_BEGIN
  294. - objc::call<void> (window, "setTitle:", objc::getNSString (newTitle));
  295. - CHOC_AUTORELEASE_END
  296. - }
  297. -
  298. - void setContent (void* view)
  299. - {
  300. - CHOC_AUTORELEASE_BEGIN
  301. - objc::call<void> (window, "setContentView:", (CHOC_OBJC_CAST_BRIDGED id) view);
  302. - CHOC_AUTORELEASE_END
  303. - }
  304. -
  305. - void setVisible (bool visible)
  306. - {
  307. - CHOC_AUTORELEASE_BEGIN
  308. - objc::call<void> (window, "setIsVisible:", (BOOL) visible);
  309. - CHOC_AUTORELEASE_END
  310. - }
  311. -
  312. - void setStyleBit (long bit, bool shouldEnable)
  313. - {
  314. - CHOC_AUTORELEASE_BEGIN
  315. - auto style = objc::call<unsigned long> (window, "styleMask");
  316. - style = shouldEnable ? (style | (unsigned long) bit) : (style & ~(unsigned long) bit);
  317. - objc::call<void> (window, "setStyleMask:", style);
  318. - CHOC_AUTORELEASE_END
  319. - }
  320. -
  321. - void setResizable (bool b) { setStyleBit (NSWindowStyleMaskResizable, b); }
  322. - void setClosable (bool b) { setStyleBit (NSWindowStyleMaskClosable, b); }
  323. -
  324. - void setMinimumSize (int w, int h) { CHOC_AUTORELEASE_BEGIN objc::call<void> (window, "setContentMinSize:", createCGSize (w, h)); CHOC_AUTORELEASE_END }
  325. - void setMaximumSize (int w, int h) { CHOC_AUTORELEASE_BEGIN objc::call<void> (window, "setContentMaxSize:", createCGSize (w, h)); CHOC_AUTORELEASE_END }
  326. -
  327. - CGRect getFrameRectForContent (Bounds b)
  328. - {
  329. - return objc::call<CGRect> (window, "frameRectForContentRect:", createCGRect (b));
  330. - }
  331. -
  332. - void centreWithSize (int w, int h)
  333. - {
  334. - CHOC_AUTORELEASE_BEGIN
  335. - objc::call<void> (window, "setFrame:display:animate:", getFrameRectForContent ({ 0, 0, w, h }), (BOOL) 1, (BOOL) 0);
  336. - objc::call<void> (window, "center");
  337. - CHOC_AUTORELEASE_END
  338. - }
  339. -
  340. - void setBounds (Bounds b)
  341. - {
  342. - CHOC_AUTORELEASE_BEGIN
  343. - objc::call<void> (window, "setFrame:display:animate:", getFrameRectForContent (b), (BOOL) 1, (BOOL) 0);
  344. - CHOC_AUTORELEASE_END
  345. - }
  346. -
  347. - void toFront()
  348. - {
  349. - CHOC_AUTORELEASE_BEGIN
  350. - objc::call<void> (objc::getSharedNSApplication(), "activateIgnoringOtherApps:", (BOOL) 1);
  351. - objc::call<void> (window, "makeKeyAndOrderFront:", (id) nullptr);
  352. - CHOC_AUTORELEASE_END
  353. - }
  354. -
  355. - static Pimpl& getPimplFromContext (id self)
  356. - {
  357. - auto view = (CHOC_OBJC_CAST_BRIDGED Pimpl*) objc_getAssociatedObject (self, "choc_window");
  358. - CHOC_ASSERT (view != nullptr);
  359. - return *view;
  360. - }
  361. -
  362. - id createDelegate()
  363. - {
  364. - static DelegateClass dc;
  365. - return objc::call<id> ((id) dc.delegateClass, "new");
  366. - }
  367. -
  368. - DesktopWindow& owner;
  369. - id window = {}, delegate = {};
  370. -
  371. - struct DelegateClass
  372. - {
  373. - DelegateClass()
  374. - {
  375. - delegateClass = choc::objc::createDelegateClass ("NSResponder", "CHOCDesktopWindowDelegate_");
  376. -
  377. - if (auto* p = objc_getProtocol ("NSWindowDelegate"))
  378. - class_addProtocol (delegateClass, p);
  379. -
  380. - class_addMethod (delegateClass, sel_registerName ("windowShouldClose:"),
  381. - (IMP) (+[](id self, SEL, id) -> BOOL
  382. - {
  383. - CHOC_AUTORELEASE_BEGIN
  384. - auto& p = getPimplFromContext (self);
  385. - p.window = {};
  386. -
  387. - if (auto callback = p.owner.windowClosed)
  388. - choc::messageloop::postMessage ([callback] { callback(); });
  389. -
  390. - CHOC_AUTORELEASE_END
  391. - return TRUE;
  392. - }),
  393. - "c@:@");
  394. -
  395. - class_addMethod (delegateClass, sel_registerName ("windowDidResize:"),
  396. - (IMP) (+[](id self, SEL, id)
  397. - {
  398. - CHOC_AUTORELEASE_BEGIN
  399. -
  400. - if (auto callback = getPimplFromContext (self).owner.windowResized)
  401. - callback();
  402. -
  403. - CHOC_AUTORELEASE_END
  404. - }),
  405. - "v@:@");
  406. -
  407. - class_addMethod (delegateClass, sel_registerName ("applicationShouldTerminateAfterLastWindowClosed:"),
  408. - (IMP) (+[](id, SEL, id) -> BOOL { return 0; }),
  409. - "c@:@");
  410. -
  411. - objc_registerClassPair (delegateClass);
  412. - }
  413. -
  414. - ~DelegateClass()
  415. - {
  416. - objc_disposeClassPair (delegateClass);
  417. - }
  418. -
  419. - Class delegateClass = {};
  420. - };
  421. -};
  422. -
  423. -} // namespace choc::ui
  424. -
  425. -//==============================================================================
  426. -#elif CHOC_WINDOWS
  427. -
  428. #undef WIN32_LEAN_AND_MEAN
  429. #define WIN32_LEAN_AND_MEAN
  430. #undef NOMINMAX
  431. @@ -453,27 +40,7 @@ struct DesktopWindow::Pimpl
  432. #include <windows.h>
  433. #undef Rectangle
  434. -namespace choc::ui
  435. -{
  436. -
  437. -static RECT boundsToRect (Bounds b)
  438. -{
  439. - RECT r;
  440. - r.left = b.x;
  441. - r.top = b.y;
  442. - r.right = b.x + b.width;
  443. - r.bottom = b.y + b.height;
  444. - return r;
  445. -}
  446. -
  447. -template <typename FunctionType>
  448. -FunctionType getUser32Function (const char* name)
  449. -{
  450. - if (auto user32 = choc::file::DynamicLibrary ("user32.dll"))
  451. - return reinterpret_cast<FunctionType> (user32.findFunction (name));
  452. -
  453. - return {};
  454. -}
  455. +START_NAMESPACE_DISTRHO
  456. struct HWNDHolder
  457. {
  458. @@ -516,7 +83,7 @@ struct WindowClass
  459. wc.lpfnWndProc = wndProc;
  460. classAtom = (LPCWSTR) (uintptr_t) RegisterClassExW (&wc);
  461. - CHOC_ASSERT (classAtom != 0);
  462. + DISTRHO_SAFE_ASSERT (classAtom != 0);
  463. }
  464. ~WindowClass()
  465. @@ -582,250 +149,6 @@ static std::wstring createUTF16StringFromUTF8 (std::string_view utf8)
  466. return {};
  467. }
  468. -inline void setWindowsDPIAwareness()
  469. -{
  470. - if (auto setProcessDPIAwarenessContext = getUser32Function<int(__stdcall *)(void*)> ("SetProcessDpiAwarenessContext"))
  471. - setProcessDPIAwarenessContext (/*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ (void*) -4);
  472. -}
  473. -
  474. -//==============================================================================
  475. -struct DesktopWindow::Pimpl
  476. -{
  477. - Pimpl (DesktopWindow& w, Bounds b) : owner (w)
  478. - {
  479. - hwnd = windowClass.createWindow (WS_OVERLAPPEDWINDOW, 640, 480, this);
  480. -
  481. - if (hwnd.hwnd == nullptr)
  482. - return;
  483. -
  484. - setBounds (b);
  485. - ShowWindow (hwnd, SW_SHOW);
  486. - UpdateWindow (hwnd);
  487. - SetFocus (hwnd);
  488. - }
  489. -
  490. - ~Pimpl()
  491. - {
  492. - hwnd.reset();
  493. - }
  494. -
  495. - void* getWindowHandle() const { return hwnd; }
  496. -
  497. - void setWindowTitle (const std::string& newTitle)
  498. - {
  499. - SetWindowTextW (hwnd, createUTF16StringFromUTF8 (newTitle).c_str());
  500. - }
  501. -
  502. - void setContent (void* childHandle)
  503. - {
  504. - if (auto child = getFirstChildWindow())
  505. - {
  506. - ShowWindow (child, SW_HIDE);
  507. - SetParent (child, nullptr);
  508. - }
  509. -
  510. - auto child = (HWND) childHandle;
  511. - auto flags = GetWindowLongPtr (child, -16);
  512. - flags = (flags & ~(decltype (flags)) WS_POPUP) | (decltype (flags)) WS_CHILD;
  513. - SetWindowLongPtr (child, -16, flags);
  514. -
  515. - SetParent (child, hwnd);
  516. - resizeContentToFit();
  517. - ShowWindow (child, IsWindowVisible (hwnd) ? SW_SHOW : SW_HIDE);
  518. - }
  519. -
  520. - void setVisible (bool visible)
  521. - {
  522. - ShowWindow (hwnd, visible ? SW_SHOW : SW_HIDE);
  523. -
  524. - if (visible)
  525. - InvalidateRect (hwnd, nullptr, 0);
  526. - }
  527. -
  528. - void setResizable (bool b)
  529. - {
  530. - auto style = GetWindowLong (hwnd, GWL_STYLE);
  531. -
  532. - if (b)
  533. - style |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
  534. - else
  535. - style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
  536. -
  537. - SetWindowLong (hwnd, GWL_STYLE, style);
  538. - }
  539. -
  540. - void setClosable (bool closable)
  541. - {
  542. - EnableMenuItem (GetSystemMenu (hwnd, FALSE), SC_CLOSE,
  543. - closable ? (MF_BYCOMMAND | MF_ENABLED)
  544. - : (MF_BYCOMMAND | MF_DISABLED | MF_GRAYED));
  545. - }
  546. -
  547. - void setMinimumSize (int w, int h)
  548. - {
  549. - minimumSize.x = w;
  550. - minimumSize.y = h;
  551. - }
  552. -
  553. - void setMaximumSize (int w, int h)
  554. - {
  555. - maximumSize.x = w;
  556. - maximumSize.y = h;
  557. - }
  558. -
  559. - void getMinMaxInfo (MINMAXINFO& m) const
  560. - {
  561. - if (maximumSize.x > 0 && maximumSize.y > 0)
  562. - {
  563. - m.ptMaxSize = maximumSize;
  564. - m.ptMaxTrackSize = maximumSize;
  565. - }
  566. -
  567. - if (minimumSize.x > 0 && minimumSize.y > 0)
  568. - m.ptMinTrackSize = minimumSize;
  569. - }
  570. -
  571. - void centreWithSize (int w, int h)
  572. - {
  573. - auto dpi = static_cast<int> (getWindowDPI());
  574. - auto screenW = (GetSystemMetrics(SM_CXSCREEN) * 96) / dpi;
  575. - auto screenH = (GetSystemMetrics(SM_CYSCREEN) * 96) / dpi;
  576. - auto x = (screenW - w) / 2;
  577. - auto y = (screenH - h) / 2;
  578. - setBounds ({ x, y, w, h }, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  579. - }
  580. -
  581. - void setBounds (Bounds b)
  582. - {
  583. - setBounds (b, SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
  584. - }
  585. -
  586. - void setBounds (Bounds b, DWORD flags)
  587. - {
  588. - auto r = boundsToRect (scaleBounds (b, getWindowDPI() / 96.0));
  589. - AdjustWindowRect (&r, WS_OVERLAPPEDWINDOW, 0);
  590. - SetWindowPos (hwnd, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top, flags);
  591. - resizeContentToFit();
  592. - }
  593. -
  594. - void toFront()
  595. - {
  596. - BringWindowToTop (hwnd);
  597. - }
  598. -
  599. -private:
  600. - DesktopWindow& owner;
  601. - HWNDHolder hwnd;
  602. - POINT minimumSize = {}, maximumSize = {};
  603. - WindowClass windowClass { L"CHOCWindow", (WNDPROC) wndProc };
  604. -
  605. - Bounds scaleBounds (Bounds b, double scale)
  606. - {
  607. - b.x = static_cast<decltype(b.x)> (b.x * scale);
  608. - b.y = static_cast<decltype(b.y)> (b.y * scale);
  609. - b.width = static_cast<decltype(b.width)> (b.width * scale);
  610. - b.height = static_cast<decltype(b.height)> (b.height * scale);
  611. -
  612. - return b;
  613. - }
  614. -
  615. - HWND getFirstChildWindow()
  616. - {
  617. - HWND result = {};
  618. -
  619. - if (IsWindow (hwnd))
  620. - {
  621. - EnumChildWindows (hwnd, +[](HWND w, LPARAM context)
  622. - {
  623. - *reinterpret_cast<HWND*> (context) = w;
  624. - return FALSE;
  625. - }, (LPARAM) &result);
  626. - }
  627. -
  628. - return result;
  629. - }
  630. -
  631. - void resizeContentToFit()
  632. - {
  633. - if (auto child = getFirstChildWindow())
  634. - {
  635. - RECT r;
  636. - GetClientRect (hwnd, &r);
  637. - SetWindowPos (child, nullptr, r.left, r.top, r.right - r.left, r.bottom - r.top,
  638. - SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE | SWP_FRAMECHANGED);
  639. - }
  640. - }
  641. -
  642. - void handleClose()
  643. - {
  644. - if (owner.windowClosed != nullptr)
  645. - owner.windowClosed();
  646. - }
  647. -
  648. - void handleSizeChange()
  649. - {
  650. - resizeContentToFit();
  651. -
  652. - if (owner.windowResized != nullptr)
  653. - owner.windowResized();
  654. - }
  655. -
  656. - static void enableNonClientDPIScaling (HWND h)
  657. - {
  658. - if (auto fn = getUser32Function<BOOL(__stdcall*)(HWND)> ("EnableNonClientDpiScaling"))
  659. - fn (h);
  660. - }
  661. -
  662. - uint32_t getWindowDPI() const
  663. - {
  664. - if (auto getDpiForWindow = getUser32Function<UINT(__stdcall*)(HWND)> ("GetDpiForWindow"))
  665. - return getDpiForWindow (hwnd);
  666. -
  667. - return 96;
  668. - }
  669. -
  670. - static Pimpl* getPimpl (HWND h) { return (Pimpl*) GetWindowLongPtr (h, GWLP_USERDATA); }
  671. -
  672. - static LRESULT wndProc (HWND h, UINT msg, WPARAM wp, LPARAM lp)
  673. - {
  674. - switch (msg)
  675. - {
  676. - case WM_NCCREATE: enableNonClientDPIScaling (h); break;
  677. - case WM_SIZE: if (auto w = getPimpl (h)) w->handleSizeChange(); break;
  678. - case WM_CLOSE: if (auto w = getPimpl (h)) w->handleClose(); return 0;
  679. - case WM_GETMINMAXINFO: if (auto w = getPimpl (h)) w->getMinMaxInfo (*(LPMINMAXINFO) lp); return 0;
  680. - default: break;
  681. - }
  682. -
  683. - return DefWindowProcW (h, msg, wp, lp);
  684. - }
  685. -};
  686. -
  687. -} // namespace choc::ui
  688. -
  689. -#else
  690. - #error "choc DesktopWindow only supports OSX, Windows or Linux!"
  691. -#endif
  692. -
  693. -namespace choc::ui
  694. -{
  695. -
  696. -//==============================================================================
  697. -inline DesktopWindow::DesktopWindow (Bounds b) { pimpl = std::make_unique<Pimpl> (*this, b); }
  698. -inline DesktopWindow::~DesktopWindow() {}
  699. -
  700. -inline void* DesktopWindow::getWindowHandle() const { return pimpl->getWindowHandle(); }
  701. -inline void DesktopWindow::setContent (void* view) { pimpl->setContent (view); }
  702. -inline void DesktopWindow::setVisible (bool visible) { pimpl->setVisible (visible); }
  703. -inline void DesktopWindow::setWindowTitle (const std::string& title) { pimpl->setWindowTitle (title); }
  704. -inline void DesktopWindow::setMinimumSize (int w, int h) { pimpl->setMinimumSize (w, h); }
  705. -inline void DesktopWindow::setMaximumSize (int w, int h) { pimpl->setMaximumSize (w, h); }
  706. -inline void DesktopWindow::setResizable (bool b) { pimpl->setResizable (b); }
  707. -inline void DesktopWindow::setClosable (bool b) { pimpl->setClosable (b); }
  708. -inline void DesktopWindow::setBounds (Bounds b) { pimpl->setBounds (b); }
  709. -inline void DesktopWindow::centreWithSize (int w, int h) { pimpl->centreWithSize (w, h); }
  710. -inline void DesktopWindow::toFront() { pimpl->toFront(); }
  711. -
  712. -} // namespace choc::ui
  713. +END_NAMESPACE_DISTRHO
  714. #endif // CHOC_DESKTOPWINDOW_HEADER_INCLUDED
  715. diff --git a/choc_DynamicLibrary.h b/choc_DynamicLibrary.h
  716. index 85b2cfd..ef8c282 100644
  717. --- a/choc_DynamicLibrary.h
  718. +++ b/choc_DynamicLibrary.h
  719. @@ -21,8 +21,7 @@
  720. #include <string>
  721. -namespace choc::file
  722. -{
  723. +START_NAMESPACE_DISTRHO
  724. //==============================================================================
  725. /**
  726. @@ -87,38 +86,8 @@ inline DynamicLibrary& DynamicLibrary::operator= (DynamicLibrary&& other)
  727. return *this;
  728. }
  729. -} // namespace choc::file
  730. -
  731. -#if ! (defined (_WIN32) || defined (_WIN64))
  732. -
  733. -#include <dlfcn.h>
  734. -
  735. -inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library)
  736. -{
  737. - handle = ::dlopen (std::string (library).c_str(), RTLD_LOCAL | RTLD_NOW);
  738. -}
  739. -
  740. -inline void choc::file::DynamicLibrary::close()
  741. -{
  742. - if (handle != nullptr)
  743. - {
  744. - ::dlclose (handle);
  745. - handle = nullptr;
  746. - }
  747. -}
  748. -
  749. -inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
  750. -{
  751. - if (handle != nullptr)
  752. - return ::dlsym (handle, std::string (name).c_str());
  753. -
  754. - return {};
  755. -}
  756. -
  757. -#else
  758. -
  759. //==============================================================================
  760. -namespace choc::win32_defs
  761. +namespace win32_defs
  762. {
  763. #if ! (defined (_WINDOWS_) || defined (_APISETLIBLOADER_)) // only use these local definitions if windows.h isn't already included
  764. using CHOC_HMODULE = void*;
  765. @@ -145,12 +114,12 @@ namespace choc::win32_defs
  766. #endif
  767. }
  768. -inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library)
  769. +inline DynamicLibrary::DynamicLibrary (std::string_view library)
  770. {
  771. handle = (void*) win32_defs::LoadLibraryA (std::string (library).c_str());
  772. }
  773. -inline void choc::file::DynamicLibrary::close()
  774. +inline void DynamicLibrary::close()
  775. {
  776. if (handle != nullptr)
  777. {
  778. @@ -159,7 +128,7 @@ inline void choc::file::DynamicLibrary::close()
  779. }
  780. }
  781. -inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
  782. +inline void* DynamicLibrary::findFunction (std::string_view name)
  783. {
  784. if (handle != nullptr)
  785. return (void*) win32_defs::GetProcAddress ((win32_defs::CHOC_HMODULE) handle, std::string (name).c_str());
  786. @@ -167,6 +136,6 @@ inline void* choc::file::DynamicLibrary::findFunction (std::string_view name)
  787. return {};
  788. }
  789. -#endif
  790. +END_NAMESPACE_DISTRHO
  791. #endif // CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED
  792. diff --git a/choc_FloatToString.h b/choc_FloatToString.h
  793. index bf89933..586ecca 100644
  794. --- a/choc_FloatToString.h
  795. +++ b/choc_FloatToString.h
  796. @@ -21,7 +21,7 @@
  797. #include <cstring>
  798. #include <string>
  799. -#include "../math/choc_MathHelpers.h"
  800. +#include "choc_MathHelpers.h"
  801. namespace choc::text
  802. {
  803. diff --git a/choc_JSON.h b/choc_JSON.h
  804. index dbea379..617e2af 100644
  805. --- a/choc_JSON.h
  806. +++ b/choc_JSON.h
  807. @@ -26,7 +26,7 @@
  808. #include "choc_UTF8.h"
  809. #include "choc_FloatToString.h"
  810. -#include "../containers/choc_Value.h"
  811. +#include "choc_Value.h"
  812. #undef max // It's never a smart idea to include any C headers before your C++ ones, as it
  813. #undef min // risks polluting your namespace with all kinds of dangerous macros like these ones.
  814. diff --git a/choc_MemoryDLL.h b/choc_MemoryDLL.h
  815. index 00c3caa..f60eea1 100644
  816. --- a/choc_MemoryDLL.h
  817. +++ b/choc_MemoryDLL.h
  818. @@ -23,8 +23,7 @@
  819. #include <memory>
  820. #include <string>
  821. -namespace choc::memory
  822. -{
  823. +START_NAMESPACE_DISTRHO
  824. /**
  825. MemoryDLL is an egregious hack that allows you to load DLL files from a chunk
  826. @@ -62,7 +61,7 @@ private:
  827. std::unique_ptr<Pimpl> pimpl;
  828. };
  829. -} // namespace choc::memory
  830. +END_NAMESPACE_DISTRHO
  831. @@ -77,8 +76,6 @@ private:
  832. //
  833. //==============================================================================
  834. -#if defined (_WIN32) || defined (_WIN64)
  835. -
  836. #include <vector>
  837. #include <unordered_map>
  838. #undef WIN32_LEAN_AND_MEAN
  839. @@ -89,8 +86,7 @@ private:
  840. #include <windows.h>
  841. #undef Rectangle
  842. -namespace choc::memory
  843. -{
  844. +START_NAMESPACE_DISTRHO
  845. struct MemoryDLL::Pimpl
  846. {
  847. @@ -497,20 +493,6 @@ private:
  848. }
  849. };
  850. -#else
  851. -
  852. -#include "choc_Assert.h"
  853. -
  854. -namespace choc::memory
  855. -{
  856. -struct MemoryDLL::Pimpl
  857. -{
  858. - bool initialise (const void*, size_t) { CHOC_ASSERT (false); return {}; } // Only available on Windows!
  859. - void* findFunction (std::string_view) { CHOC_ASSERT (false); return {}; } // Only available on Windows!
  860. -};
  861. -
  862. -#endif
  863. -
  864. inline MemoryDLL::~MemoryDLL() = default;
  865. inline MemoryDLL::MemoryDLL (const void* data, size_t size) : pimpl (std::make_unique<Pimpl>())
  866. @@ -524,6 +506,6 @@ inline void* MemoryDLL::findFunction (std::string_view name)
  867. return pimpl != nullptr ? pimpl->findFunction (name) : nullptr;
  868. }
  869. -} // namespace choc::memory
  870. +END_NAMESPACE_DISTRHO
  871. #endif // CHOC_MEMORYDLL_HEADER_INCLUDED
  872. diff --git a/choc_StringUtilities.h b/choc_StringUtilities.h
  873. index 3122f83..4cd54c6 100644
  874. --- a/choc_StringUtilities.h
  875. +++ b/choc_StringUtilities.h
  876. @@ -27,125 +27,14 @@
  877. #include <memory>
  878. #include <algorithm>
  879. #include <cwctype>
  880. -#include "../platform/choc_Assert.h"
  881. -namespace choc::text
  882. -{
  883. +START_NAMESPACE_DISTRHO
  884. //==============================================================================
  885. -inline bool isWhitespace (char c) { return c == ' ' || (c <= 13 && c >= 9); }
  886. -inline bool isDigit (char c) { return static_cast<uint32_t> (c - '0') < 10; }
  887. -
  888. -/// Replaces all occurrences of a one or more substrings.
  889. -/// The arguments must be a sequence of pairs of strings, where the first of each pair is the string to
  890. -/// look for, followed by its replacement.
  891. -template <typename StringType, typename... OtherReplacements>
  892. -std::string replace (StringType textToSearch,
  893. - std::string_view firstSubstringToReplace, std::string_view firstReplacement,
  894. - OtherReplacements&&... otherPairsOfStringsToReplace);
  895. -
  896. -/// Returns a string with any whitespace trimmed from its start and end.
  897. -std::string trim (std::string textToTrim);
  898. -
  899. -/// Returns a string with any whitespace trimmed from its start and end.
  900. -std::string_view trim (std::string_view textToTrim);
  901. -
  902. -/// Returns a string with any whitespace trimmed from its start and end.
  903. -std::string_view trim (const char* textToTrim);
  904. -
  905. -/// Returns a string with any whitespace trimmed from its start.
  906. -std::string trimStart (std::string textToTrim);
  907. -
  908. -/// Returns a string with any whitespace trimmed from its start.
  909. -std::string_view trimStart (std::string_view textToTrim);
  910. -
  911. -/// Returns a string with any whitespace trimmed from its start.
  912. -std::string_view trimStart (const char* textToTrim);
  913. -
  914. -/// Returns a string with any whitespace trimmed from its end.
  915. -std::string trimEnd (std::string textToTrim);
  916. -
  917. -/// Returns a string with any whitespace trimmed from its end.
  918. -std::string_view trimEnd (std::string_view textToTrim);
  919. -
  920. -/// Returns a string with any whitespace trimmed from its end.
  921. -std::string_view trimEnd (const char* textToTrim);
  922. -
  923. -/// If the string begins with one or more instances of the given character, this
  924. -/// skips past them, returning the remainder of the string.
  925. -std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip);
  926. -
  927. -/// If the given character is at the start and end of the string, it trims it away.
  928. -std::string removeOuterCharacter (std::string text, char outerChar);
  929. -
  930. -inline std::string removeDoubleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '"'); }
  931. -inline std::string removeSingleQuotes (std::string text) { return removeOuterCharacter (std::move (text), '\''); }
  932. -
  933. -inline std::string addDoubleQuotes (std::string text) { return "\"" + std::move (text) + "\""; }
  934. -inline std::string addSingleQuotes (std::string text) { return "'" + std::move (text) + "'"; }
  935. -
  936. -std::string toLowerCase (std::string);
  937. -std::string toUpperCase (std::string);
  938. -
  939. -template <typename IsDelimiterChar>
  940. -std::vector<std::string> splitString (std::string_view textToSplit,
  941. - IsDelimiterChar&& isDelimiterChar,
  942. - bool includeDelimitersInResult);
  943. -
  944. -template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
  945. -std::vector<std::string> splitString (std::string_view textToSplit,
  946. - CharStartsDelimiter&& isDelimiterStart,
  947. - CharIsInDelimiterBody&& isDelimiterBody,
  948. - bool includeDelimitersInResult);
  949. -
  950. -std::vector<std::string> splitString (std::string_view textToSplit,
  951. - char delimiterCharacter,
  952. - bool includeDelimitersInResult);
  953. -
  954. -std::vector<std::string> splitAtWhitespace (std::string_view text, bool keepDelimiters = false);
  955. -
  956. -/// Splits a string at newline characters, returning an array of strings.
  957. -std::vector<std::string> splitIntoLines (std::string_view text, bool includeNewLinesInResult);
  958. -
  959. -/// Joins some kind of array of strings into a single string, adding the given separator
  960. -/// between them (but not adding it at the start or end)
  961. -template <typename ArrayOfStrings>
  962. -std::string joinStrings (const ArrayOfStrings& strings, std::string_view separator);
  963. -
  964. -/// Returns true if this text contains the given sub-string.
  965. -bool contains (std::string_view text, std::string_view possibleSubstring);
  966. -/// Returns true if this text starts with the given character.
  967. -bool startsWith (std::string_view text, char possibleStart);
  968. -/// Returns true if this text starts with the given sub-string.
  969. -bool startsWith (std::string_view text, std::string_view possibleStart);
  970. -/// Returns true if this text ends with the given sub-string.
  971. -bool endsWith (std::string_view text, char possibleEnd);
  972. -/// Returns true if this text ends with the given sub-string.
  973. -bool endsWith (std::string_view text, std::string_view possibleEnd);
  974. -
  975. -/// Calculates the Levenstein distance between two strings.
  976. -template <typename StringType>
  977. -size_t getLevenshteinDistance (const StringType& string1,
  978. - const StringType& string2);
  979. -
  980. -/// Converts a hex character to a number 0-15, or -1 if it's not a valid hex digit.
  981. -int hexDigitToInt (uint32_t unicodeChar);
  982. -
  983. /// Returns a hex string for the given value.
  984. /// If the minimum number of digits is non-zero, it will be zero-padded to fill this length;
  985. template <typename IntegerType>
  986. -std::string createHexString (IntegerType value, int minNumDigits = 0);
  987. -
  988. -/// Returns a truncated, easy-to-read version of a time as hours, seconds or milliseconds,
  989. -/// depending on its magnitude. The use-cases include things like logging or console app output.
  990. -std::string getDurationDescription (std::chrono::duration<double, std::micro>);
  991. -
  992. -/// Returns an easy-to-read description of a size in bytes. Depending on the magnitude,
  993. -/// it might choose different units such as GB, MB, KB or just bytes.
  994. -std::string getByteSizeDescription (uint64_t sizeInBytes);
  995. -
  996. -/// Encodes a string as a legal URI, using percent-encoding (aka URL encoding)
  997. -std::string percentEncodeURI (std::string_view text);
  998. +std::string createHexString (IntegerType value);
  999. //==============================================================================
  1000. @@ -159,20 +48,11 @@ std::string percentEncodeURI (std::string_view text);
  1001. //
  1002. //==============================================================================
  1003. -inline int hexDigitToInt (uint32_t c)
  1004. -{
  1005. - auto d1 = c - static_cast<uint32_t> ('0'); if (d1 < 10u) return static_cast<int> (d1);
  1006. - auto d2 = d1 + static_cast<uint32_t> ('0' - 'a'); if (d2 < 6u) return static_cast<int> (d2 + 10);
  1007. - auto d3 = d2 + static_cast<uint32_t> ('a' - 'A'); if (d3 < 6u) return static_cast<int> (d3 + 10);
  1008. - return -1;
  1009. -}
  1010. -
  1011. template <typename IntegerType>
  1012. -std::string createHexString (IntegerType v, int minNumDigits)
  1013. +std::string createHexString (IntegerType v)
  1014. {
  1015. static_assert (std::is_integral<IntegerType>::value, "Need to pass integers into this method");
  1016. auto value = static_cast<typename std::make_unsigned<IntegerType>::type> (v);
  1017. - CHOC_ASSERT (minNumDigits <= 32);
  1018. char hex[40];
  1019. const auto end = hex + sizeof (hex) - 1;
  1020. @@ -183,415 +63,12 @@ std::string createHexString (IntegerType v, int minNumDigits)
  1021. {
  1022. *--d = "0123456789abcdef"[static_cast<uint32_t> (value) & 15u];
  1023. value = static_cast<decltype (value)> (value >> 4);
  1024. - --minNumDigits;
  1025. - if (value == 0 && minNumDigits <= 0)
  1026. + if (value == 0)
  1027. return std::string (d, end);
  1028. }
  1029. }
  1030. -template <typename StringType, typename... OtherReplacements>
  1031. -std::string replace (StringType textToSearch, std::string_view firstToReplace, std::string_view firstReplacement,
  1032. - OtherReplacements&&... otherPairsOfStringsToReplace)
  1033. -{
  1034. - static_assert ((sizeof... (otherPairsOfStringsToReplace) & 1u) == 0,
  1035. - "This function expects a list of pairs of strings as its arguments");
  1036. -
  1037. - if constexpr (std::is_same<const StringType, const std::string_view>::value || std::is_same<const StringType, const char* const>::value)
  1038. - {
  1039. - return replace (std::string (textToSearch), firstToReplace, firstReplacement,
  1040. - std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
  1041. - }
  1042. - else if constexpr (sizeof... (otherPairsOfStringsToReplace) == 0)
  1043. - {
  1044. - size_t pos = 0;
  1045. -
  1046. - for (;;)
  1047. - {
  1048. - pos = textToSearch.find (firstToReplace, pos);
  1049. -
  1050. - if (pos == std::string::npos)
  1051. - return textToSearch;
  1052. -
  1053. - textToSearch.replace (pos, firstToReplace.length(), firstReplacement);
  1054. - pos += firstReplacement.length();
  1055. - }
  1056. - }
  1057. - else
  1058. - {
  1059. - return replace (replace (std::move (textToSearch), firstToReplace, firstReplacement),
  1060. - std::forward<OtherReplacements> (otherPairsOfStringsToReplace)...);
  1061. - }
  1062. -}
  1063. -
  1064. -inline std::string trim (std::string text) { return trimStart (trimEnd (std::move (text))); }
  1065. -inline std::string_view trim (std::string_view text) { return trimStart (trimEnd (std::move (text))); }
  1066. -
  1067. -inline std::string_view trim (const char* text) { return trim (std::string_view (text)); }
  1068. -inline std::string_view trimStart (const char* text) { return trimStart (std::string_view (text)); }
  1069. -inline std::string_view trimEnd (const char* text) { return trimEnd (std::string_view (text)); }
  1070. -
  1071. -inline std::string trimStart (std::string text)
  1072. -{
  1073. - auto i = text.begin();
  1074. -
  1075. - if (i == text.end()) return {};
  1076. - if (! isWhitespace (*i)) return text;
  1077. -
  1078. - for (;;)
  1079. - {
  1080. - ++i;
  1081. -
  1082. - if (i == text.end()) return {};
  1083. - if (! isWhitespace (*i)) return { i, text.end() };
  1084. - }
  1085. -}
  1086. -
  1087. -inline std::string_view trimStart (std::string_view text)
  1088. -{
  1089. - size_t i = 0;
  1090. -
  1091. - for (auto c : text)
  1092. - {
  1093. - if (! isWhitespace (c))
  1094. - {
  1095. - text.remove_prefix (i);
  1096. - return text;
  1097. - }
  1098. -
  1099. - ++i;
  1100. - }
  1101. -
  1102. - return {};
  1103. -}
  1104. -
  1105. -inline std::string trimEnd (std::string text)
  1106. -{
  1107. - for (auto i = text.end();;)
  1108. - {
  1109. - if (i == text.begin())
  1110. - return {};
  1111. -
  1112. - --i;
  1113. -
  1114. - if (! isWhitespace (*i))
  1115. - {
  1116. - text.erase (i + 1, text.end());
  1117. - return text;
  1118. - }
  1119. - }
  1120. -}
  1121. -
  1122. -inline std::string_view trimEnd (std::string_view text)
  1123. -{
  1124. - for (auto i = text.length(); i != 0; --i)
  1125. - if (! isWhitespace (text[i - 1]))
  1126. - return text.substr (0, i);
  1127. -
  1128. - return {};
  1129. -}
  1130. -
  1131. -inline std::string_view trimCharacterAtStart (std::string_view textToTrim, char characterToSkip)
  1132. -{
  1133. - for (size_t i = 0; i < textToTrim.length(); ++i)
  1134. - if (textToTrim[i] != characterToSkip)
  1135. - return textToTrim.substr (i);
  1136. -
  1137. - return {};
  1138. -}
  1139. -
  1140. -inline std::string removeOuterCharacter (std::string t, char outerChar)
  1141. -{
  1142. - if (t.length() >= 2 && t.front() == outerChar && t.back() == outerChar)
  1143. - return t.substr (1, t.length() - 2);
  1144. -
  1145. - return t;
  1146. -}
  1147. -
  1148. -inline std::string toLowerCase (std::string s)
  1149. -{
  1150. - std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::tolower (static_cast<unsigned char> (c))); });
  1151. - return s;
  1152. -}
  1153. -
  1154. -inline std::string toUpperCase (std::string s)
  1155. -{
  1156. - std::transform (s.begin(), s.end(), s.begin(), [] (auto c) { return static_cast<char> (std::toupper (static_cast<unsigned char> (c))); });
  1157. - return s;
  1158. -}
  1159. -
  1160. -template <typename CharStartsDelimiter, typename CharIsInDelimiterBody>
  1161. -std::vector<std::string> splitString (std::string_view source,
  1162. - CharStartsDelimiter&& isDelimiterStart,
  1163. - CharIsInDelimiterBody&& isDelimiterBody,
  1164. - bool keepDelimiters)
  1165. -{
  1166. - std::vector<std::string> tokens;
  1167. - auto tokenStart = source.begin();
  1168. - auto pos = tokenStart;
  1169. -
  1170. - while (pos != source.end())
  1171. - {
  1172. - if (isDelimiterStart (*pos))
  1173. - {
  1174. - auto delimiterStart = pos++;
  1175. -
  1176. - while (pos != source.end() && isDelimiterBody (*pos))
  1177. - ++pos;
  1178. -
  1179. - if (pos != source.begin())
  1180. - tokens.push_back ({ tokenStart, keepDelimiters ? pos : delimiterStart });
  1181. -
  1182. - tokenStart = pos;
  1183. - }
  1184. - else
  1185. - {
  1186. - ++pos;
  1187. - }
  1188. - }
  1189. -
  1190. - if (pos != source.begin())
  1191. - tokens.push_back ({ tokenStart, pos });
  1192. -
  1193. - return tokens;
  1194. -}
  1195. -
  1196. -template <typename IsDelimiterChar>
  1197. -std::vector<std::string> splitString (std::string_view source, IsDelimiterChar&& isDelimiterChar, bool keepDelimiters)
  1198. -{
  1199. - std::vector<std::string> tokens;
  1200. - auto tokenStart = source.begin();
  1201. - auto pos = tokenStart;
  1202. -
  1203. - while (pos != source.end())
  1204. - {
  1205. - if (isDelimiterChar (*pos))
  1206. - {
  1207. - tokens.push_back ({ tokenStart, keepDelimiters ? pos + 1 : pos });
  1208. - tokenStart = ++pos;
  1209. - }
  1210. - else
  1211. - {
  1212. - ++pos;
  1213. - }
  1214. - }
  1215. -
  1216. - if (pos != source.begin())
  1217. - tokens.push_back ({ tokenStart, pos });
  1218. -
  1219. - return tokens;
  1220. -}
  1221. -
  1222. -inline std::vector<std::string> splitString (std::string_view text, char delimiterCharacter, bool keepDelimiters)
  1223. -{
  1224. - return splitString (text, [=] (char c) { return c == delimiterCharacter; }, keepDelimiters);
  1225. -}
  1226. -
  1227. -inline std::vector<std::string> splitAtWhitespace (std::string_view text, bool keepDelimiters)
  1228. -{
  1229. - return splitString (text,
  1230. - [] (char c) { return isWhitespace (c); },
  1231. - [] (char c) { return isWhitespace (c); },
  1232. - keepDelimiters);
  1233. -}
  1234. -
  1235. -inline std::vector<std::string> splitIntoLines (std::string_view text, bool includeNewLinesInResult)
  1236. -{
  1237. - return splitString (text, '\n', includeNewLinesInResult);
  1238. -}
  1239. -
  1240. -template <typename ArrayOfStrings>
  1241. -inline std::string joinStrings (const ArrayOfStrings& strings, std::string_view sep)
  1242. -{
  1243. - if (strings.empty())
  1244. - return {};
  1245. -
  1246. - auto spaceNeeded = sep.length() * strings.size();
  1247. -
  1248. - for (auto& s : strings)
  1249. - spaceNeeded += s.length();
  1250. -
  1251. - std::string result (strings.front());
  1252. - result.reserve (spaceNeeded);
  1253. -
  1254. - for (size_t i = 1; i < strings.size(); ++i)
  1255. - {
  1256. - result += sep;
  1257. - result += strings[i];
  1258. - }
  1259. -
  1260. - return result;
  1261. -}
  1262. -
  1263. -inline bool contains (std::string_view t, std::string_view s) { return t.find (s) != std::string::npos; }
  1264. -inline bool startsWith (std::string_view t, char s) { return ! t.empty() && t.front() == s; }
  1265. -inline bool endsWith (std::string_view t, char s) { return ! t.empty() && t.back() == s; }
  1266. -
  1267. -inline bool startsWith (std::string_view t, std::string_view s)
  1268. -{
  1269. - auto len = s.length();
  1270. - return t.length() >= len && t.substr (0, len) == s;
  1271. -}
  1272. -
  1273. -inline bool endsWith (std::string_view t, std::string_view s)
  1274. -{
  1275. - auto len1 = t.length(), len2 = s.length();
  1276. - return len1 >= len2 && t.substr (len1 - len2) == s;
  1277. -}
  1278. -
  1279. -inline std::string getDurationDescription (std::chrono::duration<double, std::micro> d)
  1280. -{
  1281. - auto microseconds = std::chrono::duration_cast<std::chrono::microseconds> (d).count();
  1282. -
  1283. - if (microseconds < 0) return "-" + getDurationDescription (-d);
  1284. - if (microseconds == 0) return "0 sec";
  1285. -
  1286. - std::string result;
  1287. -
  1288. - auto addLevel = [&] (int64_t size, std::string_view units, int64_t decimalScale, int64_t modulo) -> bool
  1289. - {
  1290. - if (microseconds < size)
  1291. - return false;
  1292. -
  1293. - if (! result.empty())
  1294. - result += ' ';
  1295. -
  1296. - auto scaled = (microseconds * decimalScale + size / 2) / size;
  1297. - auto whole = scaled / decimalScale;
  1298. -
  1299. - if (modulo != 0)
  1300. - whole = whole % modulo;
  1301. -
  1302. - result += std::to_string (whole);
  1303. -
  1304. - if (auto fraction = scaled % decimalScale)
  1305. - {
  1306. - result += '.';
  1307. - result += static_cast<char> ('0' + (fraction / 10));
  1308. -
  1309. - if (fraction % 10 != 0)
  1310. - result += static_cast<char> ('0' + (fraction % 10));
  1311. - }
  1312. -
  1313. - result += (whole == 1 && units.length() > 3 && units.back() == 's') ? units.substr (0, units.length() - 1) : units;
  1314. - return true;
  1315. - };
  1316. -
  1317. - bool hours = addLevel (60000000ll * 60ll, " hours", 1, 0);
  1318. - bool mins = addLevel (60000000ll, " min", 1, hours ? 60 : 0);
  1319. -
  1320. - if (hours)
  1321. - return result;
  1322. -
  1323. - if (mins)
  1324. - {
  1325. - addLevel (1000000, " sec", 1, 60);
  1326. - }
  1327. - else
  1328. - {
  1329. - if (! addLevel (1000000, " sec", 100, 0))
  1330. - if (! addLevel (1000, " ms", 100, 0))
  1331. - addLevel (1, " microseconds", 100, 0);
  1332. - }
  1333. -
  1334. - return result;
  1335. -}
  1336. -
  1337. -template <typename StringType>
  1338. -size_t getLevenshteinDistance (const StringType& string1, const StringType& string2)
  1339. -{
  1340. - if (string1.empty()) return string2.length();
  1341. - if (string2.empty()) return string1.length();
  1342. -
  1343. - auto calculate = [] (size_t* costs, size_t numCosts, const StringType& s1, const StringType& s2) -> size_t
  1344. - {
  1345. - for (size_t i = 0; i < numCosts; ++i)
  1346. - costs[i] = i;
  1347. -
  1348. - size_t p1 = 0;
  1349. -
  1350. - for (auto c1 : s1)
  1351. - {
  1352. - auto corner = p1;
  1353. - *costs = p1 + 1;
  1354. - size_t p2 = 0;
  1355. -
  1356. - for (auto c2 : s2)
  1357. - {
  1358. - auto upper = costs[p2 + 1];
  1359. - costs[p2 + 1] = c1 == c2 ? corner : (std::min (costs[p2], std::min (upper, corner)) + 1);
  1360. - ++p2;
  1361. - corner = upper;
  1362. - }
  1363. -
  1364. - ++p1;
  1365. - }
  1366. -
  1367. - return costs[numCosts - 1];
  1368. - };
  1369. -
  1370. - auto sizeNeeded = string2.length() + 1;
  1371. - constexpr size_t maxStackSize = 96;
  1372. -
  1373. - if (sizeNeeded <= maxStackSize)
  1374. - {
  1375. - size_t costs[maxStackSize];
  1376. - return calculate (costs, sizeNeeded, string1, string2);
  1377. - }
  1378. -
  1379. - std::unique_ptr<size_t[]> costs (new size_t[sizeNeeded]);
  1380. - return calculate (costs.get(), sizeNeeded, string1, string2);
  1381. -}
  1382. -
  1383. -inline std::string getByteSizeDescription (uint64_t size)
  1384. -{
  1385. - auto intToStringWith1DecPlace = [] (uint64_t n, uint64_t divisor) -> std::string
  1386. - {
  1387. - auto scaled = (n * 10 + divisor / 2) / divisor;
  1388. - auto result = std::to_string (scaled / 10);
  1389. -
  1390. - if (auto fraction = scaled % 10)
  1391. - {
  1392. - result += '.';
  1393. - result += static_cast<char> ('0' + fraction);
  1394. - }
  1395. -
  1396. - return result;
  1397. - };
  1398. -
  1399. - static constexpr uint64_t maxValue = std::numeric_limits<uint64_t>::max() / 10;
  1400. -
  1401. - if (size >= 0x40000000) return intToStringWith1DecPlace (std::min (maxValue, size), 0x40000000) + " GB";
  1402. - if (size >= 0x100000) return intToStringWith1DecPlace (size, 0x100000) + " MB";
  1403. - if (size >= 0x400) return intToStringWith1DecPlace (size, 0x400) + " KB";
  1404. - if (size != 1) return std::to_string (size) + " bytes";
  1405. -
  1406. - return "1 byte";
  1407. -}
  1408. -
  1409. -inline std::string percentEncodeURI (std::string_view text)
  1410. -{
  1411. - std::string result;
  1412. - result.reserve (text.length());
  1413. -
  1414. - for (auto c : text)
  1415. - {
  1416. - if (std::string_view ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.~").find (c) != std::string_view::npos)
  1417. - {
  1418. - result += c;
  1419. - }
  1420. - else
  1421. - {
  1422. - result += '%';
  1423. - result += "0123456789abcdef"[static_cast<uint8_t> (c) >> 4];
  1424. - result += "0123456789abcdef"[static_cast<uint8_t> (c) & 15u];
  1425. - }
  1426. - }
  1427. -
  1428. - return result;
  1429. -}
  1430. -
  1431. -
  1432. -} // namespace choc::text
  1433. +END_NAMESPACE_DISTRHO
  1434. #endif
  1435. diff --git a/choc_Value.h b/choc_Value.h
  1436. index bd27f7a..5df22c4 100644
  1437. --- a/choc_Value.h
  1438. +++ b/choc_Value.h
  1439. @@ -25,7 +25,7 @@
  1440. #include <algorithm>
  1441. #include <memory>
  1442. #include <exception>
  1443. -#include "../platform/choc_Assert.h"
  1444. +#include "choc_Assert.h"
  1445. namespace choc::value
  1446. {
  1447. diff --git a/choc_WebView.h b/choc_WebView.h
  1448. index 3ddf610..935dce1 100644
  1449. --- a/choc_WebView.h
  1450. +++ b/choc_WebView.h
  1451. @@ -23,46 +23,18 @@
  1452. #include <unordered_map>
  1453. #include <vector>
  1454. #include <functional>
  1455. -#include "../platform/choc_Platform.h"
  1456. -#include "../text/choc_JSON.h"
  1457. +#include <memory>
  1458. +#include <string>
  1459. +#include "choc_Platform.h"
  1460. +#include "choc_StringUtilities.h"
  1461. -//==============================================================================
  1462. -namespace choc::ui
  1463. -{
  1464. +START_NAMESPACE_DISTRHO
  1465. /**
  1466. Creates an embedded browser which can be placed inside some kind of parent window.
  1467. After creating a WebView object, its getViewHandle() returns a platform-specific
  1468. - handle that can be added to whatever kind of window is appropriate. The
  1469. - choc::ui::DesktopWindow class is an example of a window that can have the
  1470. - webview added to it via its choc::ui::DesktopWindow::setContent() method.
  1471. -
  1472. - There are unfortunately a few extra build steps needed for using WebView
  1473. - in your projects:
  1474. -
  1475. - - On OSX, you'll need to add the `WebKit` framework to your project
  1476. -
  1477. - - On Linux, you'll need to:
  1478. - 1. Install the libgtk-3-dev and libwebkit2gtk-4.0-dev packages.
  1479. - 2. Link the gtk+3.0 and webkit2gtk-4.0 libraries in your build.
  1480. - You might want to have a look inside choc/tests/CMakeLists.txt for
  1481. - an example of how to add those packages to your build without too
  1482. - much fuss.
  1483. -
  1484. - - On Windows, no extra build steps needed!! This is a bigger deal than it
  1485. - sounds, because normally to use an embedded Edge browser on Windows is a
  1486. - total PITA, involving downloading a special Microsoft SDK with redistributable
  1487. - DLLs, etc, but I've jumped through many ugly hoops to make this class
  1488. - fully self-contained.
  1489. -
  1490. - Because this is a GUI, it needs a message loop to be running. If you're using
  1491. - it inside an app which already runs a message loop, it should just work,
  1492. - or you can use choc::messageloop::run() and choc::messageloop::stop() for an easy
  1493. - but basic loop.
  1494. -
  1495. - For an example of how to use this class, see `choc/tests/main.cpp` where
  1496. - there's a simple demo.
  1497. + handle that can be added to whatever kind of window is appropriate.
  1498. */
  1499. class WebView
  1500. {
  1501. @@ -82,34 +54,6 @@ public:
  1502. // this empty for default behaviour.
  1503. std::string customUserAgent;
  1504. - /// If you provide a fetchResource function, it is expected to return this
  1505. - /// object, which is simply the raw content of the resource, and its MIME type.
  1506. - struct Resource
  1507. - {
  1508. - Resource() = default;
  1509. - Resource (std::string_view content, std::string mimeType);
  1510. -
  1511. - std::vector<uint8_t> data;
  1512. - std::string mimeType;
  1513. - };
  1514. -
  1515. - using FetchResource = std::function<std::optional<Resource>(const std::string& path)>;
  1516. -
  1517. - /// Serve resources to the browser from a C++ callback function.
  1518. - /// This can effectively be used to implement a basic web server,
  1519. - /// serving resources to the browser in any way the client code chooses.
  1520. - /// Given the path URL component (i.e. starting from "/"), the client should
  1521. - /// return some bytes, and the associated MIME type, for that resource.
  1522. - /// When provided, this function will initially be called with the root path
  1523. - /// ("/") in order to serve the initial content for the page (or if the
  1524. - /// customSchemeURI property is also set, it will navigate to that URI).
  1525. - /// When this happens, the client should return some HTML with a "text/html"
  1526. - /// MIME type.
  1527. - /// Subsequent relative requests made from the page (e.g. via `img` tags,
  1528. - /// `fetch` calls from javascript etc) will all invoke calls to this callback
  1529. - /// with the requested path as their argument.
  1530. - FetchResource fetchResource;
  1531. -
  1532. /// If fetchResource is being used to serve custom data, you can choose to
  1533. /// override the default URI scheme by providing a home URI here, e.g. if
  1534. /// you wanted a scheme called `foo:`, you might set this to `foo://myname.com`
  1535. @@ -132,31 +76,18 @@ public:
  1536. /// fail on some systems if the OS doesn't provide a suitable component.
  1537. bool loadedOK() const;
  1538. - /// Directly sets the HTML content of the browser
  1539. - bool setHTML (const std::string& html);
  1540. -
  1541. - /// This function type is used by evaluateJavascript().
  1542. - using CompletionHandler = std::function<void(const std::string& error,
  1543. - const choc::value::ValueView& result)>;
  1544. -
  1545. /// Asynchronously evaluates some javascript.
  1546. - /// If you want to find out the result of the expression (or whether there
  1547. - /// was a compile error etc), then provide a callback function which will be
  1548. - /// invoked when the script is complete.
  1549. /// This will return true if the webview is in a state that lets it run code, or
  1550. /// false if something prevents that.
  1551. - bool evaluateJavascript (const std::string& script, CompletionHandler completionHandler = {});
  1552. + bool evaluateJavascript (const std::string& script);
  1553. /// Sends the browser to this URL
  1554. bool navigate (const std::string& url);
  1555. /// A callback function which can be passed to bind().
  1556. - using CallbackFn = std::function<choc::value::Value(const choc::value::ValueView& args)>;
  1557. - /// Binds a C++ function to a named javascript function that can be called
  1558. - /// by code running in the browser.
  1559. - bool bind (const std::string& functionName, CallbackFn&& function);
  1560. - /// Removes a previously-bound function.
  1561. - bool unbind (const std::string& functionName);
  1562. + using CallbackFn = std::function<void(const std::string& msg)>;
  1563. + /// Set the C++ callback function.
  1564. + bool bind (CallbackFn&& function);
  1565. /// Adds a script to run when the browser loads a page
  1566. bool addInitScript (const std::string& script);
  1567. @@ -169,14 +100,11 @@ private:
  1568. struct Pimpl;
  1569. std::unique_ptr<Pimpl> pimpl;
  1570. - std::unordered_map<std::string, CallbackFn> bindings;
  1571. + CallbackFn binding {};
  1572. void invokeBinding (const std::string&);
  1573. - static std::string getURIScheme (const Options&);
  1574. - static std::string getURIHome (const Options&);
  1575. - struct DeletionChecker { bool deleted = false; };
  1576. };
  1577. -} // namespace choc::ui
  1578. +END_NAMESPACE_DISTRHO
  1579. //==============================================================================
  1580. @@ -190,737 +118,14 @@ private:
  1581. //
  1582. //==============================================================================
  1583. -#if CHOC_LINUX
  1584. -
  1585. -#include "../platform/choc_DisableAllWarnings.h"
  1586. -#include <JavaScriptCore/JavaScript.h>
  1587. -#include <webkit2/webkit2.h>
  1588. -#include "../platform/choc_ReenableAllWarnings.h"
  1589. -
  1590. -struct choc::ui::WebView::Pimpl
  1591. -{
  1592. - Pimpl (WebView& v, const Options& options)
  1593. - : owner (v), fetchResource (options.fetchResource)
  1594. - {
  1595. - if (! gtk_init_check (nullptr, nullptr))
  1596. - return;
  1597. -
  1598. - defaultURI = getURIHome (options);
  1599. - webviewContext = webkit_web_context_new();
  1600. - g_object_ref_sink (G_OBJECT (webviewContext));
  1601. - webview = webkit_web_view_new_with_context (webviewContext);
  1602. - g_object_ref_sink (G_OBJECT (webview));
  1603. - manager = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (webview));
  1604. -
  1605. - signalHandlerID = g_signal_connect (manager, "script-message-received::external",
  1606. - G_CALLBACK (+[] (WebKitUserContentManager*, WebKitJavascriptResult* r, gpointer arg)
  1607. - {
  1608. - static_cast<Pimpl*> (arg)->invokeCallback (r);
  1609. - }),
  1610. - this);
  1611. -
  1612. - webkit_user_content_manager_register_script_message_handler (manager, "external");
  1613. -
  1614. - WebKitSettings* settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (webview));
  1615. - webkit_settings_set_javascript_can_access_clipboard (settings, true);
  1616. -
  1617. - if (options.enableDebugMode)
  1618. - {
  1619. - webkit_settings_set_enable_write_console_messages_to_stdout (settings, true);
  1620. - webkit_settings_set_enable_developer_extras (settings, true);
  1621. -
  1622. - if (auto inspector = WEBKIT_WEB_INSPECTOR (webkit_web_view_get_inspector (WEBKIT_WEB_VIEW (webview))))
  1623. - webkit_web_inspector_show (inspector);
  1624. - }
  1625. -
  1626. - if (! options.customUserAgent.empty())
  1627. - webkit_settings_set_user_agent (settings, options.customUserAgent.c_str());
  1628. -
  1629. - if (options.fetchResource)
  1630. - {
  1631. - const auto onResourceRequested = [] (auto* request, auto* context)
  1632. - {
  1633. - try
  1634. - {
  1635. - const auto* path = webkit_uri_scheme_request_get_path (request);
  1636. -
  1637. - if (const auto resource = static_cast<Pimpl*> (context)->fetchResource (path))
  1638. - {
  1639. - const auto& [bytes, mimeType] = *resource;
  1640. -
  1641. - auto* streamBytes = g_bytes_new (bytes.data(), static_cast<gsize> (bytes.size()));
  1642. - auto* stream = g_memory_input_stream_new_from_bytes (streamBytes);
  1643. - g_bytes_unref (streamBytes);
  1644. -
  1645. - auto* response = webkit_uri_scheme_response_new (stream, static_cast<gint64> (bytes.size()));
  1646. - webkit_uri_scheme_response_set_status (response, 200, nullptr);
  1647. - webkit_uri_scheme_response_set_content_type (response, mimeType.c_str());
  1648. -
  1649. - auto* headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
  1650. - soup_message_headers_append (headers, "Cache-Control", "no-store");
  1651. - soup_message_headers_append (headers, "Access-Control-Allow-Origin", "*");
  1652. - webkit_uri_scheme_response_set_http_headers (response, headers); // response takes ownership of the headers
  1653. -
  1654. - webkit_uri_scheme_request_finish_with_response (request, response);
  1655. -
  1656. - g_object_unref (stream);
  1657. - g_object_unref (response);
  1658. - }
  1659. - else
  1660. - {
  1661. - auto* stream = g_memory_input_stream_new();
  1662. - auto* response = webkit_uri_scheme_response_new (stream, -1);
  1663. - webkit_uri_scheme_response_set_status (response, 404, nullptr);
  1664. -
  1665. - webkit_uri_scheme_request_finish_with_response (request, response);
  1666. -
  1667. - g_object_unref (stream);
  1668. - g_object_unref (response);
  1669. - }
  1670. - }
  1671. - catch (...)
  1672. - {
  1673. - auto* error = g_error_new (WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_FAILED, "Something went wrong");
  1674. - webkit_uri_scheme_request_finish_error (request, error);
  1675. - g_error_free (error);
  1676. - }
  1677. - };
  1678. -
  1679. - webkit_web_context_register_uri_scheme (webviewContext, getURIScheme (options).c_str(), onResourceRequested, this, nullptr);
  1680. - navigate ({});
  1681. - }
  1682. -
  1683. - gtk_widget_show_all (webview);
  1684. - }
  1685. -
  1686. - ~Pimpl()
  1687. - {
  1688. - deletionChecker->deleted = true;
  1689. -
  1690. - if (signalHandlerID != 0 && webview != nullptr)
  1691. - g_signal_handler_disconnect (manager, signalHandlerID);
  1692. -
  1693. - g_clear_object (&webview);
  1694. - g_clear_object (&webviewContext);
  1695. - }
  1696. -
  1697. - static constexpr const char* postMessageFn = "window.webkit.messageHandlers.external.postMessage";
  1698. -
  1699. - bool loadedOK() const { return getViewHandle() != nullptr; }
  1700. - void* getViewHandle() const { return (void*) webview; }
  1701. -
  1702. - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
  1703. -
  1704. - bool evaluateJavascript (const std::string& js, CompletionHandler&& completionHandler)
  1705. - {
  1706. - GAsyncReadyCallback callback = {};
  1707. - gpointer userData = {};
  1708. -
  1709. - if (completionHandler)
  1710. - {
  1711. - callback = evaluationCompleteCallback;
  1712. - userData = new CompletionHandler (std::move (completionHandler));
  1713. - }
  1714. -
  1715. - #if WEBKIT_CHECK_VERSION (2, 40, 0)
  1716. - webkit_web_view_evaluate_javascript (WEBKIT_WEB_VIEW (webview), js.c_str(), static_cast<gssize> (js.length()),
  1717. - nullptr, nullptr, nullptr, callback, userData);
  1718. - #else
  1719. - webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (webview), js.c_str(), nullptr, callback, userData);
  1720. - #endif
  1721. - return true;
  1722. - }
  1723. -
  1724. - static void evaluationCompleteCallback (GObject* object, GAsyncResult* result, gpointer userData)
  1725. - {
  1726. - std::unique_ptr<CompletionHandler> completionHandler (reinterpret_cast<CompletionHandler*> (userData));
  1727. - choc::value::Value value;
  1728. - std::string errorMessage;
  1729. - GError* error = {};
  1730. -
  1731. - #if WEBKIT_CHECK_VERSION (2, 40, 0)
  1732. - if (auto js_result = webkit_web_view_evaluate_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error))
  1733. - #else
  1734. - if (auto js_result = webkit_web_view_run_javascript_finish (WEBKIT_WEB_VIEW (object), result, &error))
  1735. - #endif
  1736. - {
  1737. - if (error != nullptr)
  1738. - {
  1739. - errorMessage = error->message;
  1740. - g_error_free (error);
  1741. - }
  1742. -
  1743. - #if WEBKIT_CHECK_VERSION (2, 40, 0)
  1744. - if (auto js_value = js_result)
  1745. - #else
  1746. - if (auto js_value = webkit_javascript_result_get_js_value (js_result))
  1747. - #endif
  1748. - {
  1749. - if (auto json = jsc_value_to_json (js_value, 0))
  1750. - {
  1751. - try
  1752. - {
  1753. - auto jsonView = std::string_view (json);
  1754. -
  1755. - if (! jsonView.empty())
  1756. - value = choc::json::parseValue (jsonView);
  1757. - }
  1758. - catch (const std::exception& e)
  1759. - {
  1760. - if (errorMessage.empty())
  1761. - errorMessage = e.what();
  1762. - }
  1763. -
  1764. - g_free (json);
  1765. - }
  1766. - else
  1767. - {
  1768. - if (errorMessage.empty())
  1769. - errorMessage = "Failed to convert result to JSON";
  1770. - }
  1771. -
  1772. - #if WEBKIT_CHECK_VERSION (2, 40, 0)
  1773. - g_object_unref (js_value);
  1774. - #endif
  1775. - }
  1776. - else
  1777. - {
  1778. - if (errorMessage.empty())
  1779. - errorMessage = "Failed to fetch result";
  1780. - }
  1781. -
  1782. - #if ! WEBKIT_CHECK_VERSION (2, 40, 0)
  1783. - webkit_javascript_result_unref (js_result);
  1784. - #endif
  1785. - }
  1786. - else
  1787. - {
  1788. - errorMessage = "Failed to fetch result";
  1789. - }
  1790. -
  1791. - (*completionHandler) (errorMessage, value);
  1792. - }
  1793. -
  1794. - bool navigate (const std::string& url)
  1795. - {
  1796. - if (url.empty())
  1797. - return navigate (defaultURI);
  1798. -
  1799. - webkit_web_view_load_uri (WEBKIT_WEB_VIEW (webview), url.c_str());
  1800. - return true;
  1801. - }
  1802. -
  1803. - bool setHTML (const std::string& html)
  1804. - {
  1805. - webkit_web_view_load_html (WEBKIT_WEB_VIEW (webview), html.c_str(), nullptr);
  1806. - return true;
  1807. - }
  1808. -
  1809. - bool addInitScript (const std::string& js)
  1810. - {
  1811. - if (manager != nullptr)
  1812. - {
  1813. - webkit_user_content_manager_add_script (manager, webkit_user_script_new (js.c_str(),
  1814. - WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
  1815. - WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
  1816. - nullptr, nullptr));
  1817. - return true;
  1818. - }
  1819. -
  1820. - return false;
  1821. - }
  1822. -
  1823. - void invokeCallback (WebKitJavascriptResult* r)
  1824. - {
  1825. - auto s = jsc_value_to_string (webkit_javascript_result_get_js_value (r));
  1826. - owner.invokeBinding (s);
  1827. - g_free (s);
  1828. - }
  1829. -
  1830. - WebView& owner;
  1831. - Options::FetchResource fetchResource;
  1832. - WebKitWebContext* webviewContext = {};
  1833. - GtkWidget* webview = {};
  1834. - WebKitUserContentManager* manager = {};
  1835. - std::string defaultURI;
  1836. - unsigned long signalHandlerID = 0;
  1837. -};
  1838. -
  1839. -//==============================================================================
  1840. -#elif CHOC_APPLE
  1841. -
  1842. -#include "choc_MessageLoop.h"
  1843. -
  1844. -struct choc::ui::WebView::Pimpl
  1845. -{
  1846. - Pimpl (WebView& v, const Options& optionsToUse)
  1847. - : owner (v), options (std::make_unique<Options> (optionsToUse))
  1848. - {
  1849. - using namespace choc::objc;
  1850. - CHOC_AUTORELEASE_BEGIN
  1851. -
  1852. - defaultURI = getURIHome (*options);
  1853. -
  1854. - id config = call<id> (getClass ("WKWebViewConfiguration"), "new");
  1855. -
  1856. - id prefs = call<id> (config, "preferences");
  1857. - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("fullScreenEnabled"));
  1858. - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("DOMPasteAllowed"));
  1859. - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("javaScriptCanAccessClipboard"));
  1860. -
  1861. - if (options->enableDebugMode)
  1862. - call<void> (prefs, "setValue:forKey:", getNSNumberBool (true), getNSString ("developerExtrasEnabled"));
  1863. -
  1864. - delegate = createDelegate();
  1865. - objc_setAssociatedObject (delegate, "choc_webview", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
  1866. -
  1867. - manager = call<id> (config, "userContentController");
  1868. - call<void> (manager, "retain");
  1869. - call<void> (manager, "addScriptMessageHandler:name:", delegate, getNSString ("external"));
  1870. -
  1871. - if (options->fetchResource)
  1872. - call<void> (config, "setURLSchemeHandler:forURLScheme:", delegate, getNSString (getURIScheme (*options)));
  1873. -
  1874. - webview = call<id> (allocateWebview(), "initWithFrame:configuration:", CGRect(), config);
  1875. - objc_setAssociatedObject (webview, "choc_webview", (CHOC_OBJC_CAST_BRIDGED id) this, OBJC_ASSOCIATION_ASSIGN);
  1876. -
  1877. - if (! options->customUserAgent.empty())
  1878. - call<void> (webview, "setValue:forKey:", getNSString (options->customUserAgent), getNSString ("customUserAgent"));
  1879. -
  1880. - call<void> (webview, "setUIDelegate:", delegate);
  1881. - call<void> (webview, "setNavigationDelegate:", delegate);
  1882. -
  1883. - call<void> (config, "release");
  1884. -
  1885. - if (options->fetchResource)
  1886. - navigate ({});
  1887. -
  1888. - CHOC_AUTORELEASE_END
  1889. - }
  1890. -
  1891. - ~Pimpl()
  1892. - {
  1893. - CHOC_AUTORELEASE_BEGIN
  1894. - deletionChecker->deleted = true;
  1895. - objc_setAssociatedObject (delegate, "choc_webview", nil, OBJC_ASSOCIATION_ASSIGN);
  1896. - objc_setAssociatedObject (webview, "choc_webview", nil, OBJC_ASSOCIATION_ASSIGN);
  1897. - objc::call<void> (webview, "release");
  1898. - webview = {};
  1899. - objc::call<void> (manager, "removeScriptMessageHandlerForName:", objc::getNSString ("external"));
  1900. - objc::call<void> (manager, "release");
  1901. - manager = {};
  1902. - objc::call<void> (delegate, "release");
  1903. - delegate = {};
  1904. - CHOC_AUTORELEASE_END
  1905. - }
  1906. -
  1907. - static constexpr const char* postMessageFn = "window.webkit.messageHandlers.external.postMessage";
  1908. -
  1909. - bool loadedOK() const { return getViewHandle() != nullptr; }
  1910. - void* getViewHandle() const { return (CHOC_OBJC_CAST_BRIDGED void*) webview; }
  1911. -
  1912. - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
  1913. -
  1914. - bool addInitScript (const std::string& script)
  1915. - {
  1916. - using namespace choc::objc;
  1917. - CHOC_AUTORELEASE_BEGIN
  1918. -
  1919. - if (id s = call<id> (call<id> (getClass ("WKUserScript"), "alloc"), "initWithSource:injectionTime:forMainFrameOnly:",
  1920. - getNSString (script), WKUserScriptInjectionTimeAtDocumentStart, (BOOL) 1))
  1921. - {
  1922. - call<void> (manager, "addUserScript:", s);
  1923. - call<void> (s, "release");
  1924. - return true;
  1925. - }
  1926. -
  1927. - CHOC_AUTORELEASE_END
  1928. - return false;
  1929. - }
  1930. -
  1931. - bool navigate (const std::string& url)
  1932. - {
  1933. - if (url.empty())
  1934. - return navigate (defaultURI);
  1935. -
  1936. - using namespace choc::objc;
  1937. - CHOC_AUTORELEASE_BEGIN
  1938. -
  1939. - if (id nsURL = call<id> (getClass ("NSURL"), "URLWithString:", getNSString (url)))
  1940. - return call<id> (webview, "loadRequest:", call<id> (getClass ("NSURLRequest"), "requestWithURL:", nsURL)) != nullptr;
  1941. -
  1942. - CHOC_AUTORELEASE_END
  1943. - return false;
  1944. - }
  1945. -
  1946. - bool setHTML (const std::string& html)
  1947. - {
  1948. - CHOC_AUTORELEASE_BEGIN
  1949. - return objc::call<id> (webview, "loadHTMLString:baseURL:", objc::getNSString (html), (id) nullptr) != nullptr;
  1950. - CHOC_AUTORELEASE_END
  1951. - }
  1952. -
  1953. - bool evaluateJavascript (const std::string& script, CompletionHandler completionHandler)
  1954. - {
  1955. - CHOC_AUTORELEASE_BEGIN
  1956. - auto s = objc::getNSString (script);
  1957. -
  1958. - if (completionHandler)
  1959. - {
  1960. - objc::call<void> (webview, "evaluateJavaScript:completionHandler:", s,
  1961. - ^(id result, id error)
  1962. - {
  1963. - CHOC_AUTORELEASE_BEGIN
  1964. -
  1965. - auto errorMessage = getMessageFromNSError (error);
  1966. - choc::value::Value value;
  1967. -
  1968. - try
  1969. - {
  1970. - if (auto json = convertNSObjectToJSON (result); ! json.empty())
  1971. - value = choc::json::parseValue (json);
  1972. - }
  1973. - catch (const std::exception& e)
  1974. - {
  1975. - errorMessage = e.what();
  1976. - }
  1977. -
  1978. - completionHandler (errorMessage, value);
  1979. - CHOC_AUTORELEASE_END
  1980. - });
  1981. - }
  1982. - else
  1983. - {
  1984. - objc::call<void> (webview, "evaluateJavaScript:completionHandler:", s, (id) nullptr);
  1985. - }
  1986. -
  1987. - return true;
  1988. - CHOC_AUTORELEASE_END
  1989. - }
  1990. -
  1991. - static std::string convertNSObjectToJSON (id value)
  1992. - {
  1993. - if (value)
  1994. - {
  1995. - if (id nsData = objc::call<id> (objc::getClass ("NSJSONSerialization"), "dataWithJSONObject:options:error:",
  1996. - value, 12, (id) nullptr))
  1997. - {
  1998. - auto data = objc::call<void*> (nsData, "bytes");
  1999. - auto length = objc::call<unsigned long> (nsData, "length");
  2000. - return std::string (static_cast<const char*> (data), static_cast<size_t> (length));
  2001. - }
  2002. - }
  2003. -
  2004. - return {};
  2005. - }
  2006. -
  2007. -private:
  2008. - id createDelegate()
  2009. - {
  2010. - static DelegateClass dc;
  2011. - return objc::call<id> ((id) dc.delegateClass, "new");
  2012. - }
  2013. -
  2014. - id allocateWebview()
  2015. - {
  2016. - static WebviewClass c;
  2017. - return objc::call<id> ((id) c.webviewClass, "alloc");
  2018. - }
  2019. -
  2020. - static std::string getMessageFromNSError (id nsError)
  2021. - {
  2022. - if (nsError)
  2023. - {
  2024. - if (id userInfo = objc::call<id> (nsError, "userInfo"))
  2025. - if (id message = objc::call<id> (userInfo, "objectForKey:", objc::getNSString ("WKJavaScriptExceptionMessage")))
  2026. - if (auto s = objc::getString (message); ! s.empty())
  2027. - return s;
  2028. -
  2029. - return objc::getString (objc::call<id> (nsError, "localizedDescription"));
  2030. - }
  2031. -
  2032. - return {};
  2033. - }
  2034. -
  2035. - void onResourceRequested (id task)
  2036. - {
  2037. - using namespace choc::objc;
  2038. - CHOC_AUTORELEASE_BEGIN
  2039. -
  2040. - try
  2041. - {
  2042. - id requestUrl = call<id> (call<id> (task, "request"), "URL");
  2043. -
  2044. - auto makeResponse = [&] (auto responseCode, id headerFields)
  2045. - {
  2046. - return call<id> (call<id> (call<id> (getClass ("NSHTTPURLResponse"), "alloc"),
  2047. - "initWithURL:statusCode:HTTPVersion:headerFields:",
  2048. - requestUrl,
  2049. - responseCode,
  2050. - getNSString ("HTTP/1.1"),
  2051. - headerFields),
  2052. - "autorelease");
  2053. - };
  2054. -
  2055. - auto path = objc::getString (call<id> (requestUrl, "path"));
  2056. -
  2057. - if (auto resource = options->fetchResource (path))
  2058. - {
  2059. - const auto& [bytes, mimeType] = *resource;
  2060. -
  2061. - auto contentLength = std::to_string (bytes.size());
  2062. - id headerKeys[] = { getNSString ("Content-Length"), getNSString ("Content-Type"), getNSString ("Cache-Control"), getNSString ("Access-Control-Allow-Origin") };
  2063. - id headerObjects[] = { getNSString (contentLength), getNSString (mimeType), getNSString ("no-store") , getNSString ("*") };
  2064. -
  2065. - id headerFields = call<id> (getClass ("NSDictionary"), "dictionaryWithObjects:forKeys:count:",
  2066. - headerObjects, headerKeys, sizeof (headerObjects) / sizeof (id));
  2067. -
  2068. - call<void> (task, "didReceiveResponse:", makeResponse (200, headerFields));
  2069. -
  2070. - id data = call<id> (getClass ("NSData"), "dataWithBytes:length:", bytes.data(), bytes.size());
  2071. - call<void> (task, "didReceiveData:", data);
  2072. - }
  2073. - else
  2074. - {
  2075. - call<void> (task, "didReceiveResponse:", makeResponse (404, nullptr));
  2076. - }
  2077. -
  2078. - call<void> (task, "didFinish");
  2079. - }
  2080. - catch (...)
  2081. - {
  2082. - id error = call<id> (getClass ("NSError"), "errorWithDomain:code:userInfo:",
  2083. - getNSString ("NSURLErrorDomain"), -1, nullptr);
  2084. -
  2085. - call<void> (task, "didFailWithError:", error);
  2086. - }
  2087. -
  2088. - CHOC_AUTORELEASE_END
  2089. - }
  2090. -
  2091. - BOOL sendAppAction (id self, const char* action)
  2092. - {
  2093. - objc::call<void> (objc::getSharedNSApplication(), "sendAction:to:from:",
  2094. - sel_registerName (action), (id) nullptr, self);
  2095. - return true;
  2096. - }
  2097. -
  2098. - BOOL performKeyEquivalent (id self, id e)
  2099. - {
  2100. - enum
  2101. - {
  2102. - NSEventTypeKeyDown = 10,
  2103. -
  2104. - NSEventModifierFlagShift = 1 << 17,
  2105. - NSEventModifierFlagControl = 1 << 18,
  2106. - NSEventModifierFlagOption = 1 << 19,
  2107. - NSEventModifierFlagCommand = 1 << 20
  2108. - };
  2109. -
  2110. - if (objc::call<int> (e, "type") == NSEventTypeKeyDown)
  2111. - {
  2112. - auto flags = objc::call<int> (e, "modifierFlags") & (NSEventModifierFlagShift | NSEventModifierFlagCommand
  2113. - | NSEventModifierFlagControl | NSEventModifierFlagOption);
  2114. -
  2115. - auto path = objc::getString (objc::call<id> (e, "charactersIgnoringModifiers"));
  2116. -
  2117. - if (flags == NSEventModifierFlagCommand)
  2118. - {
  2119. - if (path == "c") return sendAppAction (self, "copy:");
  2120. - if (path == "x") return sendAppAction (self, "cut:");
  2121. - if (path == "v") return sendAppAction (self, "paste:");
  2122. - if (path == "z") return sendAppAction (self, "undo:");
  2123. - if (path == "a") return sendAppAction (self, "selectAll:");
  2124. - }
  2125. - else if (flags == (NSEventModifierFlagShift | NSEventModifierFlagCommand))
  2126. - {
  2127. - if (path == "Z") return sendAppAction (self, "redo:");
  2128. - }
  2129. - }
  2130. -
  2131. - return false;
  2132. - }
  2133. -
  2134. - void handleError (id error)
  2135. - {
  2136. - static constexpr int NSURLErrorCancelled = -999;
  2137. -
  2138. - if (objc::call<int> (error, "code") == NSURLErrorCancelled)
  2139. - return;
  2140. -
  2141. - setHTML ("<!DOCTYPE html><html><head><title>Error</title></head>"
  2142. - "<body><h2>" + getMessageFromNSError (error) + "</h2></body></html>");
  2143. - }
  2144. -
  2145. - static Pimpl* getPimpl (id self)
  2146. - {
  2147. - return (CHOC_OBJC_CAST_BRIDGED Pimpl*) (objc_getAssociatedObject (self, "choc_webview"));
  2148. - }
  2149. -
  2150. - WebView& owner;
  2151. - // NB: this is a pointer because making it a member forces the alignment of this Pimpl class
  2152. - // to 16, which then conflicts with obj-C pointer alignment...
  2153. - std::unique_ptr<Options> options;
  2154. - id webview = {}, manager = {}, delegate = {};
  2155. - std::string defaultURI;
  2156. -
  2157. - struct WebviewClass
  2158. - {
  2159. - WebviewClass()
  2160. - {
  2161. - webviewClass = choc::objc::createDelegateClass ("WKWebView", "CHOCWebView_");
  2162. -
  2163. - class_addMethod (webviewClass, sel_registerName ("acceptsFirstMouse:"),
  2164. - (IMP) (+[](id self, SEL, id) -> BOOL
  2165. - {
  2166. - if (auto p = getPimpl (self))
  2167. - return p->options->acceptsFirstMouseClick;
  2168. -
  2169. - return false;
  2170. - }), "B@:@");
  2171. -
  2172. - class_addMethod (webviewClass, sel_registerName ("performKeyEquivalent:"),
  2173. - (IMP) (+[](id self, SEL, id e) -> BOOL
  2174. - {
  2175. - if (auto p = getPimpl (self))
  2176. - return p->performKeyEquivalent (self, e);
  2177. -
  2178. - return false;
  2179. - }), "B@:@");
  2180. -
  2181. - objc_registerClassPair (webviewClass);
  2182. - }
  2183. -
  2184. - ~WebviewClass()
  2185. - {
  2186. - // NB: it doesn't seem possible to dispose of this class late enough to avoid a warning on shutdown
  2187. - // about the KVO system still using it, so unfortunately the only option seems to be to let it leak..
  2188. - // objc_disposeClassPair (webviewClass);
  2189. - }
  2190. -
  2191. - Class webviewClass = {};
  2192. - };
  2193. -
  2194. - struct DelegateClass
  2195. - {
  2196. - DelegateClass()
  2197. - {
  2198. - using namespace choc::objc;
  2199. - delegateClass = createDelegateClass ("NSObject", "CHOCWebViewDelegate_");
  2200. -
  2201. - class_addMethod (delegateClass, sel_registerName ("userContentController:didReceiveScriptMessage:"),
  2202. - (IMP) (+[](id self, SEL, id, id msg)
  2203. - {
  2204. - if (auto p = getPimpl (self))
  2205. - p->owner.invokeBinding (objc::getString (call<id> (msg, "body")));
  2206. - }),
  2207. - "v@:@@");
  2208. -
  2209. - class_addMethod (delegateClass, sel_registerName ("webView:startURLSchemeTask:"),
  2210. - (IMP) (+[](id self, SEL, id, id task)
  2211. - {
  2212. - if (auto p = getPimpl (self))
  2213. - p->onResourceRequested (task);
  2214. - }),
  2215. - "v@:@@");
  2216. -
  2217. - class_addMethod (delegateClass, sel_registerName ("webView:didFailProvisionalNavigation:withError:"),
  2218. - (IMP) (+[](id self, SEL, id, id, id error)
  2219. - {
  2220. - if (auto p = getPimpl (self))
  2221. - p->handleError (error);
  2222. - }),
  2223. - "v@:@@@");
  2224. -
  2225. - class_addMethod (delegateClass, sel_registerName ("webView:didFailNavigation:withError:"),
  2226. - (IMP) (+[](id self, SEL, id, id, id error)
  2227. - {
  2228. - if (auto p = getPimpl (self))
  2229. - p->handleError (error);
  2230. - }),
  2231. - "v@:@@@");
  2232. -
  2233. - class_addMethod (delegateClass, sel_registerName ("webView:stopURLSchemeTask:"), (IMP) (+[](id, SEL, id, id) {}), "v@:@@");
  2234. -
  2235. - class_addMethod (delegateClass, sel_registerName ("webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:"),
  2236. - (IMP) (+[](id, SEL, id wkwebview, id params, id /*frame*/, void (^completionHandler)(id))
  2237. - {
  2238. - CHOC_AUTORELEASE_BEGIN
  2239. - id panel = call<id> (getClass ("NSOpenPanel"), "openPanel");
  2240. -
  2241. - auto allowsMultipleSelection = call<BOOL> (params, "allowsMultipleSelection");
  2242. - id allowedFileExtensions = call<id> (params, "_allowedFileExtensions");
  2243. - id window = call<id> (wkwebview, "window");
  2244. -
  2245. - call<void> (panel, "setAllowsMultipleSelection:", allowsMultipleSelection);
  2246. - call<void> (panel, "setAllowedFileTypes:", allowedFileExtensions);
  2247. -
  2248. - call<void> (panel, "beginSheetModalForWindow:completionHandler:", window,
  2249. - ^(long result)
  2250. - {
  2251. - CHOC_AUTORELEASE_BEGIN
  2252. - if (result == 1) // NSModalResponseOK
  2253. - completionHandler (call<id> (panel, "URLs"));
  2254. - else
  2255. - completionHandler (nil);
  2256. - CHOC_AUTORELEASE_END
  2257. - });
  2258. - CHOC_AUTORELEASE_END
  2259. - }), "v@:@@@@");
  2260. -
  2261. - objc_registerClassPair (delegateClass);
  2262. - }
  2263. -
  2264. - ~DelegateClass()
  2265. - {
  2266. - objc_disposeClassPair (delegateClass);
  2267. - }
  2268. -
  2269. - Class delegateClass = {};
  2270. - };
  2271. -
  2272. - static constexpr long WKUserScriptInjectionTimeAtDocumentStart = 0;
  2273. -
  2274. - // Including CodeGraphics.h can create all kinds of messy C/C++ symbol clashes
  2275. - // with other headers, but all we actually need are these coordinate structs:
  2276. - #if defined (__LP64__) && __LP64__
  2277. - using CGFloat = double;
  2278. - #else
  2279. - using CGFloat = float;
  2280. - #endif
  2281. -
  2282. - struct CGPoint { CGFloat x = 0, y = 0; };
  2283. - struct CGSize { CGFloat width = 0, height = 0; };
  2284. - struct CGRect { CGPoint origin; CGSize size; };
  2285. -};
  2286. -
  2287. -//==============================================================================
  2288. -#elif CHOC_WINDOWS
  2289. -
  2290. -#include "../platform/choc_DynamicLibrary.h"
  2291. -
  2292. -// If you want to supply your own mechanism for finding the Microsoft
  2293. -// Webview2Loader.dll file, then define the CHOC_FIND_WEBVIEW2LOADER_DLL
  2294. -// macro, which must evaluate to a choc::file::DynamicLibrary object
  2295. -// which points to the DLL.
  2296. -#ifndef CHOC_FIND_WEBVIEW2LOADER_DLL
  2297. - #define CHOC_USE_INTERNAL_WEBVIEW_DLL 1
  2298. - #define CHOC_FIND_WEBVIEW2LOADER_DLL choc::ui::getWebview2LoaderDLL()
  2299. - #include "../platform/choc_MemoryDLL.h"
  2300. - namespace choc::ui
  2301. - {
  2302. - using WebViewDLL = choc::memory::MemoryDLL;
  2303. - static WebViewDLL getWebview2LoaderDLL();
  2304. - }
  2305. -#else
  2306. - namespace choc::ui
  2307. - {
  2308. - using WebViewDLL = choc::file::DynamicLibrary;
  2309. - }
  2310. -#endif
  2311. -
  2312. +#include "choc_DynamicLibrary.h"
  2313. +#include "choc_MemoryDLL.h"
  2314. #include "choc_DesktopWindow.h"
  2315. +START_NAMESPACE_DISTRHO
  2316. +static MemoryDLL getWebview2LoaderDLL();
  2317. +END_NAMESPACE_DISTRHO
  2318. +
  2319. #ifndef __webview2_h__
  2320. #define __webview2_h__
  2321. @@ -1268,8 +473,7 @@ public:
  2322. #endif // __webview2_h__
  2323. -namespace choc::ui
  2324. -{
  2325. +START_NAMESPACE_DISTRHO
  2326. //==============================================================================
  2327. struct WebView::Pimpl
  2328. @@ -1277,10 +481,7 @@ struct WebView::Pimpl
  2329. Pimpl (WebView& v, const Options& opts)
  2330. : owner (v), options (opts)
  2331. {
  2332. - // You cam define this macro to provide a custom way of getting a
  2333. - // choc::file::DynamicLibrary that contains the redistributable
  2334. - // Microsoft WebView2Loader.dll
  2335. - webviewDLL = CHOC_FIND_WEBVIEW2LOADER_DLL;
  2336. + webviewDLL = getWebview2LoaderDLL();
  2337. if (! webviewDLL)
  2338. return;
  2339. @@ -1290,9 +491,6 @@ struct WebView::Pimpl
  2340. if (hwnd.hwnd == nullptr)
  2341. return;
  2342. - defaultURI = getURIHome (options);
  2343. - setHTMLURI = defaultURI + "getHTMLInternal";
  2344. -
  2345. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) this);
  2346. if (createEmbeddedWebView())
  2347. @@ -1304,8 +502,6 @@ struct WebView::Pimpl
  2348. ~Pimpl()
  2349. {
  2350. - deletionChecker->deleted = true;
  2351. -
  2352. if (coreWebView != nullptr)
  2353. {
  2354. coreWebView->Release();
  2355. @@ -1327,52 +523,30 @@ struct WebView::Pimpl
  2356. hwnd.reset();
  2357. }
  2358. - static constexpr const char* postMessageFn = "window.chrome.webview.postMessage";
  2359. -
  2360. bool loadedOK() const { return coreWebView != nullptr; }
  2361. void* getViewHandle() const { return (void*) hwnd.hwnd; }
  2362. - std::shared_ptr<DeletionChecker> deletionChecker { std::make_shared<DeletionChecker>() };
  2363. -
  2364. bool navigate (const std::string& url)
  2365. {
  2366. - if (url.empty())
  2367. - return navigate (defaultURI);
  2368. -
  2369. - CHOC_ASSERT (coreWebView != nullptr);
  2370. + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
  2371. return coreWebView->Navigate (createUTF16StringFromUTF8 (url).c_str()) == S_OK;
  2372. }
  2373. bool addInitScript (const std::string& script)
  2374. {
  2375. - CHOC_ASSERT (coreWebView != nullptr);
  2376. + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
  2377. return coreWebView->AddScriptToExecuteOnDocumentCreated (createUTF16StringFromUTF8 (script).c_str(), nullptr) == S_OK;
  2378. }
  2379. - bool evaluateJavascript (const std::string& script, CompletionHandler&& ch)
  2380. - {
  2381. - if (coreWebView == nullptr)
  2382. - return false;
  2383. -
  2384. - COMPtr<ExecuteScriptCompletedCallback> callback (ch ? new ExecuteScriptCompletedCallback (std::move (ch))
  2385. - : nullptr);
  2386. -
  2387. - return coreWebView->ExecuteScript (createUTF16StringFromUTF8 (script).c_str(), callback) == S_OK;
  2388. - }
  2389. -
  2390. - bool setHTML (const std::string& html)
  2391. + bool evaluateJavascript (const std::string& script)
  2392. {
  2393. - CHOC_ASSERT (coreWebView != nullptr);
  2394. - pageHTML = { html, "text/html" };
  2395. - navigate (setHTMLURI);
  2396. - return true;
  2397. + DISTRHO_SAFE_ASSERT_RETURN (coreWebView != nullptr, false);
  2398. + return coreWebView->ExecuteScript (createUTF16StringFromUTF8 (script).c_str(), nullptr) == S_OK;
  2399. }
  2400. private:
  2401. WindowClass windowClass { L"CHOCWebView", (WNDPROC) wndProc };
  2402. HWNDHolder hwnd;
  2403. - std::string defaultURI, setHTMLURI;
  2404. - WebView::Options::Resource pageHTML;
  2405. //==============================================================================
  2406. template <typename Type>
  2407. @@ -1404,7 +578,7 @@ private:
  2408. return message;
  2409. }
  2410. - return choc::text::createHexString (hr);
  2411. + return createHexString (hr);
  2412. }
  2413. static Pimpl* getPimpl (HWND h) { return (Pimpl*) GetWindowLongPtr (h, GWLP_USERDATA); }
  2414. @@ -1463,15 +637,9 @@ private:
  2415. if (coreWebView == nullptr)
  2416. return false;
  2417. - const auto wildcardFilter = createUTF16StringFromUTF8 (defaultURI + "*");
  2418. - coreWebView->AddWebResourceRequestedFilter (wildcardFilter.c_str(), COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL);
  2419. -
  2420. EventRegistrationToken token;
  2421. coreWebView->add_WebResourceRequested (handler, std::addressof (token));
  2422. - if (options.fetchResource)
  2423. - navigate ({});
  2424. -
  2425. ICoreWebView2Settings* settings = nullptr;
  2426. if (coreWebView->get_Settings (std::addressof (settings)) == S_OK
  2427. @@ -1526,14 +694,6 @@ private:
  2428. webviewInitialising.clear();
  2429. }
  2430. - std::optional<WebView::Options::Resource> fetchResourceOrPageHTML (const std::string& uri)
  2431. - {
  2432. - if (uri == setHTMLURI)
  2433. - return pageHTML;
  2434. -
  2435. - return options.fetchResource (uri.substr (defaultURI.size() - 1));
  2436. - }
  2437. -
  2438. HRESULT onResourceRequested (ICoreWebView2WebResourceRequestedEventArgs* args)
  2439. {
  2440. struct ScopedExit
  2441. @@ -1579,42 +739,8 @@ private:
  2442. ICoreWebView2WebResourceResponse* response = {};
  2443. ScopedExit cleanupResponse (makeCleanupIUnknown (response));
  2444. - if (const auto resource = fetchResourceOrPageHTML (createUTF8FromUTF16 (uri)))
  2445. - {
  2446. - const auto makeMemoryStream = [](const auto* data, auto length) -> IStream*
  2447. - {
  2448. - choc::file::DynamicLibrary lib ("shlwapi.dll");
  2449. - using SHCreateMemStreamFn = IStream*(__stdcall *)(const BYTE*, UINT);
  2450. - auto fn = reinterpret_cast<SHCreateMemStreamFn> (lib.findFunction ("SHCreateMemStream"));
  2451. - return fn ? fn (data, length) : nullptr;
  2452. - };
  2453. -
  2454. - auto* stream = makeMemoryStream (reinterpret_cast<const BYTE*> (resource->data.data()),
  2455. - static_cast<UINT> (resource->data.size()));
  2456. -
  2457. - if (stream == nullptr)
  2458. - return E_FAIL;
  2459. -
  2460. - ScopedExit cleanupStream (makeCleanupIUnknown (stream));
  2461. -
  2462. - std::vector<std::string> headers;
  2463. - headers.emplace_back ("Content-Type: " + resource->mimeType);
  2464. - headers.emplace_back ("Cache-Control: no-store");
  2465. - headers.emplace_back ("Access-Control-Allow-Origin: *");
  2466. -
  2467. - if (! options.customUserAgent.empty())
  2468. - headers.emplace_back ("User-Agent: " + options.customUserAgent);
  2469. -
  2470. - const auto headerString = createUTF16StringFromUTF8 (choc::text::joinStrings (headers, "\n"));
  2471. -
  2472. - if (coreWebViewEnvironment->CreateWebResourceResponse (stream, 200, L"OK", headerString.c_str(), std::addressof (response)) != S_OK)
  2473. - return E_FAIL;
  2474. - }
  2475. - else
  2476. - {
  2477. - if (coreWebViewEnvironment->CreateWebResourceResponse (nullptr, 404, L"Not Found", nullptr, std::addressof (response)) != S_OK)
  2478. - return E_FAIL;
  2479. - }
  2480. + if (coreWebViewEnvironment->CreateWebResourceResponse (nullptr, 404, L"Not Found", nullptr, std::addressof (response)) != S_OK)
  2481. + return E_FAIL;
  2482. if (args->put_Response (response) != S_OK)
  2483. return E_FAIL;
  2484. @@ -1708,58 +834,9 @@ private:
  2485. std::atomic<ULONG> refCount { 0 };
  2486. };
  2487. - //==============================================================================
  2488. - struct ExecuteScriptCompletedCallback : public ICoreWebView2ExecuteScriptCompletedHandler
  2489. - {
  2490. - ExecuteScriptCompletedCallback (CompletionHandler&& cb) : callback (std::move (cb)) {}
  2491. - virtual ~ExecuteScriptCompletedCallback() {}
  2492. -
  2493. - HRESULT STDMETHODCALLTYPE QueryInterface (REFIID refID, void** result) override
  2494. - {
  2495. - if (refID == IID { 0x49511172, 0xcc67, 0x4bca, { 0x99, 0x23, 0x13, 0x71, 0x12, 0xf4, 0xc4, 0xcc } }
  2496. - || refID == IID_IUnknown)
  2497. - {
  2498. - *result = this;
  2499. - AddRef();
  2500. - return S_OK;
  2501. - }
  2502. -
  2503. - *result = nullptr;
  2504. - return E_NOINTERFACE;
  2505. - }
  2506. -
  2507. - ULONG STDMETHODCALLTYPE AddRef() override { return ++refCount; }
  2508. - ULONG STDMETHODCALLTYPE Release() override { auto newCount = --refCount; if (newCount == 0) delete this; return newCount; }
  2509. -
  2510. - HRESULT STDMETHODCALLTYPE Invoke (HRESULT hr, LPCWSTR resultJSON) override
  2511. - {
  2512. - std::string errorMessage = getMessageFromHRESULT (hr);
  2513. - choc::value::Value value;
  2514. -
  2515. - if (resultJSON != nullptr)
  2516. - {
  2517. - try
  2518. - {
  2519. - if (auto json = createUTF8FromUTF16 (std::wstring (resultJSON)); ! json.empty())
  2520. - value = choc::json::parseValue (json);
  2521. - }
  2522. - catch (const std::exception& e)
  2523. - {
  2524. - errorMessage = e.what();
  2525. - }
  2526. - }
  2527. -
  2528. - callback (errorMessage, value);
  2529. - return S_OK;
  2530. - }
  2531. -
  2532. - CompletionHandler callback;
  2533. - std::atomic<ULONG> refCount { 0 };
  2534. - };
  2535. -
  2536. //==============================================================================
  2537. WebView& owner;
  2538. - WebViewDLL webviewDLL;
  2539. + MemoryDLL webviewDLL;
  2540. Options options;
  2541. ICoreWebView2Environment* coreWebViewEnvironment = nullptr;
  2542. ICoreWebView2* coreWebView = nullptr;
  2543. @@ -1793,15 +870,6 @@ private:
  2544. }
  2545. };
  2546. -} // namespace choc::ui
  2547. -
  2548. -#else
  2549. - #error "choc WebView only supports OSX, Windows or Linux!"
  2550. -#endif
  2551. -
  2552. -namespace choc::ui
  2553. -{
  2554. -
  2555. //==============================================================================
  2556. inline WebView::WebView() : WebView (Options()) {}
  2557. @@ -1820,137 +888,30 @@ inline WebView::~WebView()
  2558. inline bool WebView::loadedOK() const { return pimpl != nullptr; }
  2559. inline bool WebView::navigate (const std::string& url) { return pimpl != nullptr && pimpl->navigate (url); }
  2560. -inline bool WebView::setHTML (const std::string& html) { return pimpl != nullptr && pimpl->setHTML (html); }
  2561. inline bool WebView::addInitScript (const std::string& script) { return pimpl != nullptr && pimpl->addInitScript (script); }
  2562. -inline bool WebView::evaluateJavascript (const std::string& script, CompletionHandler completionHandler)
  2563. +inline bool WebView::evaluateJavascript (const std::string& script)
  2564. {
  2565. - return pimpl != nullptr && pimpl->evaluateJavascript (script, std::move (completionHandler));
  2566. + return pimpl != nullptr && pimpl->evaluateJavascript (script);
  2567. }
  2568. inline void* WebView::getViewHandle() const { return pimpl != nullptr ? pimpl->getViewHandle() : nullptr; }
  2569. -inline bool WebView::bind (const std::string& functionName, CallbackFn&& fn)
  2570. +inline bool WebView::bind (CallbackFn&& fn)
  2571. {
  2572. if (pimpl == nullptr)
  2573. return false;
  2574. - static constexpr std::string_view scriptTemplate = R"((function() {
  2575. -const fnBinding = window._fnBindings = (window._fnBindings || { messageID: 1 });
  2576. -
  2577. -window["FUNCTION_NAME"] = function()
  2578. -{
  2579. - const messageID = ++fnBinding.messageID;
  2580. - const promise = new Promise((resolve, reject) => { fnBinding[messageID] = { resolve, reject }; });
  2581. -
  2582. - const args = JSON.stringify ({ id: messageID,
  2583. - fn: "FUNCTION_NAME",
  2584. - params: Array.prototype.slice.call (arguments)
  2585. - },
  2586. - (key, value) => typeof value === 'bigint' ? value.toString() : value);
  2587. - INVOKE_BINDING (args);
  2588. - return promise;
  2589. -}
  2590. -})())";
  2591. -
  2592. - auto script = choc::text::replace (scriptTemplate, "FUNCTION_NAME", functionName,
  2593. - "INVOKE_BINDING", Pimpl::postMessageFn);
  2594. -
  2595. - if (addInitScript (script)
  2596. - && evaluateJavascript (script))
  2597. - {
  2598. - bindings[functionName] = std::move (fn);
  2599. - return true;
  2600. - }
  2601. -
  2602. - return false;
  2603. -}
  2604. -
  2605. -inline bool WebView::unbind (const std::string& functionName)
  2606. -{
  2607. - if (auto found = bindings.find (functionName); found != bindings.end())
  2608. - {
  2609. - evaluateJavascript ("delete window[\"" + functionName + "\"];");
  2610. - bindings.erase (found);
  2611. - return true;
  2612. - }
  2613. -
  2614. - return false;
  2615. + binding = std::move (fn);
  2616. + return true;
  2617. }
  2618. inline void WebView::invokeBinding (const std::string& msg)
  2619. {
  2620. - try
  2621. - {
  2622. - auto json = choc::json::parse (msg);
  2623. - auto b = bindings.find (std::string (json["fn"].getString()));
  2624. - auto callbackID = json["id"].getWithDefault<int64_t>(0);
  2625. -
  2626. - if (callbackID == 0 || b == bindings.end())
  2627. - return;
  2628. -
  2629. - auto callbackItem = "window._fnBindings[" + std::to_string (callbackID) + "]";
  2630. -
  2631. - try
  2632. - {
  2633. - auto deletionChecker = pimpl->deletionChecker;
  2634. - auto result = b->second (json["params"]);
  2635. -
  2636. - if (deletionChecker->deleted) // in case the WebView was deleted during the callback
  2637. - return;
  2638. -
  2639. - auto call = callbackItem + ".resolve(" + choc::json::toString (result) + "); delete " + callbackItem + ";";
  2640. - evaluateJavascript (call);
  2641. - return;
  2642. - }
  2643. - catch (const std::exception&)
  2644. - {}
  2645. -
  2646. - auto call = callbackItem + ".reject(); delete " + callbackItem + ";";
  2647. - evaluateJavascript (call);
  2648. - }
  2649. - catch (const std::exception&)
  2650. - {}
  2651. -}
  2652. -
  2653. -inline std::string WebView::getURIHome (const Options& options)
  2654. -{
  2655. - if (! options.customSchemeURI.empty())
  2656. - {
  2657. - if (choc::text::endsWith (options.customSchemeURI, "/"))
  2658. - return options.customSchemeURI;
  2659. -
  2660. - return options.customSchemeURI + "/";
  2661. - }
  2662. -
  2663. - #if CHOC_WINDOWS
  2664. - return "https://choc.localhost/";
  2665. - #else
  2666. - return "choc://choc.choc/";
  2667. - #endif
  2668. -}
  2669. -
  2670. -inline std::string WebView::getURIScheme (const Options& options)
  2671. -{
  2672. - auto uri = getURIHome (options);
  2673. - auto colon = uri.find (":");
  2674. - CHOC_ASSERT (colon != std::string::npos && colon != 0); // need to provide a valid URI with a scheme at the start.
  2675. - return uri.substr (0, colon);
  2676. -}
  2677. -
  2678. -inline WebView::Options::Resource::Resource (std::string_view content, std::string mime)
  2679. -{
  2680. - if (! content.empty())
  2681. - {
  2682. - auto src = content.data();
  2683. - data.insert (data.end(), src, src + content.length());
  2684. - }
  2685. -
  2686. - mimeType = std::move (mime);
  2687. + binding(msg);
  2688. }
  2689. -} // namespace choc::ui
  2690. -
  2691. +END_NAMESPACE_DISTRHO
  2692. //==============================================================================
  2693. //==============================================================================
  2694. @@ -2035,7 +996,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  2695. )")
  2696. #endif
  2697. -inline choc::ui::WebViewDLL choc::ui::getWebview2LoaderDLL()
  2698. +inline DISTRHO_NAMESPACE::MemoryDLL DISTRHO_NAMESPACE::getWebview2LoaderDLL()
  2699. {
  2700. // This is version 1.0.2365.46
  2701. @@ -5798,7 +4759,7 @@ inline choc::ui::WebViewDLL choc::ui::getWebview2LoaderDLL()
  2702. };
  2703. #endif
  2704. - return choc::memory::MemoryDLL (dllData, sizeof (dllData));
  2705. + return DISTRHO_NAMESPACE::MemoryDLL (dllData, sizeof (dllData));
  2706. }
  2707. #endif