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.

751 lines
23KB

  1. /*
  2. * DISTRHO Cardinal Plugin
  3. * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 3 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include <app/Scene.hpp>
  18. #include <context.hpp>
  19. #include <helpers.hpp>
  20. #include <patch.hpp>
  21. #include <settings.hpp>
  22. #include <string.hpp>
  23. #include <system.hpp>
  24. #include <ui/Button.hpp>
  25. #include <ui/MenuItem.hpp>
  26. #include <ui/MenuSeparator.hpp>
  27. #include <window/Window.hpp>
  28. #ifdef NDEBUG
  29. # undef DEBUG
  30. #endif
  31. #include <Application.hpp>
  32. #include "AsyncDialog.hpp"
  33. #include "PluginContext.hpp"
  34. #include "WindowParameters.hpp"
  35. #include "ResizeHandle.hpp"
  36. GLFWAPI int glfwGetKeyScancode(int) { return 0; }
  37. GLFWAPI const char* glfwGetClipboardString(GLFWwindow*)
  38. {
  39. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  40. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, nullptr);
  41. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, nullptr);
  42. const char* mimeType = nullptr;
  43. size_t dataSize = 0;
  44. if (const void* const clipboard = context->ui->getClipboard(mimeType, dataSize))
  45. {
  46. if (mimeType == nullptr || std::strcmp(mimeType, "text/plain") != 0)
  47. return nullptr;
  48. return static_cast<const char*>(clipboard);
  49. }
  50. return nullptr;
  51. }
  52. GLFWAPI void glfwSetClipboardString(GLFWwindow*, const char* const text)
  53. {
  54. DISTRHO_SAFE_ASSERT_RETURN(text != nullptr,);
  55. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  56. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr,);
  57. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr,);
  58. context->ui->setClipboard(nullptr, text, std::strlen(text)+1);
  59. }
  60. GLFWAPI double glfwGetTime(void)
  61. {
  62. CardinalPluginContext* const context = static_cast<CardinalPluginContext*>(APP);
  63. DISTRHO_SAFE_ASSERT_RETURN(context != nullptr, 0.0);
  64. DISTRHO_SAFE_ASSERT_RETURN(context->ui != nullptr, 0.0);
  65. return context->ui->getApp().getTime();
  66. }
  67. GLFWAPI const char* glfwGetKeyName(const int key, int)
  68. {
  69. switch (key)
  70. {
  71. case '\"': return "\"";
  72. case '\'': return "\'";
  73. case '\\': return "\\";
  74. case ' ': return " ";
  75. case '!': return "!";
  76. case '#': return "#";
  77. case '$': return "$";
  78. case '%': return "%";
  79. case '&': return "&";
  80. case '(': return "(";
  81. case ')': return ")";
  82. case '*': return "*";
  83. case '+': return "+";
  84. case ',': return ",";
  85. case '-': return "-";
  86. case '.': return ".";
  87. case '/': return "/";
  88. case '0': return "0";
  89. case '1': return "1";
  90. case '2': return "2";
  91. case '3': return "3";
  92. case '4': return "4";
  93. case '5': return "5";
  94. case '6': return "6";
  95. case '7': return "7";
  96. case '8': return "8";
  97. case '9': return "9";
  98. case ':': return ":";
  99. case ';': return ";";
  100. case '<': return "<";
  101. case '=': return "=";
  102. case '>': return ">";
  103. case '?': return "?";
  104. case '@': return "@";
  105. case 'A': return "A";
  106. case 'B': return "B";
  107. case 'C': return "C";
  108. case 'D': return "D";
  109. case 'E': return "E";
  110. case 'F': return "F";
  111. case 'G': return "G";
  112. case 'H': return "H";
  113. case 'I': return "I";
  114. case 'J': return "J";
  115. case 'K': return "K";
  116. case 'L': return "L";
  117. case 'M': return "M";
  118. case 'N': return "N";
  119. case 'O': return "O";
  120. case 'P': return "P";
  121. case 'Q': return "Q";
  122. case 'R': return "R";
  123. case 'S': return "S";
  124. case 'T': return "T";
  125. case 'U': return "U";
  126. case 'V': return "V";
  127. case 'W': return "W";
  128. case 'X': return "X";
  129. case 'Y': return "Y";
  130. case 'Z': return "Z";
  131. case '[': return "[";
  132. case ']': return "]";
  133. case '^': return "^";
  134. case '_': return "_";
  135. case '`': return "`";
  136. case 'a': return "a";
  137. case 'b': return "b";
  138. case 'c': return "c";
  139. case 'd': return "d";
  140. case 'e': return "e";
  141. case 'f': return "f";
  142. case 'g': return "g";
  143. case 'h': return "h";
  144. case 'i': return "i";
  145. case 'j': return "j";
  146. case 'k': return "k";
  147. case 'l': return "l";
  148. case 'm': return "m";
  149. case 'n': return "n";
  150. case 'o': return "o";
  151. case 'p': return "p";
  152. case 'q': return "q";
  153. case 'r': return "r";
  154. case 's': return "s";
  155. case 't': return "t";
  156. case 'u': return "u";
  157. case 'v': return "v";
  158. case 'w': return "w";
  159. case 'x': return "x";
  160. case 'y': return "y";
  161. case 'z': return "z";
  162. default: return nullptr;
  163. }
  164. }
  165. namespace rack {
  166. namespace app {
  167. widget::Widget* createMenuBar(bool isStandalone);
  168. }
  169. namespace window {
  170. void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui);
  171. void WindowSetMods(Window* window, int mods);
  172. }
  173. }
  174. START_NAMESPACE_DISTRHO
  175. // -----------------------------------------------------------------------------------------------------------
  176. bool CardinalPluginContext::addIdleCallback(IdleCallback* const cb)
  177. {
  178. if (ui == nullptr)
  179. return false;
  180. ui->addIdleCallback(cb);
  181. return true;
  182. }
  183. void CardinalPluginContext::removeIdleCallback(IdleCallback* const cb)
  184. {
  185. if (ui == nullptr)
  186. return;
  187. ui->removeIdleCallback(cb);
  188. }
  189. void handleHostParameterDrag(CardinalPluginContext* pcontext, uint index, bool started)
  190. {
  191. DISTRHO_SAFE_ASSERT_RETURN(pcontext->ui != nullptr,);
  192. if (started)
  193. {
  194. pcontext->ui->editParameter(index, true);
  195. pcontext->ui->setParameterValue(index, pcontext->parameters[index]);
  196. }
  197. else
  198. {
  199. pcontext->ui->editParameter(index, false);
  200. }
  201. }
  202. // -----------------------------------------------------------------------------------------------------------
  203. class CardinalUI : public CardinalBaseUI,
  204. public WindowParametersCallback
  205. {
  206. rack::math::Vec fLastMousePos;
  207. ResizeHandle fResizeHandle;
  208. WindowParameters fWindowParameters;
  209. struct ScopedContext {
  210. CardinalPluginContext* const context;
  211. ScopedContext(CardinalUI* const ui)
  212. : context(ui->context)
  213. {
  214. rack::contextSet(context);
  215. WindowParametersRestore(context->window);
  216. }
  217. ScopedContext(CardinalUI* const ui, const int mods)
  218. : context(ui->context)
  219. {
  220. rack::contextSet(context);
  221. rack::window::WindowSetMods(context->window, mods);
  222. WindowParametersRestore(context->window);
  223. }
  224. ~ScopedContext()
  225. {
  226. if (context->window != nullptr)
  227. WindowParametersSave(context->window);
  228. rack::contextSet(nullptr);
  229. }
  230. };
  231. public:
  232. CardinalUI()
  233. : CardinalBaseUI(1228, 666),
  234. fResizeHandle(this)
  235. {
  236. Window& window(getWindow());
  237. window.setIgnoringKeyRepeat(true);
  238. context->nativeWindowId = window.getNativeWindowHandle();
  239. if (isResizable())
  240. fResizeHandle.hide();
  241. const double scaleFactor = getScaleFactor();
  242. if (scaleFactor != 1)
  243. setSize(1228 * scaleFactor, 666 * scaleFactor);
  244. rack::contextSet(context);
  245. rack::window::WindowSetPluginUI(context->window, this);
  246. if (context->scene->menuBar != nullptr)
  247. context->scene->removeChild(context->scene->menuBar);
  248. context->scene->menuBar = rack::app::createMenuBar(getApp().isStandalone());
  249. context->scene->addChildBelow(context->scene->menuBar, context->scene->rackScroll);
  250. // hide "Browse VCV Library" button
  251. rack::widget::Widget* const browser = context->scene->browser->children.back();
  252. rack::widget::Widget* const headerLayout = browser->children.front();
  253. rack::widget::Widget* const libraryButton = headerLayout->children.back();
  254. libraryButton->hide();
  255. context->window->step();
  256. rack::contextSet(nullptr);
  257. WindowParametersSetCallback(context->window, this);
  258. }
  259. ~CardinalUI() override
  260. {
  261. rack::contextSet(context);
  262. context->nativeWindowId = 0;
  263. rack::widget::Widget* const menuBar = context->scene->menuBar;
  264. context->scene->menuBar = nullptr;
  265. context->scene->removeChild(menuBar);
  266. rack::window::WindowSetPluginUI(context->window, nullptr);
  267. rack::contextSet(nullptr);
  268. }
  269. void onNanoDisplay() override
  270. {
  271. const ScopedContext sc(this);
  272. context->window->step();
  273. }
  274. void uiIdle() override
  275. {
  276. if (filebrowserhandle != nullptr && fileBrowserIdle(filebrowserhandle))
  277. {
  278. {
  279. const char* const path = fileBrowserGetPath(filebrowserhandle);
  280. const ScopedContext sc(this);
  281. filebrowseraction(path != nullptr ? strdup(path) : nullptr);
  282. }
  283. fileBrowserClose(filebrowserhandle);
  284. filebrowseraction = nullptr;
  285. filebrowserhandle = nullptr;
  286. }
  287. repaint();
  288. }
  289. void WindowParametersChanged(const WindowParameterList param, float value) override
  290. {
  291. float mult = 1.0f;
  292. switch (param)
  293. {
  294. case kWindowParameterShowTooltips:
  295. fWindowParameters.tooltips = value > 0.5f;
  296. break;
  297. case kWindowParameterCableOpacity:
  298. mult = 100.0f;
  299. fWindowParameters.cableOpacity = value;
  300. break;
  301. case kWindowParameterCableTension:
  302. mult = 100.0f;
  303. fWindowParameters.cableTension = value;
  304. break;
  305. case kWindowParameterRackBrightness:
  306. mult = 100.0f;
  307. fWindowParameters.rackBrightness = value;
  308. break;
  309. case kWindowParameterHaloBrightness:
  310. mult = 100.0f;
  311. fWindowParameters.haloBrightness = value;
  312. break;
  313. case kWindowParameterKnobMode:
  314. switch (static_cast<int>(value + 0.5f))
  315. {
  316. case rack::settings::KNOB_MODE_LINEAR:
  317. value = 0;
  318. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  319. break;
  320. case rack::settings::KNOB_MODE_ROTARY_ABSOLUTE:
  321. value = 1;
  322. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  323. break;
  324. case rack::settings::KNOB_MODE_ROTARY_RELATIVE:
  325. value = 2;
  326. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  327. break;
  328. }
  329. break;
  330. case kWindowParameterWheelKnobControl:
  331. fWindowParameters.knobScroll = value > 0.5f;
  332. break;
  333. case kWindowParameterWheelSensitivity:
  334. mult = 1000.0f;
  335. fWindowParameters.knobScrollSensitivity = value;
  336. break;
  337. case kWindowParameterLockModulePositions:
  338. fWindowParameters.lockModules = value > 0.5f;
  339. break;
  340. default:
  341. return;
  342. }
  343. setParameterValue(kModuleParameters + param, value * mult);
  344. }
  345. protected:
  346. /* --------------------------------------------------------------------------------------------------------
  347. * DSP/Plugin Callbacks */
  348. /**
  349. A parameter has changed on the plugin side.
  350. This is called by the host to inform the UI about parameter changes.
  351. */
  352. void parameterChanged(const uint32_t index, const float value) override
  353. {
  354. if (index < kModuleParameters)
  355. return;
  356. switch (index - kModuleParameters)
  357. {
  358. case kWindowParameterShowTooltips:
  359. fWindowParameters.tooltips = value > 0.5f;
  360. break;
  361. case kWindowParameterCableOpacity:
  362. fWindowParameters.cableOpacity = value / 100.0f;
  363. break;
  364. case kWindowParameterCableTension:
  365. fWindowParameters.cableTension = value / 100.0f;
  366. break;
  367. case kWindowParameterRackBrightness:
  368. fWindowParameters.rackBrightness = value / 100.0f;
  369. break;
  370. case kWindowParameterHaloBrightness:
  371. fWindowParameters.haloBrightness = value / 100.0f;
  372. break;
  373. case kWindowParameterKnobMode:
  374. switch (static_cast<int>(value + 0.5f))
  375. {
  376. case 0:
  377. fWindowParameters.knobMode = rack::settings::KNOB_MODE_LINEAR;
  378. break;
  379. case 1:
  380. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_ABSOLUTE;
  381. break;
  382. case 2:
  383. fWindowParameters.knobMode = rack::settings::KNOB_MODE_ROTARY_RELATIVE;
  384. break;
  385. }
  386. break;
  387. case kWindowParameterWheelKnobControl:
  388. fWindowParameters.knobScroll = value > 0.5f;
  389. break;
  390. case kWindowParameterWheelSensitivity:
  391. fWindowParameters.knobScrollSensitivity = value / 1000.0f;
  392. break;
  393. case kWindowParameterLockModulePositions:
  394. fWindowParameters.lockModules = value > 0.5f;
  395. break;
  396. default:
  397. return;
  398. }
  399. WindowParametersSetValues(context->window, fWindowParameters);
  400. }
  401. void stateChanged(const char* key, const char* value) override
  402. {
  403. if (std::strcmp(key, "windowSize") != 0)
  404. return;
  405. int width = 0;
  406. int height = 0;
  407. std::sscanf(value, "%i:%i", &width, &height);
  408. if (width > 0 && height > 0)
  409. {
  410. const double scaleFactor = getScaleFactor();
  411. setSize(width * scaleFactor, height * scaleFactor);
  412. }
  413. }
  414. // -------------------------------------------------------------------------------------------------------
  415. static int glfwMods(const uint mod) noexcept
  416. {
  417. int mods = 0;
  418. if (mod & kModifierControl)
  419. mods |= GLFW_MOD_CONTROL;
  420. if (mod & kModifierShift)
  421. mods |= GLFW_MOD_SHIFT;
  422. if (mod & kModifierAlt)
  423. mods |= GLFW_MOD_ALT;
  424. if (mod & kModifierSuper)
  425. mods |= GLFW_MOD_SUPER;
  426. /*
  427. if (glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)
  428. mods |= GLFW_MOD_SHIFT;
  429. if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)
  430. mods |= GLFW_MOD_CONTROL;
  431. if (glfwGetKey(win, GLFW_KEY_LEFT_ALT) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)
  432. mods |= GLFW_MOD_ALT;
  433. if (glfwGetKey(win, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)
  434. mods |= GLFW_MOD_SUPER;
  435. */
  436. return mods;
  437. }
  438. bool onMouse(const MouseEvent& ev) override
  439. {
  440. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  441. const int mods = glfwMods(ev.mod);
  442. int button;
  443. switch (ev.button)
  444. {
  445. case 1: button = GLFW_MOUSE_BUTTON_LEFT; break;
  446. #ifdef DISTRHO_OS_MAC
  447. case 2: button = GLFW_MOUSE_BUTTON_RIGHT; break;
  448. case 3: button = GLFW_MOUSE_BUTTON_MIDDLE; break;
  449. #else
  450. case 2: button = GLFW_MOUSE_BUTTON_MIDDLE; break;
  451. case 3: button = GLFW_MOUSE_BUTTON_RIGHT; break;
  452. #endif
  453. default:
  454. button = ev.button;
  455. break;
  456. }
  457. /*
  458. #if defined ARCH_MAC
  459. // Remap Ctrl-left click to right click on Mac
  460. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == GLFW_MOD_CONTROL) {
  461. button = GLFW_MOUSE_BUTTON_RIGHT;
  462. mods &= ~GLFW_MOD_CONTROL;
  463. }
  464. // Remap Ctrl-shift-left click to middle click on Mac
  465. if (button == GLFW_MOUSE_BUTTON_LEFT && (mods & RACK_MOD_MASK) == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT)) {
  466. button = GLFW_MOUSE_BUTTON_MIDDLE;
  467. mods &= ~(GLFW_MOD_CONTROL | GLFW_MOD_SHIFT);
  468. }
  469. #endif
  470. */
  471. const ScopedContext sc(this, mods);
  472. return context->event->handleButton(fLastMousePos, button, action, mods);
  473. }
  474. bool onMotion(const MotionEvent& ev) override
  475. {
  476. const rack::math::Vec mousePos = rack::math::Vec(ev.pos.getX(), ev.pos.getY()).div(getScaleFactor()).round();
  477. const rack::math::Vec mouseDelta = mousePos.minus(fLastMousePos);
  478. fLastMousePos = mousePos;
  479. const ScopedContext sc(this, glfwMods(ev.mod));
  480. return context->event->handleHover(mousePos, mouseDelta);
  481. }
  482. bool onScroll(const ScrollEvent& ev) override
  483. {
  484. rack::math::Vec scrollDelta = rack::math::Vec(ev.delta.getX(), ev.delta.getY());
  485. #ifdef DISTRHO_OS_MAC
  486. scrollDelta = scrollDelta.mult(10.0);
  487. #else
  488. scrollDelta = scrollDelta.mult(50.0);
  489. #endif
  490. const int mods = glfwMods(ev.mod);
  491. const ScopedContext sc(this, mods);
  492. return context->event->handleScroll(fLastMousePos, scrollDelta);
  493. }
  494. bool onCharacterInput(const CharacterInputEvent& ev) override
  495. {
  496. if (ev.character < ' ' || ev.character >= kKeyDelete)
  497. return false;
  498. const int mods = glfwMods(ev.mod);
  499. const ScopedContext sc(this, mods);
  500. return context->event->handleText(fLastMousePos, ev.character);
  501. }
  502. bool onKeyboard(const KeyboardEvent& ev) override
  503. {
  504. const int action = ev.press ? GLFW_PRESS : GLFW_RELEASE;
  505. const int mods = glfwMods(ev.mod);
  506. /* These are unsupported in pugl right now
  507. #define GLFW_KEY_KP_0 320
  508. #define GLFW_KEY_KP_1 321
  509. #define GLFW_KEY_KP_2 322
  510. #define GLFW_KEY_KP_3 323
  511. #define GLFW_KEY_KP_4 324
  512. #define GLFW_KEY_KP_5 325
  513. #define GLFW_KEY_KP_6 326
  514. #define GLFW_KEY_KP_7 327
  515. #define GLFW_KEY_KP_8 328
  516. #define GLFW_KEY_KP_9 329
  517. #define GLFW_KEY_KP_DECIMAL 330
  518. #define GLFW_KEY_KP_DIVIDE 331
  519. #define GLFW_KEY_KP_MULTIPLY 332
  520. #define GLFW_KEY_KP_SUBTRACT 333
  521. #define GLFW_KEY_KP_ADD 334
  522. #define GLFW_KEY_KP_ENTER 335
  523. #define GLFW_KEY_KP_EQUAL 336
  524. */
  525. int key;
  526. switch (ev.key)
  527. {
  528. case '\r': key = GLFW_KEY_ENTER; break;
  529. case '\t': key = GLFW_KEY_TAB; break;
  530. case kKeyBackspace: key = GLFW_KEY_BACKSPACE; break;
  531. case kKeyEscape: key = GLFW_KEY_ESCAPE; break;
  532. case kKeyDelete: key = GLFW_KEY_DELETE; break;
  533. case kKeyF1: key = GLFW_KEY_F1; break;
  534. case kKeyF2: key = GLFW_KEY_F2; break;
  535. case kKeyF3: key = GLFW_KEY_F3; break;
  536. case kKeyF4: key = GLFW_KEY_F4; break;
  537. case kKeyF5: key = GLFW_KEY_F5; break;
  538. case kKeyF6: key = GLFW_KEY_F6; break;
  539. case kKeyF7: key = GLFW_KEY_F7; break;
  540. case kKeyF8: key = GLFW_KEY_F8; break;
  541. case kKeyF9: key = GLFW_KEY_F9; break;
  542. case kKeyF10: key = GLFW_KEY_F10; break;
  543. case kKeyF11: key = GLFW_KEY_F11; break;
  544. case kKeyF12: key = GLFW_KEY_F12; break;
  545. case kKeyLeft: key = GLFW_KEY_LEFT; break;
  546. case kKeyUp: key = GLFW_KEY_UP; break;
  547. case kKeyRight: key = GLFW_KEY_RIGHT; break;
  548. case kKeyDown: key = GLFW_KEY_DOWN; break;
  549. case kKeyPageUp: key = GLFW_KEY_PAGE_UP; break;
  550. case kKeyPageDown: key = GLFW_KEY_PAGE_DOWN; break;
  551. case kKeyHome: key = GLFW_KEY_HOME; break;
  552. case kKeyEnd: key = GLFW_KEY_END; break;
  553. case kKeyInsert: key = GLFW_KEY_INSERT; break;
  554. case kKeyShiftL: key = GLFW_KEY_LEFT_SHIFT; break;
  555. case kKeyShiftR: key = GLFW_KEY_RIGHT_SHIFT; break;
  556. case kKeyControlL: key = GLFW_KEY_LEFT_CONTROL; break;
  557. case kKeyControlR: key = GLFW_KEY_RIGHT_CONTROL; break;
  558. case kKeyAltL: key = GLFW_KEY_LEFT_ALT; break;
  559. case kKeyAltR: key = GLFW_KEY_RIGHT_ALT; break;
  560. case kKeySuperL: key = GLFW_KEY_LEFT_SUPER; break;
  561. case kKeySuperR: key = GLFW_KEY_RIGHT_SUPER; break;
  562. case kKeyMenu: key = GLFW_KEY_MENU; break;
  563. case kKeyCapsLock: key = GLFW_KEY_CAPS_LOCK; break;
  564. case kKeyScrollLock: key = GLFW_KEY_SCROLL_LOCK; break;
  565. case kKeyNumLock: key = GLFW_KEY_NUM_LOCK; break;
  566. case kKeyPrintScreen: key = GLFW_KEY_PRINT_SCREEN; break;
  567. case kKeyPause: key = GLFW_KEY_PAUSE; break;
  568. default: key = ev.key; break;
  569. }
  570. const ScopedContext sc(this, mods);
  571. return context->event->handleKey(fLastMousePos, key, ev.keycode, action, mods);
  572. }
  573. void onResize(const ResizeEvent& ev) override
  574. {
  575. UI::onResize(ev);
  576. if (context->window != nullptr)
  577. context->window->setSize(rack::math::Vec(ev.size.getWidth(), ev.size.getHeight()));
  578. const double scaleFactor = getScaleFactor();
  579. char sizeString[64];
  580. std::snprintf(sizeString, sizeof(sizeString), "%d:%d",
  581. (int)(ev.size.getWidth() / scaleFactor), (int)(ev.size.getHeight() / scaleFactor));
  582. setState("windowSize", sizeString);
  583. }
  584. void uiFocus(const bool focus, CrossingMode) override
  585. {
  586. if (focus)
  587. return;
  588. const ScopedContext sc(this, 0);
  589. context->event->handleLeave();
  590. }
  591. void uiFileBrowserSelected(const char* const filename) override
  592. {
  593. if (filename == nullptr)
  594. return;
  595. rack::contextSet(context);
  596. WindowParametersRestore(context->window);
  597. std::string sfilename = filename;
  598. if (saving)
  599. {
  600. if (rack::system::getExtension(sfilename) != ".vcv")
  601. sfilename += ".vcv";
  602. try {
  603. context->patch->save(sfilename);
  604. }
  605. catch (rack::Exception& e) {
  606. std::string message = rack::string::f("Could not save patch: %s", e.what());
  607. asyncDialog::create(message.c_str());
  608. return;
  609. }
  610. }
  611. else
  612. {
  613. try {
  614. context->patch->load(sfilename);
  615. } catch (rack::Exception& e) {
  616. std::string message = rack::string::f("Could not load patch: %s", e.what());
  617. asyncDialog::create(message.c_str());
  618. return;
  619. }
  620. }
  621. context->patch->path = sfilename;
  622. context->history->setSaved();
  623. }
  624. #if 0
  625. void uiReshape(const uint width, const uint height) override
  626. {
  627. glEnable(GL_BLEND);
  628. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  629. glMatrixMode(GL_PROJECTION);
  630. glLoadIdentity();
  631. glOrtho(0.0, width, 0.0, height, -1.0, 1.0);
  632. glViewport(0, 0, width, height);
  633. glMatrixMode(GL_MODELVIEW);
  634. glLoadIdentity();
  635. }
  636. #endif
  637. private:
  638. /**
  639. Set our UI class as non-copyable and add a leak detector just in case.
  640. */
  641. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CardinalUI)
  642. };
  643. /* ------------------------------------------------------------------------------------------------------------
  644. * UI entry point, called by DPF to create a new UI instance. */
  645. UI* createUI()
  646. {
  647. return new CardinalUI();
  648. }
  649. // -----------------------------------------------------------------------------------------------------------
  650. END_NAMESPACE_DISTRHO