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.

322 lines
7.5KB

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