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.

317 lines
8.2KB

  1. #include <thread>
  2. #include <osdialog.h>
  3. #include <app/Scene.hpp>
  4. #include <app/Browser.hpp>
  5. #include <app/TipWindow.hpp>
  6. #include <app/MenuBar.hpp>
  7. #include <context.hpp>
  8. #include <system.hpp>
  9. #include <network.hpp>
  10. #include <history.hpp>
  11. #include <settings.hpp>
  12. #include <patch.hpp>
  13. #include <asset.hpp>
  14. namespace rack {
  15. namespace app {
  16. struct ResizeHandle : widget::OpaqueWidget {
  17. math::Vec size;
  18. void draw(const DrawArgs& args) override {
  19. nvgBeginPath(args.vg);
  20. nvgMoveTo(args.vg, box.size.x, box.size.y);
  21. nvgLineTo(args.vg, 0, box.size.y);
  22. nvgLineTo(args.vg, box.size.x, 0);
  23. nvgClosePath(args.vg);
  24. nvgFillColor(args.vg, nvgRGBAf(1, 1, 1, 0.15));
  25. nvgFill(args.vg);
  26. }
  27. void onDragStart(const DragStartEvent& e) override {
  28. size = APP->window->getSize();
  29. }
  30. void onDragMove(const DragMoveEvent& e) override {
  31. size = size.plus(e.mouseDelta);
  32. APP->window->setSize(size.round());
  33. }
  34. };
  35. struct Scene::Internal {
  36. ResizeHandle* resizeHandle;
  37. };
  38. Scene::Scene() {
  39. internal = new Internal;
  40. rackScroll = new RackScrollWidget;
  41. addChild(rackScroll);
  42. rack = rackScroll->rackWidget;
  43. menuBar = createMenuBar();
  44. addChild(menuBar);
  45. browser = browserCreate();
  46. browser->hide();
  47. addChild(browser);
  48. if (settings::showTipsOnLaunch) {
  49. addChild(tipWindowCreate());
  50. }
  51. internal->resizeHandle = new ResizeHandle;
  52. internal->resizeHandle->box.size = math::Vec(15, 15);
  53. internal->resizeHandle->hide();
  54. addChild(internal->resizeHandle);
  55. }
  56. Scene::~Scene() {
  57. delete internal;
  58. }
  59. void Scene::step() {
  60. if (APP->window->isFullScreen()) {
  61. // Expand RackScrollWidget to cover entire screen if fullscreen
  62. rackScroll->box.pos.y = 0;
  63. }
  64. else {
  65. // Always show MenuBar if not fullscreen
  66. menuBar->show();
  67. rackScroll->box.pos.y = menuBar->box.size.y;
  68. }
  69. internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
  70. // Resize owned descendants
  71. menuBar->box.size.x = box.size.x;
  72. rackScroll->box.size = box.size.minus(rackScroll->box.pos);
  73. // Autosave periodically
  74. if (settings::autosaveInterval > 0.0) {
  75. double time = system::getTime();
  76. if (time - lastAutosaveTime >= settings::autosaveInterval) {
  77. lastAutosaveTime = time;
  78. APP->patch->saveAutosave();
  79. settings::save();
  80. }
  81. }
  82. Widget::step();
  83. }
  84. void Scene::draw(const DrawArgs& args) {
  85. Widget::draw(args);
  86. }
  87. void Scene::onHover(const HoverEvent& e) {
  88. mousePos = e.pos;
  89. if (mousePos.y < menuBar->box.size.y) {
  90. menuBar->show();
  91. }
  92. OpaqueWidget::onHover(e);
  93. }
  94. void Scene::onDragHover(const DragHoverEvent& e) {
  95. mousePos = e.pos;
  96. OpaqueWidget::onDragHover(e);
  97. }
  98. void Scene::onHoverKey(const HoverKeyEvent& e) {
  99. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  100. // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str());
  101. if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  102. APP->patch->loadTemplateDialog();
  103. e.consume(this);
  104. }
  105. if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  106. APP->window->close();
  107. e.consume(this);
  108. }
  109. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  110. APP->patch->loadDialog();
  111. e.consume(this);
  112. }
  113. if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  114. APP->patch->revertDialog();
  115. e.consume(this);
  116. }
  117. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  118. APP->patch->saveDialog();
  119. e.consume(this);
  120. }
  121. if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  122. APP->patch->saveAsDialog();
  123. e.consume(this);
  124. }
  125. if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  126. APP->history->undo();
  127. e.consume(this);
  128. }
  129. if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  130. APP->history->redo();
  131. e.consume(this);
  132. }
  133. if (e.keyName == "-" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  134. float zoom = settings::zoom;
  135. zoom *= 2;
  136. zoom = std::ceil(zoom - 0.01f) - 1;
  137. zoom /= 2;
  138. settings::zoom = zoom;
  139. settings::zoom = std::fmax(settings::zoom, settings::zoomMin);
  140. e.consume(this);
  141. }
  142. // Numpad has a "+" key, but the main keyboard section hides it under "="
  143. if ((e.keyName == "=" || e.keyName == "+") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  144. float zoom = settings::zoom;
  145. zoom *= 2;
  146. zoom = std::floor(zoom + 0.01f) + 1;
  147. zoom /= 2;
  148. settings::zoom = zoom;
  149. settings::zoom = std::fmin(settings::zoom, settings::zoomMax);
  150. e.consume(this);
  151. }
  152. if ((e.keyName == "0") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  153. settings::zoom = 0.f;
  154. e.consume(this);
  155. }
  156. if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
  157. browser->show();
  158. e.consume(this);
  159. }
  160. if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
  161. system::openBrowser("https://vcvrack.com/manual/");
  162. e.consume(this);
  163. }
  164. if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
  165. settings::cpuMeter ^= true;
  166. e.consume(this);
  167. }
  168. if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) {
  169. APP->window->setFullScreen(!APP->window->isFullScreen());
  170. // The MenuBar will be hidden when the mouse moves over the RackScrollWidget.
  171. // menuBar->hide();
  172. e.consume(this);
  173. }
  174. // Alternate key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding.
  175. if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
  176. if (APP->window->isFullScreen()) {
  177. APP->window->setFullScreen(false);
  178. e.consume(this);
  179. }
  180. }
  181. // Module selections
  182. if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  183. rack->selectAll();
  184. e.consume(this);
  185. }
  186. if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  187. rack->deselectAll();
  188. e.consume(this);
  189. }
  190. if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  191. if (rack->hasSelection()) {
  192. rack->copyClipboardSelection();
  193. e.consume(this);
  194. }
  195. }
  196. if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  197. rack->pasteClipboardAction();
  198. e.consume(this);
  199. }
  200. if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  201. if (rack->hasSelection()) {
  202. rack->resetSelectionAction();
  203. e.consume(this);
  204. }
  205. }
  206. if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  207. if (rack->hasSelection()) {
  208. rack->randomizeSelectionAction();
  209. e.consume(this);
  210. }
  211. }
  212. if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  213. if (rack->hasSelection()) {
  214. rack->disconnectSelectionAction();
  215. e.consume(this);
  216. }
  217. }
  218. if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  219. if (rack->hasSelection()) {
  220. rack->bypassSelectionAction(!rack->isSelectionBypassed());
  221. e.consume(this);
  222. }
  223. }
  224. if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  225. if (rack->hasSelection()) {
  226. rack->cloneSelectionAction();
  227. e.consume(this);
  228. }
  229. }
  230. if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) {
  231. if (rack->hasSelection()) {
  232. rack->deleteSelectionAction();
  233. e.consume(this);
  234. }
  235. }
  236. }
  237. // Scroll RackScrollWidget with arrow keys
  238. if (e.action == RACK_HELD) {
  239. float arrowSpeed = 32.f;
  240. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL)
  241. arrowSpeed /= 4.f;
  242. if ((e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT)
  243. arrowSpeed *= 4.f;
  244. if ((e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT))
  245. arrowSpeed /= 16.f;
  246. if (e.key == GLFW_KEY_LEFT) {
  247. rackScroll->offset.x -= arrowSpeed;
  248. e.consume(this);
  249. }
  250. if (e.key == GLFW_KEY_RIGHT) {
  251. rackScroll->offset.x += arrowSpeed;
  252. e.consume(this);
  253. }
  254. if (e.key == GLFW_KEY_UP) {
  255. rackScroll->offset.y -= arrowSpeed;
  256. e.consume(this);
  257. }
  258. if (e.key == GLFW_KEY_DOWN) {
  259. rackScroll->offset.y += arrowSpeed;
  260. e.consume(this);
  261. }
  262. }
  263. if (e.isConsumed())
  264. return;
  265. OpaqueWidget::onHoverKey(e);
  266. }
  267. void Scene::onPathDrop(const PathDropEvent& e) {
  268. if (e.paths.size() >= 1) {
  269. const std::string& path = e.paths[0];
  270. if (system::getExtension(path) == ".vcv") {
  271. APP->patch->loadPathDialog(path);
  272. e.consume(this);
  273. return;
  274. }
  275. }
  276. OpaqueWidget::onPathDrop(e);
  277. }
  278. } // namespace app
  279. } // namespace rack