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.

350 lines
8.2KB

  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->setSelectedWidget(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. struct ParamWidget::Internal {
  128. ui::Tooltip* tooltip = NULL;
  129. /** For triggering the Change event. `*/
  130. float lastValue = NAN;
  131. };
  132. ParamWidget::ParamWidget() {
  133. internal = new Internal;
  134. }
  135. ParamWidget::~ParamWidget() {
  136. delete internal;
  137. }
  138. void ParamWidget::createTooltip() {
  139. if (!settings::tooltips)
  140. return;
  141. if (internal->tooltip)
  142. return;
  143. if (!module)
  144. return;
  145. ParamTooltip* tooltip = new ParamTooltip;
  146. tooltip->paramWidget = this;
  147. APP->scene->addChild(tooltip);
  148. internal->tooltip = tooltip;
  149. }
  150. void ParamWidget::destroyTooltip() {
  151. if (!internal->tooltip)
  152. return;
  153. APP->scene->removeChild(internal->tooltip);
  154. delete internal->tooltip;
  155. internal->tooltip = NULL;
  156. }
  157. void ParamWidget::step() {
  158. engine::ParamQuantity* pq = getParamQuantity();
  159. if (pq) {
  160. float value = pq->getSmoothValue();
  161. // Dispatch change event when the ParamQuantity value changes
  162. if (value != internal->lastValue) {
  163. ChangeEvent eChange;
  164. onChange(eChange);
  165. internal->lastValue = value;
  166. }
  167. }
  168. Widget::step();
  169. }
  170. void ParamWidget::draw(const DrawArgs& args) {
  171. Widget::draw(args);
  172. // Param map indicator
  173. engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
  174. if (paramHandle) {
  175. NVGcolor color = paramHandle->color;
  176. nvgBeginPath(args.vg);
  177. const float radius = 6;
  178. // nvgCircle(args.vg, box.size.x / 2, box.size.y / 2, radius);
  179. nvgRect(args.vg, box.size.x - radius, box.size.y - radius, radius, radius);
  180. nvgFillColor(args.vg, color);
  181. nvgFill(args.vg);
  182. nvgStrokeColor(args.vg, color::mult(color, 0.5));
  183. nvgStrokeWidth(args.vg, 1.0);
  184. nvgStroke(args.vg);
  185. }
  186. }
  187. void ParamWidget::onButton(const ButtonEvent& e) {
  188. OpaqueWidget::onButton(e);
  189. // Touch parameter
  190. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT && (e.mods & RACK_MOD_MASK) == 0) {
  191. if (module) {
  192. APP->scene->rack->touchedParam = this;
  193. }
  194. e.consume(this);
  195. }
  196. // Right click to open context menu
  197. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT && (e.mods & RACK_MOD_MASK) == 0) {
  198. destroyTooltip();
  199. createContextMenu();
  200. e.consume(this);
  201. }
  202. }
  203. void ParamWidget::onDoubleClick(const DoubleClickEvent& e) {
  204. resetAction();
  205. }
  206. void ParamWidget::onEnter(const EnterEvent& e) {
  207. createTooltip();
  208. }
  209. void ParamWidget::onLeave(const LeaveEvent& e) {
  210. destroyTooltip();
  211. }
  212. void ParamWidget::createContextMenu() {
  213. ui::Menu* menu = createMenu();
  214. engine::ParamQuantity* pq = getParamQuantity();
  215. engine::SwitchQuantity* switchQuantity = dynamic_cast<engine::SwitchQuantity*>(pq);
  216. ParamLabel* paramLabel = new ParamLabel;
  217. paramLabel->paramWidget = this;
  218. menu->addChild(paramLabel);
  219. if (switchQuantity) {
  220. float minValue = pq->getMinValue();
  221. int index = (int) std::floor(pq->getValue() - minValue);
  222. int numStates = switchQuantity->labels.size();
  223. for (int i = 0; i < numStates; i++) {
  224. std::string label = switchQuantity->labels[i];
  225. ParamValueItem* paramValueItem = new ParamValueItem;
  226. paramValueItem->text = label;
  227. paramValueItem->rightText = CHECKMARK(i == index);
  228. paramValueItem->paramWidget = this;
  229. paramValueItem->value = minValue + i;
  230. menu->addChild(paramValueItem);
  231. }
  232. if (numStates > 0) {
  233. menu->addChild(new ui::MenuSeparator);
  234. }
  235. }
  236. else {
  237. ParamField* paramField = new ParamField;
  238. paramField->box.size.x = 100;
  239. paramField->setParamWidget(this);
  240. menu->addChild(paramField);
  241. }
  242. if (pq && pq->resetEnabled && pq->isBounded()) {
  243. ParamResetItem* resetItem = new ParamResetItem;
  244. resetItem->text = "Initialize";
  245. resetItem->rightText = "Double-click";
  246. resetItem->paramWidget = this;
  247. menu->addChild(resetItem);
  248. }
  249. // ParamFineItem *fineItem = new ParamFineItem;
  250. // fineItem->text = "Fine adjust";
  251. // fineItem->rightText = RACK_MOD_CTRL_NAME "+drag";
  252. // fineItem->disabled = true;
  253. // menu->addChild(fineItem);
  254. engine::ParamHandle* paramHandle = module ? APP->engine->getParamHandle(module->id, paramId) : NULL;
  255. if (paramHandle) {
  256. ParamUnmapItem* unmapItem = new ParamUnmapItem;
  257. unmapItem->text = "Unmap";
  258. unmapItem->rightText = paramHandle->text;
  259. unmapItem->paramWidget = this;
  260. menu->addChild(unmapItem);
  261. }
  262. appendContextMenu(menu);
  263. }
  264. void ParamWidget::resetAction() {
  265. engine::ParamQuantity* pq = getParamQuantity();
  266. if (pq && pq->resetEnabled && pq->isBounded()) {
  267. float oldValue = pq->getValue();
  268. pq->reset();
  269. float newValue = pq->getValue();
  270. if (oldValue != newValue) {
  271. // Push ParamChange history action
  272. history::ParamChange* h = new history::ParamChange;
  273. h->name = "reset parameter";
  274. h->moduleId = module->id;
  275. h->paramId = paramId;
  276. h->oldValue = oldValue;
  277. h->newValue = newValue;
  278. APP->history->push(h);
  279. }
  280. }
  281. }
  282. } // namespace app
  283. } // namespace rack