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.

511 lines
15KB

  1. /*
  2. * Dear ImGui for DPF, converted to VCV
  3. * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  4. * Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
  5. *
  6. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  7. * or without fee is hereby granted, provided that the above copyright notice and this
  8. * permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  11. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  12. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  13. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  15. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include "ImGuiWidget.hpp"
  18. #include "DistrhoUtils.hpp"
  19. #ifndef DGL_NO_SHARED_RESOURCES
  20. # include "../../../dpf/dgl/src/Resources.hpp"
  21. #endif
  22. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  23. # include "DearImGui/imgui_impl_opengl3.h"
  24. #else
  25. # include "DearImGui/imgui_impl_opengl2.h"
  26. #endif
  27. static const char* GetClipboardTextFn(void*)
  28. {
  29. return glfwGetClipboardString(nullptr);
  30. }
  31. static void SetClipboardTextFn(void*, const char* const text)
  32. {
  33. glfwSetClipboardString(nullptr, text);
  34. }
  35. static void setupIO()
  36. {
  37. ImGuiIO& io(ImGui::GetIO());
  38. io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
  39. io.IniFilename = nullptr;
  40. io.LogFilename = nullptr;
  41. io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
  42. io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
  43. io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
  44. io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
  45. io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
  46. io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
  47. io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
  48. io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
  49. io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
  50. io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
  51. io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
  52. io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
  53. io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
  54. io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
  55. io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
  56. io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER;
  57. io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
  58. io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
  59. io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
  60. io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
  61. io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
  62. io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
  63. io.GetClipboardTextFn = GetClipboardTextFn;
  64. io.SetClipboardTextFn = SetClipboardTextFn;
  65. }
  66. struct ImGuiWidget::PrivateData {
  67. ImGuiContext* context = nullptr;
  68. bool created = false;
  69. bool fontGenerated = false;
  70. bool useMonospacedFont = false;
  71. float originalScaleFactor = 0.0f;
  72. float scaleFactor = 0.0f;
  73. double lastFrameTime = 0.0;
  74. PrivateData()
  75. {
  76. IMGUI_CHECKVERSION();
  77. context = ImGui::CreateContext();
  78. ImGui::SetCurrentContext(context);
  79. setupIO();
  80. }
  81. ~PrivateData()
  82. {
  83. // this should not happen
  84. if (created)
  85. {
  86. ImGui::SetCurrentContext(context);
  87. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  88. ImGui_ImplOpenGL3_Shutdown();
  89. #else
  90. ImGui_ImplOpenGL2_Shutdown();
  91. #endif
  92. }
  93. ImGui::DestroyContext(context);
  94. }
  95. void generateFontIfNeeded()
  96. {
  97. if (fontGenerated)
  98. return;
  99. DISTRHO_SAFE_ASSERT_RETURN(scaleFactor != 0.0f,);
  100. fontGenerated = true;
  101. ImGuiIO& io(ImGui::GetIO());
  102. if (useMonospacedFont)
  103. {
  104. const std::string fontPath = asset::system("res/fonts/ShareTechMono-Regular.ttf");
  105. ImFontConfig fc;
  106. fc.OversampleH = 1;
  107. fc.OversampleV = 1;
  108. fc.PixelSnapH = true;
  109. io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 13.0f * scaleFactor, &fc);
  110. io.Fonts->Build();
  111. }
  112. else
  113. {
  114. #ifndef DGL_NO_SHARED_RESOURCES
  115. using namespace dpf_resources;
  116. ImFontConfig fc;
  117. fc.FontDataOwnedByAtlas = false;
  118. fc.OversampleH = 1;
  119. fc.OversampleV = 1;
  120. fc.PixelSnapH = true;
  121. io.Fonts->AddFontFromMemoryTTF((void*)dejavusans_ttf, dejavusans_ttf_size, 13.0f * scaleFactor, &fc);
  122. // extra fonts we can try loading for unicode support
  123. static const char* extraFontPathsToTry[] = {
  124. #if defined(ARCH_WIN)
  125. // TODO
  126. // "Meiryo.ttc",
  127. #elif defined(ARCH_MAC)
  128. // TODO
  129. #elif defined(ARCH_LIN)
  130. "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc",
  131. #endif
  132. };
  133. fc.FontDataOwnedByAtlas = true;
  134. fc.MergeMode = true;
  135. for (size_t i=0; i<ARRAY_SIZE(extraFontPathsToTry); ++i)
  136. {
  137. if (rack::system::exists(extraFontPathsToTry[i]))
  138. io.Fonts->AddFontFromFileTTF(extraFontPathsToTry[i], 13.0f * scaleFactor, &fc,
  139. io.Fonts->GetGlyphRangesJapanese());
  140. }
  141. io.Fonts->Build();
  142. #endif
  143. }
  144. }
  145. void resetEverything(const bool doInit)
  146. {
  147. if (created)
  148. {
  149. ImGui::SetCurrentContext(context);
  150. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  151. ImGui_ImplOpenGL3_Shutdown();
  152. #else
  153. ImGui_ImplOpenGL2_Shutdown();
  154. #endif
  155. created = false;
  156. }
  157. fontGenerated = false;
  158. originalScaleFactor = 0.0f;
  159. scaleFactor = 0.0f;
  160. lastFrameTime = 0.0;
  161. ImGui::DestroyContext(context);
  162. context = ImGui::CreateContext();
  163. ImGui::SetCurrentContext(context);
  164. setupIO();
  165. if (doInit)
  166. {
  167. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  168. ImGui_ImplOpenGL3_Init();
  169. #else
  170. ImGui_ImplOpenGL2_Init();
  171. #endif
  172. created = true;
  173. }
  174. }
  175. void resetStyle()
  176. {
  177. ImGuiStyle& style(ImGui::GetStyle());
  178. style.FrameRounding = 4;
  179. style.ScaleAllSizes(scaleFactor);
  180. const ImVec4 color_Cardinal(0.76f, 0.11f, 0.22f, 1.00f);
  181. const ImVec4 color_DimCardinal(171.0 / 255.0, 54.0 / 255.0, 73.0 / 255.0, 1.00f);
  182. ImVec4* const colors = style.Colors;
  183. colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
  184. colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
  185. colors[ImGuiCol_WindowBg] = ImVec4(0.101f, 0.101f, 0.101f, 0.94f);
  186. colors[ImGuiCol_FrameBg] = ImVec4(0.20f, 0.21f, 0.22f, 0.54f);
  187. colors[ImGuiCol_FrameBgHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.40f);
  188. colors[ImGuiCol_FrameBgActive] = ImVec4(0.18f, 0.18f, 0.18f, 0.67f);
  189. colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.29f, 0.29f, 1.00f);
  190. colors[ImGuiCol_CheckMark] = color_Cardinal;
  191. colors[ImGuiCol_SliderGrab] = color_DimCardinal;
  192. colors[ImGuiCol_SliderGrabActive] = color_Cardinal;
  193. colors[ImGuiCol_Button] = color_DimCardinal;
  194. colors[ImGuiCol_ButtonHovered] = color_Cardinal;
  195. colors[ImGuiCol_ButtonActive] = color_Cardinal;
  196. colors[ImGuiCol_TextSelectedBg] = ImVec4(0.87f, 0.87f, 0.87f, 0.35f);
  197. colors[ImGuiCol_Header] = ImVec4(0.44f, 0.44f, 0.44f, 0.40f);
  198. colors[ImGuiCol_HeaderHovered] = color_DimCardinal;
  199. colors[ImGuiCol_HeaderActive] = color_Cardinal;
  200. }
  201. };
  202. ImGuiWidget::ImGuiWidget()
  203. : imData(new PrivateData()) {}
  204. ImGuiWidget::~ImGuiWidget()
  205. {
  206. delete imData;
  207. }
  208. float ImGuiWidget::getScaleFactor() const noexcept
  209. {
  210. return imData->scaleFactor;
  211. }
  212. void ImGuiWidget::onContextCreate(const ContextCreateEvent& e)
  213. {
  214. OpenGlWidgetWithBrowserPreview::onContextCreate(e);
  215. DISTRHO_SAFE_ASSERT_RETURN(!imData->created,);
  216. ImGui::SetCurrentContext(imData->context);
  217. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  218. ImGui_ImplOpenGL3_Init();
  219. #else
  220. ImGui_ImplOpenGL2_Init();
  221. #endif
  222. imData->created = true;
  223. }
  224. void ImGuiWidget::onContextDestroy(const ContextDestroyEvent& e)
  225. {
  226. if (imData->created)
  227. {
  228. ImGui::SetCurrentContext(imData->context);
  229. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  230. ImGui_ImplOpenGL3_Shutdown();
  231. #else
  232. ImGui_ImplOpenGL2_Shutdown();
  233. #endif
  234. imData->created = false;
  235. }
  236. OpenGlWidgetWithBrowserPreview::onContextDestroy(e);
  237. }
  238. void ImGuiWidget::setAsCurrentContext()
  239. {
  240. ImGui::SetCurrentContext(imData->context);
  241. }
  242. void ImGuiWidget::setUseMonospaceFont(const bool useMonoFont)
  243. {
  244. DISTRHO_SAFE_ASSERT_RETURN(!imData->fontGenerated,);
  245. imData->useMonospacedFont = useMonoFont;
  246. }
  247. void ImGuiWidget::onHover(const HoverEvent& e)
  248. {
  249. ImGui::SetCurrentContext(imData->context);
  250. ImGuiIO& io(ImGui::GetIO());
  251. io.MousePos.x = e.pos.x + e.mouseDelta.x;
  252. io.MousePos.y = e.pos.y + e.mouseDelta.y;
  253. if (d_isNotEqual(imData->scaleFactor, 1.0f))
  254. {
  255. io.MousePos.x *= imData->scaleFactor;
  256. io.MousePos.y *= imData->scaleFactor;
  257. }
  258. }
  259. void ImGuiWidget::onDragHover(const DragHoverEvent& e)
  260. {
  261. ImGui::SetCurrentContext(imData->context);
  262. ImGuiIO& io(ImGui::GetIO());
  263. io.MousePos.x = e.pos.x + e.mouseDelta.x;
  264. io.MousePos.y = e.pos.y + e.mouseDelta.y;
  265. if (d_isNotEqual(imData->scaleFactor, 1.0f))
  266. {
  267. io.MousePos.x *= imData->scaleFactor;
  268. io.MousePos.y *= imData->scaleFactor;
  269. }
  270. }
  271. void ImGuiWidget::onDragEnd(const DragEndEvent& e)
  272. {
  273. ImGui::SetCurrentContext(imData->context);
  274. ImGuiIO& io(ImGui::GetIO());
  275. io.MouseDown[0] = io.MouseDown[1] = io.MouseDown[2] = false;
  276. }
  277. void ImGuiWidget::onHoverScroll(const HoverScrollEvent& e)
  278. {
  279. ImGui::SetCurrentContext(imData->context);
  280. float deltaX = e.scrollDelta.x;
  281. float deltaY = e.scrollDelta.y;
  282. if (d_isNotEqual(imData->scaleFactor, 1.0f))
  283. {
  284. deltaX *= imData->scaleFactor;
  285. deltaY *= imData->scaleFactor;
  286. }
  287. ImGuiIO& io(ImGui::GetIO());
  288. io.MouseWheel += deltaY * 0.01f;
  289. io.MouseWheelH += deltaX * 0.01f;
  290. if (io.WantCaptureMouse)
  291. e.consume(this);
  292. }
  293. void ImGuiWidget::onButton(const ButtonEvent& e)
  294. {
  295. ImGui::SetCurrentContext(imData->context);
  296. ImGuiIO& io(ImGui::GetIO());
  297. switch (e.button)
  298. {
  299. case GLFW_MOUSE_BUTTON_LEFT:
  300. io.MouseDown[0] = e.action == GLFW_PRESS;
  301. break;
  302. /* Don't capture these, let Cardinal handle it instead
  303. case GLFW_MOUSE_BUTTON_MIDDLE:
  304. io.MouseDown[1] = e.action == GLFW_PRESS;
  305. break;
  306. case GLFW_MOUSE_BUTTON_RIGHT:
  307. io.MouseDown[2] = e.action == GLFW_PRESS;
  308. break;
  309. */
  310. default:
  311. return;
  312. }
  313. io.KeyCtrl = e.mods & GLFW_MOD_CONTROL;
  314. io.KeyShift = e.mods & GLFW_MOD_SHIFT;
  315. io.KeyAlt = e.mods & GLFW_MOD_ALT;
  316. io.KeySuper = e.mods & GLFW_MOD_SUPER;
  317. if (io.WantCaptureMouse)
  318. e.consume(this);
  319. }
  320. void ImGuiWidget::onSelectKey(const SelectKeyEvent& e)
  321. {
  322. if (e.key < 0 || e.key >= IM_ARRAYSIZE(ImGuiIO::KeysDown))
  323. return;
  324. ImGui::SetCurrentContext(imData->context);
  325. ImGuiIO& io(ImGui::GetIO());
  326. switch (e.action)
  327. {
  328. case GLFW_PRESS:
  329. io.KeysDown[e.key] = true;
  330. break;
  331. case GLFW_RELEASE:
  332. io.KeysDown[e.key] = false;
  333. break;
  334. default:
  335. return;
  336. }
  337. io.KeyCtrl = e.mods & GLFW_MOD_CONTROL;
  338. io.KeyShift = e.mods & GLFW_MOD_SHIFT;
  339. io.KeyAlt = e.mods & GLFW_MOD_ALT;
  340. io.KeySuper = e.mods & GLFW_MOD_SUPER;
  341. if (io.WantCaptureKeyboard)
  342. e.consume(this);
  343. }
  344. void ImGuiWidget::onSelectText(const SelectTextEvent& e)
  345. {
  346. ImGui::SetCurrentContext(imData->context);
  347. ImGuiIO& io(ImGui::GetIO());
  348. io.AddInputCharacter(e.codepoint);
  349. if (io.WantCaptureKeyboard)
  350. e.consume(this);
  351. }
  352. void ImGuiWidget::drawFramebuffer()
  353. {
  354. const float scaleFactor = APP->window->pixelRatio * std::max(1.0f, APP->scene->rack->getAbsoluteZoom());
  355. if (d_isNotEqual(imData->scaleFactor, scaleFactor))
  356. imData->resetEverything(true);
  357. drawFramebufferCommon(getFramebufferSize(), scaleFactor);
  358. }
  359. void ImGuiWidget::drawFramebufferForBrowserPreview()
  360. {
  361. imData->resetEverything(true);
  362. drawFramebufferCommon(box.size.mult(oversample), oversample);
  363. }
  364. void ImGuiWidget::drawFramebufferCommon(const Vec& fbSize, const float scaleFactor)
  365. {
  366. ImGui::SetCurrentContext(imData->context);
  367. ImGuiIO& io(ImGui::GetIO());
  368. if (d_isNotEqual(imData->scaleFactor, scaleFactor))
  369. {
  370. imData->scaleFactor = scaleFactor;
  371. ImGuiStyle& style(ImGui::GetStyle());
  372. new(&style)ImGuiStyle();
  373. imData->resetStyle();
  374. if (! imData->fontGenerated)
  375. {
  376. imData->originalScaleFactor = scaleFactor;
  377. imData->generateFontIfNeeded();
  378. }
  379. else
  380. {
  381. io.FontGlobalScale = scaleFactor / imData->originalScaleFactor;
  382. }
  383. }
  384. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  385. // TODO?
  386. #else
  387. glMatrixMode(GL_PROJECTION);
  388. glPushMatrix();
  389. glLoadIdentity();
  390. glOrtho(0.0, box.size.x * scaleFactor, box.size.y * scaleFactor, 0.0, -1.0, 1.0);
  391. glViewport(0.0, 0.0, fbSize.x, fbSize.y);
  392. glMatrixMode(GL_MODELVIEW);
  393. glPushMatrix();
  394. glLoadIdentity();
  395. #endif
  396. io.DisplaySize = ImVec2(box.size.x * scaleFactor, box.size.y * scaleFactor);
  397. io.DisplayFramebufferScale = ImVec2(fbSize.x / (box.size.x * scaleFactor), fbSize.y / (box.size.y * scaleFactor));
  398. if (!imData->created)
  399. {
  400. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  401. ImGui_ImplOpenGL3_Init();
  402. #else
  403. ImGui_ImplOpenGL2_Init();
  404. #endif
  405. imData->created = true;
  406. }
  407. const double time = glfwGetTime();
  408. io.DeltaTime = time - imData->lastFrameTime;
  409. imData->lastFrameTime = time;
  410. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  411. ImGui_ImplOpenGL3_NewFrame();
  412. #else
  413. ImGui_ImplOpenGL2_NewFrame();
  414. #endif
  415. ImGui::NewFrame();
  416. drawImGui();
  417. ImGui::Render();
  418. if (ImDrawData* const data = ImGui::GetDrawData())
  419. {
  420. #if defined(DGL_USE_GLES2) || defined(DGL_USE_GLES3) || defined(DGL_USE_OPENGL3)
  421. ImGui_ImplOpenGL3_RenderDrawData(data);
  422. #else
  423. ImGui_ImplOpenGL2_RenderDrawData(data);
  424. #endif
  425. }
  426. // FIXME
  427. io.KeysDown[GLFW_KEY_DELETE] = io.KeysDown[GLFW_KEY_BACKSPACE] = io.KeysDown[GLFW_KEY_ENTER] = false;
  428. }