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.

318 lines
7.7KB

  1. #include <app/ParamWidget.hpp>
  2. #include <ui/MenuOverlay.hpp>
  3. #include <ui/TextField.hpp>
  4. #include <app/Scene.hpp>
  5. #include <context.hpp>
  6. #include <engine/Engine.hpp>
  7. #include <engine/ParamQuantity.hpp>
  8. #include <settings.hpp>
  9. #include <history.hpp>
  10. #include <helpers.hpp>
  11. namespace rack {
  12. namespace app {
  13. struct ParamField : ui::TextField {
  14. ParamWidget* paramWidget;
  15. void step() override {
  16. // Keep selected
  17. APP->event->setSelected(this);
  18. TextField::step();
  19. }
  20. void setParamWidget(ParamWidget* paramWidget) {
  21. this->paramWidget = paramWidget;
  22. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  23. if (pq)
  24. text = pq->getDisplayValueString();
  25. selectAll();
  26. }
  27. void onSelectKey(const event::SelectKey& e) override {
  28. if (e.action == GLFW_PRESS && (e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER)) {
  29. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  30. assert(pq);
  31. float oldValue = pq->getValue();
  32. if (pq)
  33. pq->setDisplayValueString(text);
  34. float newValue = pq->getValue();
  35. if (oldValue != newValue) {
  36. // Push ParamChange history action
  37. history::ParamChange* h = new history::ParamChange;
  38. h->moduleId = paramWidget->module->id;
  39. h->paramId = paramWidget->paramId;
  40. h->oldValue = oldValue;
  41. h->newValue = newValue;
  42. APP->history->push(h);
  43. }
  44. ui::MenuOverlay* overlay = getAncestorOfType<ui::MenuOverlay>();
  45. overlay->requestDelete();
  46. e.consume(this);
  47. }
  48. if (!e.getTarget())
  49. TextField::onSelectKey(e);
  50. }
  51. };
  52. struct ParamValueItem : ui::MenuItem {
  53. ParamWidget* paramWidget;
  54. float value;
  55. void onAction(const event::Action& e) override {
  56. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  57. if (pq) {
  58. float oldValue = pq->getValue();
  59. pq->setValue(value);
  60. float newValue = pq->getValue();
  61. if (oldValue != newValue) {
  62. // Push ParamChange history action
  63. history::ParamChange* h = new history::ParamChange;
  64. h->name = "set parameter";
  65. h->moduleId = paramWidget->module->id;
  66. h->paramId = paramWidget->paramId;
  67. h->oldValue = oldValue;
  68. h->newValue = newValue;
  69. APP->history->push(h);
  70. }
  71. }
  72. }
  73. };
  74. struct ParamTooltip : ui::Tooltip {
  75. ParamWidget* paramWidget;
  76. void step() override {
  77. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  78. if (pq) {
  79. // Quantity string
  80. text = pq->getString();
  81. // Description
  82. std::string description = pq->getDescription();
  83. if (description != "") {
  84. text += "\n";
  85. text += description;
  86. }
  87. }
  88. Tooltip::step();
  89. // Position at bottom-right of parameter
  90. box.pos = paramWidget->getAbsoluteOffset(paramWidget->box.size).round();
  91. // Fit inside parent (copied from Tooltip.cpp)
  92. assert(parent);
  93. box = box.nudge(parent->box.zeroPos());
  94. }
  95. };
  96. struct ParamLabel : ui::MenuLabel {
  97. ParamWidget* paramWidget;
  98. void step() override {
  99. engine::ParamQuantity* pq = paramWidget->getParamQuantity();
  100. text = pq->getString();
  101. MenuLabel::step();
  102. }
  103. };
  104. struct ParamResetItem : ui::MenuItem {
  105. ParamWidget* paramWidget;
  106. void onAction(const event::Action& e) override {
  107. paramWidget->resetAction();
  108. }
  109. };
  110. struct ParamFineItem : ui::MenuItem {
  111. };
  112. struct ParamUnmapItem : ui::MenuItem {
  113. ParamWidget* paramWidget;
  114. void onAction(const event::Action& e) override {
  115. engine::ParamHandle* paramHandle = APP->engine->getParamHandle(paramWidget->module->id, paramWidget->paramId);
  116. if (paramHandle) {
  117. APP->engine->updateParamHandle(paramHandle, -1, 0);
  118. }
  119. }
  120. };
  121. engine::ParamQuantity* ParamWidget::getParamQuantity() {
  122. if (!module)
  123. return NULL;
  124. return module->paramQuantities[paramId];
  125. }
  126. void ParamWidget::createTooltip() {
  127. if (!settings::tooltips)
  128. return;
  129. if (this->tooltip)
  130. return;
  131. if (!module)
  132. return;
  133. ParamTooltip* tooltip = new ParamTooltip;
  134. tooltip->paramWidget = this;
  135. APP->scene->addChild(tooltip);
  136. this->tooltip = tooltip;
  137. }
  138. void ParamWidget::destroyTooltip() {
  139. if (!tooltip)
  140. return;
  141. APP->scene->removeChild(tooltip);
  142. delete tooltip;
  143. tooltip = NULL;
  144. }
  145. void ParamWidget::step() {
  146. engine::ParamQuantity* pq = getParamQuantity();
  147. if (pq) {
  148. float value = pq->getSmoothValue();
  149. // Trigger change event when the ParamQuantity value changes
  150. if (value != lastValue) {
  151. event::Change eChange;
  152. onChange(eChange);
  153. lastValue = value;
  154. }
  155. }
  156. Widget::step();
  157. }
  158. void ParamWidget::draw(const DrawArgs& args) {
  159. Widget::draw(args);
  160. // Param map indicator
  161. engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
  162. if (paramHandle) {
  163. NVGcolor color = paramHandle->color;
  164. nvgBeginPath(args.vg);
  165. const float radius = 6;
  166. // nvgCircle(args.vg, box.size.x / 2, box.size.y / 2, radius);
  167. nvgRect(args.vg, box.size.x - radius, box.size.y - radius, radius, radius);
  168. nvgFillColor(args.vg, color);
  169. nvgFill(args.vg);
  170. nvgStrokeColor(args.vg, color::mult(color, 0.5));
  171. nvgStrokeWidth(args.vg, 1.0);
  172. nvgStroke(args.vg);
  173. }
  174. }
  175. void ParamWidget::onButton(const event::Button& e) {
  176. OpaqueWidget::onButton(e);
  177. // Touch parameter
  178. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & RACK_MOD_MASK) == 0) {
  179. if (module) {
  180. APP->scene->rack->touchedParam = this;
  181. }
  182. e.consume(this);
  183. }
  184. // Right click to open context menu
  185. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && (e.mods & RACK_MOD_MASK) == 0) {
  186. destroyTooltip();
  187. createContextMenu();
  188. e.consume(this);
  189. }
  190. }
  191. void ParamWidget::onDoubleClick(const event::DoubleClick& e) {
  192. resetAction();
  193. }
  194. void ParamWidget::onEnter(const event::Enter& e) {
  195. createTooltip();
  196. }
  197. void ParamWidget::onLeave(const event::Leave& e) {
  198. destroyTooltip();
  199. }
  200. void ParamWidget::createContextMenu() {
  201. ui::Menu* menu = createMenu();
  202. engine::ParamQuantity* pq = getParamQuantity();
  203. engine::SwitchQuantity* switchQuantity = dynamic_cast<engine::SwitchQuantity*>(pq);
  204. ParamLabel* paramLabel = new ParamLabel;
  205. paramLabel->paramWidget = this;
  206. menu->addChild(paramLabel);
  207. if (switchQuantity) {
  208. int index = (int) std::floor(pq->getValue());
  209. for (int i = 0; i < (int) switchQuantity->labels.size(); i++) {
  210. std::string label = switchQuantity->labels[i];
  211. ParamValueItem* paramValueItem = new ParamValueItem;
  212. paramValueItem->text = label;
  213. paramValueItem->rightText = CHECKMARK(i == index);
  214. paramValueItem->paramWidget = this;
  215. paramValueItem->value = i;
  216. menu->addChild(paramValueItem);
  217. }
  218. }
  219. else {
  220. ParamField* paramField = new ParamField;
  221. paramField->box.size.x = 100;
  222. paramField->setParamWidget(this);
  223. menu->addChild(paramField);
  224. }
  225. if (pq && pq->resetEnabled && pq->isBounded()) {
  226. ParamResetItem* resetItem = new ParamResetItem;
  227. resetItem->text = "Initialize";
  228. resetItem->rightText = "Double-click";
  229. resetItem->paramWidget = this;
  230. menu->addChild(resetItem);
  231. }
  232. // ParamFineItem *fineItem = new ParamFineItem;
  233. // fineItem->text = "Fine adjust";
  234. // fineItem->rightText = RACK_MOD_CTRL_NAME "+drag";
  235. // fineItem->disabled = true;
  236. // menu->addChild(fineItem);
  237. engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
  238. if (paramHandle) {
  239. ParamUnmapItem* unmapItem = new ParamUnmapItem;
  240. unmapItem->text = "Unmap";
  241. unmapItem->rightText = paramHandle->text;
  242. unmapItem->paramWidget = this;
  243. menu->addChild(unmapItem);
  244. }
  245. appendContextMenu(menu);
  246. }
  247. void ParamWidget::resetAction() {
  248. engine::ParamQuantity* pq = getParamQuantity();
  249. if (pq && pq->resetEnabled && pq->isBounded()) {
  250. float oldValue = pq->getValue();
  251. pq->reset();
  252. float newValue = pq->getValue();
  253. if (oldValue != newValue) {
  254. // Push ParamChange history action
  255. history::ParamChange* h = new history::ParamChange;
  256. h->name = "reset parameter";
  257. h->moduleId = module->id;
  258. h->paramId = paramId;
  259. h->oldValue = oldValue;
  260. h->newValue = newValue;
  261. APP->history->push(h);
  262. }
  263. }
  264. }
  265. } // namespace app
  266. } // namespace rack