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.

323 lines
7.8KB

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