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.

1626 lines
45KB

  1. #ifndef TROWASOFT_COMPONENTS_HPP
  2. #define TROWASOFT_COMPONENTS_HPP
  3. #include "rack.hpp"
  4. using namespace rack;
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include "window.hpp"
  8. #include "ui.hpp"
  9. #include "util/math.hpp"
  10. #include "dsp/digital.hpp"
  11. #include "componentlibrary.hpp"
  12. #include "plugin.hpp"
  13. #include "trowaSoftUtilities.hpp"
  14. #include "util/color.hpp"
  15. #ifdef USE_VST2
  16. #define plugin "trowaSoft"
  17. #endif // USE_VST2
  18. //=======================================================
  19. // trowaSoft - TurtleMonkey Components
  20. //=======================================================
  21. //::: Helpers :::::::::::::::::::::::::::::::::::::::::::
  22. // Extra colors (may be defined in future?)
  23. #ifndef COLOR_MAGENTA
  24. #define COLOR_MAGENTA nvgRGB(240, 50, 230)
  25. #endif
  26. #ifndef COLOR_LIME
  27. #define COLOR_LIME nvgRGB(210, 245, 60)
  28. #endif
  29. #ifndef COLOR_PINK
  30. #define COLOR_PINK nvgRGB(250, 190, 190)
  31. #endif
  32. #ifndef COLOR_TEAL
  33. #define COLOR_TEAL nvgRGB(0, 128, 128)
  34. #endif
  35. #ifndef COLOR_LAVENDER
  36. #define COLOR_LAVENDER nvgRGB(230, 190, 255)
  37. #endif
  38. #ifndef COLOR_BROWN
  39. #define COLOR_BROWN nvgRGB(170, 110, 40)
  40. #endif
  41. #ifndef COLOR_BEIGE
  42. #define COLOR_BEIGE nvgRGB(255, 250, 200)
  43. #endif
  44. #ifndef COLOR_MAROON
  45. #define COLOR_MAROON nvgRGB(128, 0, 0)
  46. #endif
  47. #ifndef COLOR_MINT
  48. #define COLOR_MINT nvgRGB(170, 255, 195)
  49. #endif
  50. #ifndef COLOR_OLIVE
  51. #define COLOR_OLIVE nvgRGB(128, 128, 0)
  52. #endif
  53. #ifndef COLOR_CORAL
  54. #define COLOR_CORAL nvgRGB(255, 215, 180)
  55. #endif
  56. #ifndef COLOR_NAVY
  57. #define COLOR_NAVY nvgRGB(0, 0, 128)
  58. #endif
  59. #ifndef COLOR_DARK_ORANGE
  60. #define COLOR_DARK_ORANGE nvgRGB(0xFF, 0x8C, 0x00)
  61. #endif
  62. #ifndef COLOR_PUMPKIN_ORANGE
  63. #define COLOR_PUMPKIN_ORANGE nvgRGB(0xF8, 0x72, 0x17)
  64. #endif
  65. #ifndef COLOR_DARK_GRAY
  66. #define COLOR_DARK_GRAY nvgRGB(0x33, 0x33, 0x33)
  67. #endif
  68. #ifndef COLOR_TS_RED
  69. #define COLOR_TS_RED nvgRGB(0xFF, 0x00, 0x00)
  70. #endif
  71. #ifndef COLOR_TS_ORANGE
  72. // Orange in components.hpp is different
  73. #define COLOR_TS_ORANGE nvgRGB(0xFF, 0xA5, 0x00)
  74. #endif
  75. #ifndef COLOR_TS_GREEN
  76. #define COLOR_TS_GREEN nvgRGB(0x00, 0xFF, 0x00)
  77. #endif
  78. #ifndef COLOR_TS_BLUE
  79. #define COLOR_TS_BLUE nvgRGB(0x33, 0x66, 0xFF)
  80. #endif
  81. #ifndef COLOR_TS_GRAY
  82. #define COLOR_TS_GRAY nvgRGB(0xAA, 0xAA, 0xAB)
  83. #endif
  84. #ifndef KNOB_SENSITIVITY
  85. #define KNOB_SENSITIVITY 0.0015
  86. #endif // ! KNOB_SENSITIVITY
  87. //-----------------------------------------------------------------
  88. // Form controls - Default colors and such
  89. //-----------------------------------------------------------------
  90. #define FORMS_DEFAULT_TEXT_COLOR nvgRGB(0xee, 0xee, 0xee)
  91. #define FORMS_DEFAULT_BORDER_COLOR nvgRGB(0x66, 0x66, 0x66)
  92. #define FORMS_DEFAULT_BG_COLOR COLOR_BLACK
  93. //--------------------------------------------------------------
  94. // ColorValueLight - Sorta like the old ColorValueLight that was in Rack.
  95. //--------------------------------------------------------------
  96. struct ColorValueLight : ModuleLightWidget {
  97. NVGcolor baseColor;
  98. // Pixels to add for outer radius (either px or relative %).
  99. float outerRadiusHalo = 0.35;
  100. bool outerRadiusRelative = true;
  101. ColorValueLight()
  102. {
  103. bgColor = nvgRGB(0x20, 0x20, 0x20);
  104. borderColor = nvgRGBA(0, 0, 0, 0);
  105. return;
  106. };
  107. virtual ~ColorValueLight(){};
  108. // Set a single color
  109. void setColor(NVGcolor bColor)
  110. {
  111. color = bColor;
  112. baseColor = bColor;
  113. if (baseColors.size() < 1)
  114. {
  115. baseColors.push_back(bColor);
  116. }
  117. else
  118. {
  119. baseColors[0] = bColor;
  120. }
  121. }
  122. void drawHalo(NVGcontext *vg) override {
  123. float radius = box.size.x / 2.0;
  124. float oradius = radius + ((outerRadiusRelative) ? (radius*outerRadiusHalo) : outerRadiusHalo);
  125. nvgBeginPath(vg);
  126. nvgRect(vg, radius - oradius, radius - oradius, 2 * oradius, 2 * oradius);
  127. NVGpaint paint;
  128. NVGcolor icol = colorMult(color, 0.10);
  129. NVGcolor ocol = nvgRGB(0, 0, 0);
  130. paint = nvgRadialGradient(vg, radius, radius, radius, oradius, icol, ocol);
  131. nvgFillPaint(vg, paint);
  132. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  133. nvgFill(vg);
  134. }
  135. };
  136. //------------------------------------------------------------------------------------------------
  137. // TS_Label : Label with the trowaSoft default label font.
  138. //------------------------------------------------------------------------------------------------
  139. struct TS_Label : Label {
  140. // Font size. Default is 10.
  141. int fontSize = 10;
  142. // Font face
  143. std::shared_ptr<Font> font;
  144. // The font color. Default is Dark Gray.
  145. NVGcolor textColor = COLOR_DARK_GRAY;
  146. enum TextAlignment {
  147. Left,
  148. Center,
  149. Right
  150. };
  151. // Text alignment. Default is Left.
  152. TextAlignment textAlign = TextAlignment::Left;
  153. enum VerticalAlignment {
  154. Baseline,
  155. Top,
  156. Middle,
  157. Bottom
  158. };
  159. // Vertical alignment. Default is Baseline.
  160. VerticalAlignment verticalAlign = VerticalAlignment::Baseline;
  161. // The text letter spacing (default 1.0f).
  162. float textLetterSpacing = 1.0f;
  163. bool drawBackground = false;
  164. NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  165. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  166. float padding = 0.f;
  167. TS_Label(const char* fontPath)
  168. {
  169. font = Font::load(assetPlugin(plugin, fontPath));
  170. return;
  171. }
  172. TS_Label() : TS_Label(TROWA_LABEL_FONT)
  173. {
  174. return;
  175. }
  176. TS_Label(const char* fontPath, Vec size) : TS_Label(fontPath)
  177. {
  178. box.size = size;
  179. return;
  180. }
  181. TS_Label(Vec size) : TS_Label(TROWA_LABEL_FONT)
  182. {
  183. box.size = size;
  184. return;
  185. }
  186. TS_Label(const char* fontPath, std::string text, Vec size) : TS_Label(fontPath, size)
  187. {
  188. this->text = text;
  189. return;
  190. }
  191. TS_Label(std::string text, Vec size) : TS_Label(size)
  192. {
  193. this->text = text;
  194. return;
  195. }
  196. void draw(NVGcontext *vg) override
  197. {
  198. if (visible)
  199. {
  200. //nvgGlobalCompositeOperation(vg, NVG_SOURCE_OVER);//Restore to default.
  201. if (drawBackground)
  202. {
  203. nvgBeginPath(vg);
  204. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  205. nvgFillColor(vg, backgroundColor);
  206. nvgFill(vg);
  207. nvgStrokeWidth(vg, 1.0);
  208. nvgStrokeColor(vg, borderColor);
  209. nvgStroke(vg);
  210. }
  211. nvgFontFaceId(vg, font->handle);
  212. nvgFontSize(vg, fontSize);
  213. nvgTextLetterSpacing(vg, textLetterSpacing);
  214. float x = 0;
  215. float y = 0;
  216. uint8_t alignment = 0x00;
  217. switch (textAlign)
  218. {
  219. case TextAlignment::Center:
  220. x = box.size.x / 2.0;
  221. alignment = NVGalign::NVG_ALIGN_CENTER;
  222. break;
  223. case TextAlignment::Right:
  224. x = box.size.x - padding;
  225. alignment = NVGalign::NVG_ALIGN_RIGHT;
  226. break;
  227. case TextAlignment::Left:
  228. default:
  229. x = 0.0f + padding;
  230. alignment = NVGalign::NVG_ALIGN_LEFT;
  231. break;
  232. }
  233. switch (verticalAlign)
  234. {
  235. case VerticalAlignment::Middle:
  236. y = box.size.y / 2.0;
  237. alignment |= NVGalign::NVG_ALIGN_MIDDLE;
  238. break;
  239. case VerticalAlignment::Bottom:
  240. y = box.size.y - padding;
  241. alignment |= NVGalign::NVG_ALIGN_BOTTOM;
  242. break;
  243. case VerticalAlignment::Top:
  244. y = 0.0f + padding;
  245. alignment |= NVGalign::NVG_ALIGN_TOP;
  246. break;
  247. case VerticalAlignment::Baseline:
  248. default:
  249. y = 0.0f + padding;
  250. alignment |= NVGalign::NVG_ALIGN_BASELINE; // Default, align text vertically to baseline.
  251. break;
  252. }
  253. nvgTextAlign(vg, alignment);
  254. nvgFillColor(vg, textColor);
  255. nvgText(vg, x, y, text.c_str(), NULL);
  256. }
  257. return;
  258. }
  259. };
  260. //--------------------------------------------------------------
  261. // TS_PadSwitch
  262. // Empty momentary switch of given size.
  263. //--------------------------------------------------------------
  264. struct TS_PadSwitch : MomentarySwitch {
  265. int btnId = -1;
  266. // Group id (to match guys that should respond to mouse down drag).
  267. int groupId = -1;
  268. TS_PadSwitch() {
  269. return;
  270. }
  271. TS_PadSwitch(Vec size) {
  272. box.size = size;
  273. return;
  274. }
  275. // Allow mouse-down & drag to set buttons (i.e. on Sequencer grid where there are many buttons).
  276. // Suggestion from @LKHSogpit, Solution from @AndrewBelt.
  277. // https://github.com/j4s0n-c/trowaSoft-VCV/issues/7
  278. // https://github.com/VCVRack/Rack/issues/607
  279. /** Called when a widget responds to `onMouseDown` for a left button press */
  280. void onDragStart(EventDragStart &e) override {
  281. float newVal = (value < maxValue) ? maxValue : minValue;
  282. //debug("onDragStart(%d) - Current Value is %.1f, setting to %.1f.", btnId, value, newVal);
  283. setValue(newVal); // Toggle Value
  284. return;
  285. }
  286. /** Called when the left button is released and this widget is being dragged */
  287. // https://github.com/j4s0n-c/trowaSoft-VCV/issues/12
  288. // Last button keeps pressed down.
  289. void onDragEnd(EventDragEnd &e) override {
  290. //debug("onDragEnd(%d) - Current Value is %.1f, setting to %.1f (off).", btnId, value, minValue);
  291. setValue(minValue); // Turn Off
  292. return;
  293. }
  294. /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */
  295. void onDragEnter(EventDragEnter &e) override {
  296. // Set these no matter what because if you drag back onto your starting square, you want to toggle it again.
  297. TS_PadSwitch *origin = dynamic_cast<TS_PadSwitch*>(e.origin);
  298. if (origin && origin->groupId == this->groupId) {
  299. float newVal = (value < maxValue) ? maxValue : minValue;
  300. //debug("onDragEnter(%d) - Current Value is %.1f, setting to %.1f.", btnId, value, newVal);
  301. setValue(newVal); // Toggle Value
  302. }
  303. }
  304. void onDragLeave(EventDragEnter &e) override {
  305. TS_PadSwitch *origin = dynamic_cast<TS_PadSwitch*>(e.origin);
  306. if (origin && origin->groupId == this->groupId) {
  307. //debug("onDragLeave(%d) - Current Value is %.1f, setting to %.1f (off).", btnId, value, minValue);
  308. setValue(minValue); // Turn Off
  309. }
  310. }
  311. void onMouseUp(EventMouseUp &e) override {
  312. //debug("onMouseUp(%d) - Current Value is %.1f, setting to %.1f (off).", btnId, value, minValue);
  313. setValue(minValue); // Turn Off
  314. };
  315. };
  316. //--------------------------------------------------------------
  317. // TS_PadSquare - A Square Pad button.
  318. //--------------------------------------------------------------
  319. struct TS_PadSquare : SVGSwitch, TS_PadSwitch {
  320. TS_PadSquare()
  321. {
  322. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_0.svg")));
  323. sw->wrap();
  324. box.size = sw->box.size;
  325. }
  326. TS_PadSquare(Vec size)
  327. {
  328. addFrame(SVG::load(assetPlugin(plugin, "res/ComponentLibrary/TS_pad_0.svg")));
  329. sw->box.size = size;
  330. box.size = size;
  331. }
  332. };
  333. //--------------------------------------------------------------
  334. // TS_PadBtn - A wide Pad button. (Empty text)
  335. //--------------------------------------------------------------
  336. struct TS_PadBtn : SVGSwitch, MomentarySwitch {
  337. TS_PadBtn()
  338. {
  339. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_btn_0.svg")));
  340. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_btn_1.svg")));
  341. sw->wrap();
  342. box.size = sw->box.size;
  343. }
  344. };
  345. //--------------------------------------------------------------
  346. // TS_PadRun - A wide Pad button. (RUN >)
  347. //--------------------------------------------------------------
  348. struct TS_Pad_Run : SVGSwitch, MomentarySwitch {
  349. TS_Pad_Run()
  350. {
  351. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_run_0.svg")));
  352. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_run_1.svg")));
  353. sw->wrap();
  354. box.size = sw->box.size;
  355. }
  356. };
  357. //--------------------------------------------------------------
  358. // TS_PadReset - A wide Pad button. (< RST)
  359. //--------------------------------------------------------------
  360. struct TS_Pad_Reset : SVGSwitch, MomentarySwitch {
  361. TS_Pad_Reset()
  362. {
  363. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_reset_0.svg")));
  364. addFrame(SVG::load(assetPlugin(plugin,"res/ComponentLibrary/TS_pad_reset_1.svg")));
  365. sw->wrap();
  366. box.size = sw->box.size;
  367. }
  368. };
  369. struct HideableLEDButton : LEDButton
  370. {
  371. void draw(NVGcontext *vg) override {
  372. if (visible) {
  373. LEDButton::draw(vg);
  374. }
  375. }
  376. void onMouseDown(EventMouseDown &e) override {
  377. if (visible) {
  378. LEDButton::onMouseDown(e);
  379. }
  380. };
  381. void onMouseUp(EventMouseUp &e) override {
  382. if (visible) {
  383. LEDButton::onMouseUp(e);
  384. }
  385. };
  386. /** Called on every frame, even if mouseRel = Vec(0, 0) */
  387. void onMouseMove(EventMouseMove &e) override {
  388. if (visible) {
  389. LEDButton::onMouseMove(e);
  390. }
  391. }
  392. void onHoverKey(EventHoverKey &e) override {
  393. if (visible) {
  394. LEDButton::onHoverKey(e);
  395. }
  396. };
  397. ///** Called when this widget begins responding to `onMouseMove` events */
  398. //virtual void onMouseEnter(EventMouseEnter &e) {}
  399. ///** Called when another widget begins responding to `onMouseMove` events */
  400. //virtual void onMouseLeave(EventMouseLeave &e) {}
  401. //virtual void onFocus(EventFocus &e) {}
  402. //virtual void onDefocus(EventDefocus &e) {}
  403. //virtual void onScroll(EventScroll &e);
  404. /** Called when a widget responds to `onMouseDown` for a left button press */
  405. void onDragStart(EventDragStart &e) override {
  406. if (visible) {
  407. LEDButton::onDragStart(e);
  408. }
  409. }
  410. /** Called when the left button is released and this widget is being dragged */
  411. void onDragEnd(EventDragEnd &e) override {
  412. if (visible) {
  413. LEDButton::onDragEnd(e);
  414. }
  415. }
  416. /** Called when a widget responds to `onMouseMove` and is being dragged */
  417. void onDragMove(EventDragMove &e) override {
  418. if (visible) {
  419. LEDButton::onDragMove(e);
  420. }
  421. }
  422. /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */
  423. void onDragEnter(EventDragEnter &e) override {
  424. if (visible) {
  425. LEDButton::onDragEnter(e);
  426. }
  427. }
  428. void onDragLeave(EventDragEnter &e) override {
  429. if (visible) {
  430. LEDButton::onDragLeave(e);
  431. }
  432. }
  433. void onDragDrop(EventDragDrop &e) override {
  434. if (visible) {
  435. LEDButton::onDragDrop(e);
  436. }
  437. }
  438. void onPathDrop(EventPathDrop &e)override {
  439. if (visible) {
  440. LEDButton::onPathDrop(e);
  441. }
  442. }
  443. void onAction(EventAction &e) override {
  444. if (visible) {
  445. LEDButton::onAction(e);
  446. }
  447. }
  448. };
  449. //--------------------------------------------------------------
  450. // TS_ScreenBtn - Screen button.
  451. //--------------------------------------------------------------
  452. struct TS_ScreenBtn : MomentarySwitch {
  453. bool visible = true;
  454. // Text to display on the btn.
  455. std::string btnText;
  456. // Background color
  457. NVGcolor backgroundColor = nvgRGBA(0, 0, 0, 0);
  458. // Text color
  459. NVGcolor color = COLOR_TS_GRAY;
  460. // Border color
  461. NVGcolor borderColor = COLOR_TS_GRAY;
  462. // Border width
  463. int borderWidth = 1;
  464. // Corner radius. 0 for straight corners.
  465. int cornerRadius = 5;
  466. // Font size for our display numbers
  467. int fontSize = 10;
  468. // Font face
  469. std::shared_ptr<Font> font = NULL;
  470. // Padding
  471. int padding = 1;
  472. enum TextAlignment {
  473. Left,
  474. Center,
  475. Right
  476. };
  477. TextAlignment textAlign = TextAlignment::Center;
  478. //TS_ScreenBtn()
  479. //{
  480. // return;
  481. //}
  482. TS_ScreenBtn(Vec size, Module* module, int paramId, std::string text, float minVal, float maxVal, float defVal)
  483. {
  484. box.size = size;
  485. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  486. fontSize = 10;
  487. btnText = text;
  488. this->module = module;
  489. this->paramId = paramId;
  490. this->value = 0.0;
  491. this->minValue = minVal;
  492. this->maxValue = maxVal;
  493. this->defaultValue = defVal;
  494. return;
  495. }
  496. /** Called when a widget responds to `onMouseDown` for a left button press */
  497. void onDragStart(EventDragStart &e) override {
  498. if (visible) {
  499. MomentarySwitch::onDragStart(e);
  500. }
  501. return;
  502. }
  503. /** Called when the left button is released and this widget is being dragged */
  504. void onDragEnd(EventDragEnd &e) override {
  505. if (visible) {
  506. MomentarySwitch::onDragEnd(e);
  507. }
  508. return;
  509. }
  510. /** Called when a widget responds to `onMouseUp` for a left button release and a widget is being dragged */
  511. void onDragEnter(EventDragEnter &e) override {
  512. if (visible) {
  513. MomentarySwitch::onDragEnter(e);
  514. }
  515. }
  516. void onMouseDown(EventMouseDown &e) override {
  517. if (visible) {
  518. MomentarySwitch::onMouseDown(e);
  519. }
  520. }
  521. void setVisible(bool visible) {
  522. this->visible = visible;
  523. return;
  524. }
  525. virtual void draw(NVGcontext *vg) override
  526. {
  527. if (!visible)
  528. return;
  529. // Background
  530. nvgBeginPath(vg);
  531. if (cornerRadius > 0)
  532. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, cornerRadius);
  533. else
  534. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  535. nvgFillColor(vg, backgroundColor);
  536. nvgFill(vg);
  537. // Background - border.
  538. if (borderWidth > 0) {
  539. nvgStrokeWidth(vg, borderWidth);
  540. nvgStrokeColor(vg, borderColor);
  541. nvgStroke(vg);
  542. }
  543. // Text
  544. nvgBeginPath(vg);
  545. nvgScissor(vg, padding, padding, box.size.x - 2*padding, box.size.y - 2*padding);
  546. nvgFontSize(vg, fontSize);
  547. nvgFontFaceId(vg, font->handle);
  548. nvgFillColor(vg, color);
  549. float x, y;
  550. NVGalign nvgAlign;
  551. y = box.size.y / 2.0f;
  552. switch (textAlign) {
  553. case TextAlignment::Left:
  554. nvgAlign = NVG_ALIGN_LEFT;
  555. x = box.size.x + padding;
  556. break;
  557. case TextAlignment::Right:
  558. nvgAlign = NVG_ALIGN_RIGHT;
  559. x = box.size.x - padding;
  560. break;
  561. case TextAlignment::Center:
  562. default:
  563. nvgAlign = NVG_ALIGN_CENTER;
  564. x = box.size.x / 2.0f;
  565. break;
  566. }
  567. nvgTextAlign(vg, nvgAlign | NVG_ALIGN_MIDDLE);
  568. nvgText(vg, x, y, btnText.c_str(), NULL);
  569. nvgResetScissor(vg);
  570. return;
  571. }
  572. };
  573. //--------------------------------------------------------------
  574. // TS_ScreenCheckBox : Screen checkbox button
  575. //--------------------------------------------------------------
  576. struct TS_ScreenCheckBox : TS_ScreenBtn {
  577. // If the check box should show as checked.
  578. bool checked = false;
  579. float checkBoxWidth = 14;
  580. float checkBoxHeight = 14;
  581. TS_ScreenCheckBox(Vec size, Module* module, int paramId, std::string text, float minVal, float maxVal, float defVal)
  582. : TS_ScreenBtn(size, module, paramId, text, minVal, maxVal, defVal)
  583. {
  584. return;
  585. }
  586. void draw(NVGcontext *vg) override
  587. {
  588. if (!visible)
  589. return;
  590. // Background
  591. nvgBeginPath(vg);
  592. if (cornerRadius > 0)
  593. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, cornerRadius);
  594. else
  595. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  596. nvgFillColor(vg, backgroundColor);
  597. nvgFill(vg);
  598. // Background - border.
  599. if (borderWidth > 0) {
  600. nvgStrokeWidth(vg, borderWidth);
  601. nvgStrokeColor(vg, borderColor);
  602. nvgStroke(vg);
  603. }
  604. // Text
  605. nvgBeginPath(vg);
  606. nvgScissor(vg, padding, padding, box.size.x - 2 * padding, box.size.y - 2 * padding);
  607. nvgFontSize(vg, fontSize);
  608. nvgFontFaceId(vg, font->handle);
  609. nvgFillColor(vg, color);
  610. float x, y;
  611. NVGalign nvgAlign;
  612. y = box.size.y / 2.0f;
  613. switch (textAlign) {
  614. case TextAlignment::Left:
  615. nvgAlign = NVG_ALIGN_LEFT;
  616. x = box.size.x + padding;
  617. break;
  618. case TextAlignment::Right:
  619. nvgAlign = NVG_ALIGN_RIGHT;
  620. x = box.size.x - padding;
  621. break;
  622. case TextAlignment::Center:
  623. default:
  624. nvgAlign = NVG_ALIGN_CENTER;
  625. x = box.size.x / 2.0f;
  626. break;
  627. }
  628. nvgTextAlign(vg, nvgAlign | NVG_ALIGN_MIDDLE);
  629. float txtBounds[4] = { 0,0,0,0 };
  630. nvgTextBounds(vg, x, y, btnText.c_str(), NULL, txtBounds);
  631. nvgText(vg, x, y, btnText.c_str(), NULL);
  632. nvgResetScissor(vg);
  633. // Check box ::::::::::::::::::::::::::::::::::::::::::::::
  634. float boxX = txtBounds[0] - checkBoxWidth - padding;
  635. float boxY = y - checkBoxHeight / 2.0 - padding;
  636. nvgBeginPath(vg);
  637. nvgRoundedRect(vg, boxX, boxY, checkBoxWidth, checkBoxHeight, 3);
  638. nvgStrokeColor(vg, color);
  639. nvgStrokeWidth(vg, 1.0f);
  640. nvgStroke(vg);
  641. if (checked)
  642. {
  643. nvgBeginPath(vg);
  644. nvgRoundedRect(vg, boxX + padding, boxY + padding, checkBoxWidth - padding * 2, checkBoxHeight - padding*2, 3);
  645. nvgFillColor(vg, color);
  646. nvgFill(vg);
  647. }
  648. return;
  649. }
  650. };
  651. //------------------------------------------------------------------------------------------------
  652. // TS_LightArc - Lighted arc for light knobs
  653. //------------------------------------------------------------------------------------------------
  654. struct TS_LightArc : ColorValueLight {
  655. // The inner radius
  656. float innerRadius = 22;
  657. // Pointer to current angle in radians. This is the differential like from a knob.
  658. float* currentAngle_radians;
  659. // Font size for our display numbers
  660. int fontSize;
  661. // Font face
  662. std::shared_ptr<Font> font;
  663. // Numeric value to print out
  664. float* numericValue;
  665. // Buffer for our light string.
  666. char lightString[10];
  667. // The point where the angle is considered 0 degrees / radians.
  668. float zeroAnglePoint;
  669. // Pointer to the Sequencer Value mode information.
  670. ValueSequencerMode* valueMode;
  671. TS_LightArc()
  672. {
  673. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  674. fontSize = 10;
  675. bgColor = nvgRGBAf(0.0, 0, 0, /*alpha */ 1.0);
  676. baseColor = COLOR_WHITE;
  677. zeroAnglePoint = TROWA_ANGLE_STRAIGHT_UP_RADIANS;
  678. }
  679. void draw(NVGcontext *vg) override
  680. {
  681. float oradius = box.size.x / 2.0; // 25
  682. float radius = oradius - 2; // 23
  683. float angle = *currentAngle_radians;
  684. zeroAnglePoint = valueMode->zeroPointAngle_radians;
  685. int dir = (angle < zeroAnglePoint) ? NVG_CCW : NVG_CW;
  686. // Background - Solid
  687. nvgBeginPath(vg);
  688. nvgCircle(vg, oradius, oradius, innerRadius);
  689. nvgFillColor(vg, bgColor);
  690. nvgFill(vg);
  691. nvgStrokeWidth(vg, radius - innerRadius);
  692. NVGcolor borderColor = color;// bgColor;
  693. borderColor.a *= 0.5;//1.0;
  694. nvgStrokeColor(vg, borderColor);
  695. nvgStroke(vg);
  696. // svg Angles go clockwise from positive x -->
  697. // Inner glow
  698. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  699. nvgCircle(vg, oradius, oradius, radius);
  700. borderColor = color;
  701. borderColor.a = 0.25;
  702. nvgStrokeWidth(vg, oradius - radius);
  703. nvgStrokeColor(vg, borderColor);
  704. nvgStroke(vg);
  705. nvgBeginPath(vg);
  706. //nvgArcTo(vg, oradius, oradius, float x2, float y2, float radius);
  707. // Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
  708. // and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW).
  709. // Angles are specified in radians.
  710. // nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir);
  711. nvgArc(vg, /*cx*/ oradius, /*cy*/ oradius, /*radius*/ innerRadius,
  712. /*a0*/ zeroAnglePoint, /*a1*/ angle, /*dir*/ dir);
  713. nvgStrokeWidth(vg, oradius - innerRadius);
  714. borderColor = baseColor;
  715. borderColor.a *= 0.7;
  716. nvgStrokeColor(vg, borderColor);
  717. nvgStroke(vg);
  718. // Outer glow
  719. nvgBeginPath(vg);
  720. nvgArc(vg, /*cx*/ oradius, /*cy*/ oradius, innerRadius - 3,
  721. /*a0*/ zeroAnglePoint, /*a1*/ angle, /*dir*/ dir);
  722. NVGpaint paint;
  723. NVGcolor icol = color;
  724. icol.a *= 0.8;
  725. NVGcolor ocol = color;
  726. ocol.a = 0.0;
  727. paint = nvgRadialGradient(vg, oradius, oradius, innerRadius, oradius, icol, ocol);
  728. nvgStrokeWidth(vg, oradius - innerRadius + 3);
  729. nvgStrokePaint(vg, paint);
  730. nvgStroke(vg);
  731. if (numericValue != NULL)
  732. {
  733. nvgBeginPath(vg);
  734. nvgGlobalCompositeOperation(vg, NVG_SOURCE_OVER);//Restore to default.
  735. NVGcolor textColor = COLOR_WHITE;
  736. nvgFontSize(vg, fontSize);
  737. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
  738. float v = valueMode->GetOutputValue(*numericValue);
  739. valueMode->GetDisplayString(v, lightString);
  740. nvgFillColor(vg, textColor);
  741. nvgText(vg, oradius, oradius, lightString, NULL);
  742. }
  743. return;
  744. }
  745. }; // end TS_LightArc
  746. //------------------------------------------------------------------------------------------------
  747. // TS_LightedKnob - Knob to be used with light arcs. (not actually lit itself)
  748. //------------------------------------------------------------------------------------------------
  749. struct TS_LightedKnob : SVGKnob {
  750. float currentAngle;
  751. float differentialAngle;
  752. const float zeroAnglePoint = TROWA_ANGLE_STRAIGHT_UP_RADIANS;
  753. int id = 0; // For debugging.
  754. TS_LightedKnob() {
  755. minAngle = -0.83*NVG_PI;
  756. maxAngle = 0.83*NVG_PI;
  757. box.size = Vec(50, 50);
  758. currentAngle = 0;
  759. minValue = -10;
  760. maxValue = 10;
  761. snap = false;
  762. return;
  763. }
  764. TS_LightedKnob(int id) : TS_LightedKnob() {
  765. this->id = id;
  766. }
  767. void randomize() override { return; }
  768. void setKnobValue(float val)
  769. {
  770. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  771. float prevVal = value;
  772. #endif
  773. value = val;
  774. differentialAngle = rescale(value, minValue, maxValue, minAngle, maxAngle);
  775. currentAngle = zeroAnglePoint + differentialAngle;
  776. this->dirty = true;
  777. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  778. debug("Knob %d: Set value to %.2f (from %.2f). Current Value is now %.2f.", id, val, prevVal, value);
  779. #endif
  780. return;
  781. }
  782. void step() override {
  783. // Re-transform TransformWidget if dirty
  784. if (dirty) {
  785. differentialAngle = rescale(value, minValue, maxValue, minAngle, maxAngle);
  786. currentAngle = zeroAnglePoint + differentialAngle;
  787. tw->identity();
  788. // Scale SVG to box
  789. tw->scale(box.size.div(sw->box.size));
  790. // Rotate SVG
  791. Vec center = sw->box.getCenter();
  792. tw->translate(center);
  793. tw->rotate(currentAngle);
  794. tw->translate(center.neg());
  795. }
  796. FramebufferWidget::step();
  797. }
  798. }; // end TS_LightedKnob
  799. //--------------------------------------------------------------
  800. // TS_LightString - A light with a string (message/text).
  801. //--------------------------------------------------------------
  802. struct TS_LightString : ColorValueLight
  803. {
  804. const char * lightString;
  805. float cornerRadius = 3.0;
  806. std::shared_ptr<Font> font;
  807. int fontSize;
  808. TS_LightString()
  809. {
  810. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  811. fontSize = 14;
  812. bgColor = nvgRGBAf(0.2, 0.2, 0.2, /*alpha */ 1);
  813. baseColor = COLOR_WHITE;
  814. }
  815. void draw(NVGcontext *vg) override
  816. {
  817. float radius = box.size.x / 2.0;
  818. float oradius = radius + 20.0;
  819. float radiusY = box.size.y / 2.0;
  820. float oradiusY = radiusY + 20.0;
  821. NVGcolor outerColor = color;
  822. // Solid
  823. nvgBeginPath(vg);
  824. // Border
  825. nvgStrokeWidth(vg, 1.0);
  826. NVGcolor borderColor = bgColor;
  827. borderColor.a *= 0.5;
  828. nvgStrokeColor(vg, borderColor);
  829. nvgStroke(vg);
  830. // Inner glow
  831. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  832. nvgFillColor(vg, color);
  833. nvgFill(vg);
  834. // Outer glow
  835. nvgBeginPath(vg);
  836. nvgRoundedRect(vg, /*x*/ radius - oradius, /*y*/ radiusY - oradiusY, /*w*/ 3*oradius, /*h*/ 2*oradiusY, cornerRadius);
  837. NVGpaint paint;
  838. NVGcolor icol = outerColor;// color;
  839. icol.a *= 0.5;
  840. NVGcolor ocol = outerColor;// color;
  841. ocol.a = 0.0;
  842. float feather = 3;
  843. // Feather defines how blurry the border of the rectangle is.
  844. paint = nvgBoxGradient(vg, /*x*/ 0, /*y*/ 0, /*w*/ box.size.x, /*h*/ oradiusY - 10,
  845. /*r: corner radius*/ cornerRadius, /*f: feather*/ feather,
  846. /*inner color*/ icol, /*outer color */ ocol);
  847. nvgFillPaint(vg, paint);
  848. nvgFill(vg);
  849. nvgBeginPath(vg);
  850. nvgGlobalCompositeOperation(vg, NVG_SOURCE_OVER);//Restore to default.
  851. NVGcolor textColor = baseColor;
  852. nvgFillColor(vg, textColor);
  853. nvgFontSize(vg, fontSize);
  854. nvgFontFaceId(vg, font->handle);
  855. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
  856. nvgText(vg, box.size.x / 2, box.size.y / 2, lightString, NULL);
  857. return;
  858. }
  859. }; // end TS_LightString
  860. //--------------------------------------------------------------
  861. // TS_LightSquare - Square light.
  862. //--------------------------------------------------------------
  863. struct TS_LightSquare : ColorValueLight
  864. {
  865. // Radius on corners
  866. float cornerRadius = 5.0;
  867. TS_LightSquare()
  868. {
  869. bgColor = nvgRGBAf(0, 0, 0, /*alpha */ 0.5);
  870. baseColor = COLOR_WHITE;
  871. }
  872. void draw(NVGcontext *vg) override
  873. {
  874. float radius = box.size.x / 2.0;
  875. float oradius = radius*1.1;
  876. NVGcolor backColor = bgColor;
  877. NVGcolor outerColor = color;
  878. // Solid
  879. nvgBeginPath(vg);
  880. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, cornerRadius);
  881. nvgFillColor(vg, backColor);
  882. nvgFill(vg);
  883. // Border
  884. nvgStrokeWidth(vg, 1.0);
  885. NVGcolor borderColor = bgColor;
  886. borderColor.a *= 0.5;
  887. nvgStrokeColor(vg, borderColor);
  888. nvgStroke(vg);
  889. // Inner glow
  890. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  891. nvgFillColor(vg, color);
  892. nvgFill(vg);
  893. // Outer glow
  894. nvgBeginPath(vg);
  895. nvgRoundedRect(vg, /*x*/ radius - oradius, /*y*/ radius - oradius, /*w*/ 2*oradius, /*h*/ 2*oradius, cornerRadius);
  896. NVGpaint paint;
  897. NVGcolor icol = outerColor;// color;
  898. icol.a *= 0.25;
  899. NVGcolor ocol = outerColor;// color;
  900. ocol.a = 0.0;
  901. float feather = 2;
  902. // Feather defines how blurry the border of the rectangle is. // Fixed 01/19/2018, made it too tiny before
  903. paint = nvgBoxGradient(vg, /*x*/ radius - oradius, /*y*/ radius - oradius, /*w*/ 2 * oradius, /*h*/ 2 * oradius, //vg, /*x*/ -5, /*y*/ -5, /*w*/ 2*oradius + 10, /*h*/ 2*oradius + 10,
  904. /*r: corner radius*/ cornerRadius, /*f: feather*/ feather,
  905. /*inner color*/ icol, /*outer color */ ocol);
  906. nvgFillPaint(vg, paint);
  907. nvgFill(vg);
  908. return;
  909. }
  910. }; // end TS_LightSquare
  911. //--------------------------------------------------------------
  912. // TS_LightRing - Light to be used around ports.
  913. //--------------------------------------------------------------
  914. struct TS_LightRing : ColorValueLight
  915. {
  916. // The inner radius
  917. float innerRadius = 6.8;
  918. TS_LightRing()
  919. {
  920. bgColor = nvgRGBAf(0, 0, 0, /*alpha */ 0.2);
  921. baseColor = COLOR_WHITE;
  922. }
  923. void draw(NVGcontext *vg) override
  924. {
  925. float radius = box.size.x / 2.0;
  926. float oradius = radius + 10.0;
  927. // Solid
  928. nvgBeginPath(vg);
  929. nvgCircle(vg, radius, radius, radius);
  930. // Border
  931. nvgStrokeWidth(vg, radius - innerRadius);
  932. NVGcolor borderColor = bgColor;
  933. borderColor.a *= 1.0;
  934. nvgStrokeColor(vg, borderColor);
  935. nvgStroke(vg);
  936. // Inner glow
  937. nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  938. //nvgFillColor(vg, color);
  939. //nvgFill(vg);
  940. // Outer glow
  941. nvgBeginPath(vg);
  942. nvgRect(vg, radius - oradius, radius - oradius, 2*oradius, 2*oradius);
  943. NVGpaint paint;
  944. NVGcolor icol = color;
  945. icol.a *= (module != NULL) ? module->lights[firstLightId].value : 0;
  946. //icol.a *= value;
  947. NVGcolor ocol = color;
  948. ocol.a = 0.0;
  949. paint = nvgRadialGradient(vg, radius, radius, innerRadius, oradius, icol, ocol);
  950. nvgFillPaint(vg, paint);
  951. nvgFill(vg);
  952. return;
  953. }
  954. };
  955. //--------------------------------------------------------------
  956. // TS_TinyBlackKnob - 20x20 RoundBlackKnob
  957. //--------------------------------------------------------------
  958. struct TS_TinyBlackKnob : RoundKnob {
  959. const int size = 20;
  960. TS_TinyBlackKnob() {
  961. box.size = Vec(size, size);
  962. setSVG(SVG::load(assetPlugin(plugin, "res/ComponentLibrary/TS_RoundBlackKnob_20.svg")));
  963. }
  964. };
  965. //--------------------------------------------------------------
  966. // TS_15_BlackKnob - 15x15 RoundBlackKnob
  967. // This is a little too tiny
  968. //--------------------------------------------------------------
  969. struct TS_15_BlackKnob : RoundKnob {
  970. const int size = 15;
  971. TS_15_BlackKnob() {
  972. box.size = Vec(size, size);
  973. setSVG(SVG::load(assetPlugin(plugin, "res/ComponentLibrary/TS_RoundBlackKnob_15.svg")));
  974. }
  975. };
  976. //--------------------------------------------------------------
  977. // TS_20_BlackEncoder - 20x20 Encoder
  978. // Pseudo continuous.... Still enforces the limits but allows the knob rotate 360.
  979. //--------------------------------------------------------------
  980. struct TS_20_BlackEncoder : RoundKnob {
  981. const int size = 20;
  982. int c = 0;
  983. float rotationRangeMin = -1.f;
  984. float rotationRangeMax = 1.f;
  985. float fineControlMult = 1. / 16.f;
  986. float coarseControlMult = 16.f;
  987. TS_20_BlackEncoder() {
  988. box.size = Vec(size, size);
  989. setSVG(SVG::load(assetPlugin(plugin, "res/ComponentLibrary/TS_RoundBlackEncoder_20.svg")));
  990. minAngle = 0;
  991. maxAngle = 2 * M_PI;
  992. return;
  993. }
  994. // Set the amount a full rotation should yield.
  995. void setRotationAmount(float amt) {
  996. rotationRangeMin = -0.5f*amt;
  997. rotationRangeMax = 0.5f*amt;
  998. return;
  999. }
  1000. // Override to allow pseudo endless encoding (still bound by some real values).
  1001. void onDragMove(EventDragMove &e) override {
  1002. float range = rotationRangeMax - rotationRangeMin;
  1003. float delta = KNOB_SENSITIVITY * -e.mouseRel.y * speed * range;
  1004. // Drag slower if Mod is held
  1005. if (windowIsModPressed())
  1006. delta *= fineControlMult; // Finer control
  1007. else if (windowIsShiftPressed())
  1008. delta *= coarseControlMult; // Coarser control
  1009. dragValue += delta;
  1010. dragValue = clamp2(dragValue, minValue, maxValue);
  1011. if (snap)
  1012. setValue(roundf(dragValue));
  1013. else
  1014. setValue(dragValue);
  1015. return;
  1016. }
  1017. void step() override {
  1018. // Re-transform TransformWidget if dirty
  1019. if (dirty) {
  1020. // Allow rotations 360 degrees:
  1021. float angle = rescale(value, rotationRangeMin, rotationRangeMax, minAngle, maxAngle);
  1022. angle = fmodf(angle, 2 * M_PI);
  1023. tw->identity();
  1024. // Rotate SVG
  1025. Vec center = sw->box.getCenter();
  1026. tw->translate(center);
  1027. tw->rotate(angle);
  1028. tw->translate(center.neg());
  1029. }
  1030. FramebufferWidget::step();
  1031. return;
  1032. }
  1033. };
  1034. //--------------------------------------------------------------
  1035. // TS_Port - Smaller port with set light color and light disable
  1036. // (by just making the lights transparent... TODO: get rid of light completely.)
  1037. //--------------------------------------------------------------
  1038. struct TS_Port : SVGPort {
  1039. NVGcolor negColor;
  1040. NVGcolor posColor;
  1041. TS_Port() : SVGPort() {
  1042. background->svg = SVG::load(assetPlugin(plugin, "res/ComponentLibrary/TS_Port.svg"));
  1043. background->wrap();
  1044. box.size = background->box.size;
  1045. if (plugLight)
  1046. {
  1047. negColor = plugLight->baseColors[1];
  1048. posColor = plugLight->baseColors[0];
  1049. }
  1050. }
  1051. void disableLights()
  1052. {
  1053. // Save our colors:
  1054. if (plugLight)
  1055. {
  1056. negColor = plugLight->baseColors[1];
  1057. posColor = plugLight->baseColors[0];
  1058. plugLight->baseColors[0] = nvgRGBAf(0,0,0,0);
  1059. plugLight->baseColors[1] = nvgRGBAf(0,0,0,0);
  1060. }
  1061. }
  1062. void enableLights()
  1063. {
  1064. if (plugLight)
  1065. {
  1066. plugLight->baseColors[1] = negColor;
  1067. plugLight->baseColors[0] = posColor;
  1068. }
  1069. }
  1070. void setLightColor(NVGcolor color)
  1071. {
  1072. negColor = color;
  1073. posColor = color;
  1074. if (plugLight)
  1075. {
  1076. plugLight->baseColors[0] = color;
  1077. plugLight->baseColors[1] = color;
  1078. }
  1079. }
  1080. void setLightColor(NVGcolor negativeColor, NVGcolor positiveColor)
  1081. {
  1082. negColor = negativeColor;
  1083. posColor = positiveColor;
  1084. if (plugLight)
  1085. {
  1086. plugLight->baseColors[1] = negativeColor;
  1087. plugLight->baseColors[2] = positiveColor;
  1088. }
  1089. }
  1090. };
  1091. //--------------------------------------------------------------
  1092. // TS_Panel - Panel with controllable borders on all sides.
  1093. //--------------------------------------------------------------
  1094. struct TS_Panel : Panel
  1095. {
  1096. NVGcolor originalBackgroundColor;
  1097. NVGcolor borderColor = COLOR_BLACK;
  1098. float borderWidth = 0;
  1099. float borderTop = 0;
  1100. float borderLeft = 0;
  1101. float borderRight = 0;
  1102. float borderBottom = 0;
  1103. void setBorderWidth(float top, float right, float bottom, float left)
  1104. {
  1105. borderTop = top;
  1106. borderLeft = left;
  1107. borderRight = right;
  1108. borderBottom = bottom;
  1109. return;
  1110. }
  1111. //void invertBackgroundColor()
  1112. //{
  1113. // backgroundColor = ColorInvertToNegative(originalBackgroundColor);
  1114. // return;
  1115. //}
  1116. void draw(NVGcontext *vg) override
  1117. {
  1118. nvgBeginPath(vg);
  1119. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  1120. // Background color
  1121. if (backgroundColor.a > 0) {
  1122. nvgFillColor(vg, backgroundColor);
  1123. nvgFill(vg);
  1124. }
  1125. // Background image
  1126. if (backgroundImage) {
  1127. int width, height;
  1128. nvgImageSize(vg, backgroundImage->handle, &width, &height);
  1129. NVGpaint paint = nvgImagePattern(vg, 0.0, 0.0, width, height, 0.0, backgroundImage->handle, 1.0);
  1130. nvgFillPaint(vg, paint);
  1131. nvgFill(vg);
  1132. }
  1133. // Border
  1134. if (borderWidth > 0)
  1135. {
  1136. nvgBeginPath(vg);
  1137. nvgRect(vg, borderWidth / 2.0, borderWidth / 2.0, box.size.x - borderWidth, box.size.y - borderWidth);
  1138. nvgStrokeColor(vg, borderColor);
  1139. nvgStrokeWidth(vg, borderWidth);
  1140. nvgStroke(vg);
  1141. }
  1142. int x, y;
  1143. if (borderTop > 0)
  1144. {
  1145. // Line at top
  1146. nvgBeginPath(vg);
  1147. x = 0;
  1148. y = borderTop / 2.0;
  1149. nvgMoveTo(vg, /*start x*/ x, /*start y*/ y); // Top Left
  1150. x = box.size.x;
  1151. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Top Right
  1152. nvgStrokeColor(vg, borderColor);
  1153. nvgStrokeWidth(vg, borderTop);
  1154. nvgStroke(vg);
  1155. }
  1156. if (borderRight > 0)
  1157. {
  1158. nvgBeginPath(vg);
  1159. x = box.size.x - borderRight / 2.0;
  1160. y = 0;
  1161. nvgMoveTo(vg, /*x*/ x, /*y*/ y); // Top Right
  1162. y = box.size.y;// - borderRight;
  1163. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Bottom Right
  1164. nvgStrokeColor(vg, borderColor);
  1165. nvgStrokeWidth(vg, borderRight);
  1166. nvgStroke(vg);
  1167. }
  1168. if (borderBottom > 0)
  1169. {
  1170. nvgBeginPath(vg);
  1171. x = box.size.x;// - borderBottom;
  1172. y = box.size.y - borderBottom / 2.0;// - borderBottom;
  1173. nvgMoveTo(vg, /*x*/ x, /*y*/ y); // Bottom Right
  1174. x = 0;// borderBottom / 2.0;
  1175. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Bottom Left
  1176. nvgStrokeColor(vg, borderColor);
  1177. nvgStrokeWidth(vg, borderBottom);
  1178. nvgStroke(vg);
  1179. }
  1180. if (borderLeft > 0)
  1181. {
  1182. nvgBeginPath(vg);
  1183. x = borderLeft / 2.0;
  1184. y = box.size.y;// - borderLeft;
  1185. nvgMoveTo(vg, /*x*/ x, /*y*/ y); // Bottom Left
  1186. y = 0;//borderLeft / 2.0;
  1187. nvgLineTo(vg, /*x*/ x, /*y*/ y); // Top Left
  1188. nvgStrokeColor(vg, borderColor);
  1189. nvgStrokeWidth(vg, borderLeft);
  1190. nvgStroke(vg);
  1191. }
  1192. Widget::draw(vg);
  1193. } // end draw()
  1194. }; // end TS_Panel
  1195. //--------------------------------------------------------------
  1196. // TS_SVGPanel - SVG Panel without mandatory border on LHS
  1197. //--------------------------------------------------------------
  1198. struct TS_SVGPanel : SVGPanel
  1199. {
  1200. NVGcolor borderColor = COLOR_BLACK;
  1201. float borderTop = 0;
  1202. float borderLeft = 0;
  1203. float borderRight = 0;
  1204. float borderBottom = 0;
  1205. TS_SVGPanel() : SVGPanel()
  1206. {
  1207. return;
  1208. }
  1209. TS_SVGPanel(float borderTop, float borderRight, float borderBottom, float borderLeft) : TS_SVGPanel()
  1210. {
  1211. this->borderTop = borderTop;
  1212. this->borderRight = borderRight;
  1213. this->borderBottom = borderBottom;
  1214. this->borderLeft = borderLeft;
  1215. return;
  1216. }
  1217. void setBackground(std::shared_ptr<SVG> svg) {
  1218. SVGWidget *sw = new SVGWidget();
  1219. sw->setSVG(svg);
  1220. addChild(sw);
  1221. // Set size
  1222. box.size = sw->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);
  1223. if (borderTop + borderRight + borderLeft + borderBottom > 0)
  1224. {
  1225. TS_Panel* pb = new TS_Panel();
  1226. pb->setBorderWidth(borderTop, borderRight, borderBottom, borderLeft);
  1227. pb->borderColor = this->borderColor;
  1228. pb->box.size = box.size;
  1229. addChild(pb);
  1230. }
  1231. // PanelBorder *pb = new PanelBorder();
  1232. // pb->box.size = box.size;
  1233. // addChild(pb);
  1234. } // end setBackground()
  1235. }; // end TS_SVGPanel
  1236. //--------------------------------------------------------------
  1237. // TS_ColorSlider - Horizontal color slider control 'knob'.
  1238. // Meant for picking colors via Hue, Saturation, Lightness.
  1239. //--------------------------------------------------------------
  1240. struct TS_ColorSlider : Knob {
  1241. // If this control should be rendered
  1242. bool visible = true;
  1243. // Starting color.
  1244. TSColorHSL startColorHSL;
  1245. // Ending color.
  1246. TSColorHSL endColorHSL;
  1247. // HSL representation of the selected color.
  1248. TSColorHSL selectedColorHSL;
  1249. // NVGcolor of the selected color (RGBA)
  1250. NVGcolor selectedColor;
  1251. // Number of stops for the gradients (doing them manually since nanovg only lets you have 2 colors)
  1252. int numStops = 24;
  1253. float handleWidth = 15.0;
  1254. float handleMargin = 3.0;
  1255. TS_ColorSlider() : Knob()
  1256. {
  1257. minValue = 0.0;
  1258. maxValue = 1.0;
  1259. startColorHSL.h = 0.0;
  1260. startColorHSL.s = 1.0;
  1261. startColorHSL.lum = 0.5;
  1262. endColorHSL.h = 1.0;
  1263. endColorHSL.s = 1.0;
  1264. endColorHSL.lum = 0.5;
  1265. selectedColorHSL.h = 1.0;
  1266. selectedColorHSL.s = 1.0;
  1267. selectedColorHSL.lum = 0.5;
  1268. return;
  1269. }
  1270. TS_ColorSlider(Vec size) : TS_ColorSlider()
  1271. {
  1272. box.size = size;
  1273. return;
  1274. }
  1275. // Set the component value for start and end
  1276. void setComponent(int index, float val)
  1277. {
  1278. startColorHSL.hsl[index] = val;
  1279. endColorHSL.hsl[index] = val;
  1280. return;
  1281. }
  1282. void onDragStart(EventDragStart &e) override {
  1283. if (visible)
  1284. Knob::onDragStart(e);
  1285. }
  1286. void onDragMove(EventDragMove &e) override {
  1287. if (visible)
  1288. {
  1289. // Drag slower if Mod
  1290. float delta = KNOB_SENSITIVITY * (maxValue - minValue) * e.mouseRel.x;
  1291. if (windowIsModPressed())
  1292. delta /= 16.0;
  1293. dragValue += delta;
  1294. if (snap)
  1295. setValue(roundf(dragValue));
  1296. else
  1297. setValue(dragValue);
  1298. }
  1299. return;
  1300. }
  1301. void onDragEnd(EventDragEnd &e) override {
  1302. if (visible)
  1303. Knob::onDragEnd(e);
  1304. }
  1305. void onChange(EventChange &e) override {
  1306. if (visible)
  1307. Knob::onChange(e);
  1308. }
  1309. //void step() override {
  1310. // return;
  1311. //}
  1312. //void onChange(EventChange &e) override {
  1313. // //dirty = true;
  1314. // ParamWidget::onChange(e);
  1315. //}
  1316. void draw(NVGcontext *vg) override {
  1317. if (!visible)
  1318. return;
  1319. // Draw the background:
  1320. float x = 0;
  1321. float y = 0;
  1322. float dx = box.size.x / numStops;
  1323. float deltaComponents[3];
  1324. // Calculate the delta/interval for each component (HSL)
  1325. for (int i = 0; i < 3; i++)
  1326. {
  1327. deltaComponents[i] = (endColorHSL.hsl[i] - startColorHSL.hsl[i]) / numStops;
  1328. }
  1329. float hue, sat, lht;
  1330. hue = startColorHSL.h; sat = startColorHSL.s; lht = startColorHSL.lum;
  1331. NVGcolor sColor;
  1332. NVGcolor eColor = nvgHSLA(hue, sat, lht, 0xFF);
  1333. y = box.size.y / 2.0;
  1334. for (int i = 0; i < numStops; i++)
  1335. {
  1336. sColor = eColor;
  1337. eColor = nvgHSLA(hue += deltaComponents[0], sat += deltaComponents[1], lht += deltaComponents[2], 0xFF);
  1338. nvgBeginPath(vg);
  1339. nvgRect(vg, x, 0, dx + 1, box.size.y);
  1340. nvgStrokeWidth(vg, 0.0);
  1341. NVGpaint paint = nvgLinearGradient(vg, x, y, x + dx + 1, y, sColor, eColor);
  1342. nvgFillPaint(vg, paint);
  1343. nvgFill(vg);
  1344. x += dx;
  1345. }
  1346. for (int i = 0; i < 3; i++)
  1347. {
  1348. selectedColorHSL.hsl[i] = startColorHSL.hsl[i] + (endColorHSL.hsl[i] - startColorHSL.hsl[i]) * value;
  1349. }
  1350. selectedColor = nvgHSL(selectedColorHSL.hsl[0], selectedColorHSL.hsl[1], selectedColorHSL.hsl[2]);
  1351. float handleHeight = box.size.y + 2 * handleMargin;
  1352. float handleX = rescale(value, minValue, maxValue, 0, box.size.x) - handleWidth / 2.0;
  1353. float handleY = -handleMargin;// rescale(value, minValue, maxValue, minHandlePos.y, maxHandlePos.y);
  1354. // Draw handle
  1355. nvgBeginPath(vg);
  1356. nvgRoundedRect(vg, handleX, handleY, handleWidth, handleHeight, 5);
  1357. nvgFillColor(vg, selectedColor);
  1358. nvgFill(vg);
  1359. nvgStrokeWidth(vg, 1.0);
  1360. NVGcolor strokeColor = ((value < 0.5) ? COLOR_WHITE : COLOR_BLACK);
  1361. nvgStrokeColor(vg, strokeColor);
  1362. nvgStroke(vg);
  1363. /*nvgFontSize(vg, 9.0);
  1364. nvgFillColor(vg, COLOR_WHITE);
  1365. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  1366. char buffer[20];
  1367. sprintf(buffer, "V: %.2f, W: %.2f", value, box.size.x);
  1368. nvgText(vg, 0, 0, buffer, NULL);*/
  1369. }
  1370. }; // end TS_ColorSlider
  1371. //:::-:::-:::-:::- Helpers -:::-:::-:::-:::
  1372. template <class TModuleLightWidget>
  1373. ColorValueLight * TS_createColorValueLight(Vec pos, Module *module, int lightId, Vec size, NVGcolor lightColor) {
  1374. ColorValueLight *light = new TModuleLightWidget();
  1375. light->box.pos = pos;
  1376. light->module = module;
  1377. light->firstLightId = lightId;
  1378. //light->value = value;
  1379. light->box.size = size;
  1380. light->setColor(lightColor);
  1381. //light->baseColor = lightColor;
  1382. return light;
  1383. }
  1384. template <class TModuleLightWidget>
  1385. ColorValueLight * TS_createColorValueLight(Vec pos, Module *module, int lightId, Vec size, NVGcolor lightColor, NVGcolor backColor) {
  1386. ColorValueLight *light = new TModuleLightWidget();
  1387. light->box.pos = pos;
  1388. light->module = module;
  1389. light->firstLightId = lightId;
  1390. light->box.size = size;
  1391. light->setColor(lightColor);
  1392. light->bgColor = backColor;
  1393. return light;
  1394. }
  1395. template <class TPort>
  1396. TS_Port* TS_createInput(Vec pos, Module *module, int inputId) {
  1397. TS_Port *port = new TPort();
  1398. port->box.pos = pos;
  1399. port->module = module;
  1400. port->type = Port::INPUT;
  1401. port->portId = inputId;
  1402. port->disableLights();
  1403. return port;
  1404. }
  1405. template <class TPort>
  1406. TS_Port* TS_createInput(Vec pos, Module *module, int inputId, NVGcolor lightColor) {
  1407. TS_Port *port = new TPort();
  1408. port->box.pos = pos;
  1409. port->module = module;
  1410. port->type = Port::INPUT;
  1411. port->portId = inputId;
  1412. port->setLightColor(lightColor);
  1413. port->enableLights();
  1414. return port;
  1415. }
  1416. template <class TPort>
  1417. TS_Port* TS_createInput(Vec pos, Module *module, int inputId, NVGcolor negColor, NVGcolor posColor) {
  1418. TS_Port *port = new TPort();
  1419. port->box.pos = pos;
  1420. port->module = module;
  1421. port->type = Port::INPUT;
  1422. port->portId = inputId;
  1423. port->setLightColor(negColor, posColor);
  1424. port->enableLights();
  1425. return port;
  1426. }
  1427. template <class TPort>
  1428. TS_Port* TS_createInput(Vec pos, Module *module, int inputId, bool disableLight) {
  1429. TS_Port *port = new TPort();
  1430. port->box.pos = pos;
  1431. port->module = module;
  1432. port->type = Port::INPUT;
  1433. port->portId = inputId;
  1434. if (disableLight)
  1435. port->disableLights();
  1436. else
  1437. port->enableLights();
  1438. return port;
  1439. }
  1440. template <class TPort>
  1441. TS_Port* TS_createInput(Vec pos, Module *module, int inputId, bool disableLight, NVGcolor lightColor) {
  1442. TS_Port *port = new TPort();
  1443. port->box.pos = pos;
  1444. port->module = module;
  1445. port->type = Port::INPUT;
  1446. port->portId = inputId;
  1447. port->setLightColor(lightColor);
  1448. if (disableLight)
  1449. port->disableLights();
  1450. else
  1451. port->enableLights();
  1452. return port;
  1453. }
  1454. template <class TPort>
  1455. TS_Port* TS_createOutput(Vec pos, Module *module, int inputId) {
  1456. TS_Port *port = new TPort();
  1457. port->box.pos = pos;
  1458. port->module = module;
  1459. port->type = Port::OUTPUT;
  1460. port->portId = inputId;
  1461. port->disableLights();
  1462. return port;
  1463. }
  1464. template <class TPort>
  1465. TS_Port* TS_createOutput(Vec pos, Module *module, int inputId, NVGcolor lightColor) {
  1466. TS_Port *port = new TPort();
  1467. port->box.pos = pos;
  1468. port->module = module;
  1469. port->type = Port::OUTPUT;
  1470. port->portId = inputId;
  1471. port->setLightColor(lightColor);
  1472. port->enableLights();
  1473. return port;
  1474. }
  1475. template <class TPort>
  1476. TS_Port* TS_createOutput(Vec pos, Module *module, int inputId, NVGcolor negColor, NVGcolor posColor) {
  1477. TS_Port *port = new TPort();
  1478. port->box.pos = pos;
  1479. port->module = module;
  1480. port->type = Port::OUTPUT;
  1481. port->portId = inputId;
  1482. port->setLightColor(negColor, posColor);
  1483. port->enableLights();
  1484. return port;
  1485. }
  1486. template <class TPort>
  1487. TS_Port* TS_createOutput(Vec pos, Module *module, int inputId, bool disableLight) {
  1488. TS_Port *port = new TPort();
  1489. port->box.pos = pos;
  1490. port->module = module;
  1491. port->type = Port::OUTPUT;
  1492. port->portId = inputId;
  1493. if (disableLight)
  1494. port->disableLights();
  1495. else
  1496. port->enableLights();
  1497. return port;
  1498. }
  1499. template <class TPort>
  1500. TS_Port* TS_createOutput(Vec pos, Module *module, int inputId, bool disableLight, NVGcolor lightColor) {
  1501. TS_Port *port = new TPort();
  1502. port->box.pos = pos;
  1503. port->module = module;
  1504. port->type = Port::OUTPUT;
  1505. port->portId = inputId;
  1506. port->setLightColor(lightColor);
  1507. if (disableLight)
  1508. port->disableLights();
  1509. else
  1510. port->enableLights();
  1511. return port;
  1512. }
  1513. #endif // end if not defined