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.

375 lines
9.6KB

  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. double lastAutosaveTime = 0.0;
  38. bool heldArrowKeys[4] = {};
  39. };
  40. Scene::Scene() {
  41. internal = new Internal;
  42. rackScroll = new RackScrollWidget;
  43. addChild(rackScroll);
  44. rack = rackScroll->rackWidget;
  45. menuBar = createMenuBar();
  46. addChild(menuBar);
  47. browser = browserCreate();
  48. browser->hide();
  49. addChild(browser);
  50. if (settings::showTipsOnLaunch) {
  51. addChild(tipWindowCreate());
  52. }
  53. internal->resizeHandle = new ResizeHandle;
  54. internal->resizeHandle->box.size = math::Vec(15, 15);
  55. internal->resizeHandle->hide();
  56. addChild(internal->resizeHandle);
  57. }
  58. Scene::~Scene() {
  59. delete internal;
  60. }
  61. math::Vec Scene::getMousePos() {
  62. return mousePos;
  63. }
  64. void Scene::step() {
  65. if (APP->window->isFullScreen()) {
  66. // Expand RackScrollWidget to cover entire screen if fullscreen
  67. rackScroll->box.pos.y = 0;
  68. }
  69. else {
  70. // Always show MenuBar if not fullscreen
  71. menuBar->show();
  72. rackScroll->box.pos.y = menuBar->box.size.y;
  73. }
  74. internal->resizeHandle->box.pos = box.size.minus(internal->resizeHandle->box.size);
  75. // Resize owned descendants
  76. menuBar->box.size.x = box.size.x;
  77. rackScroll->box.size = box.size.minus(rackScroll->box.pos);
  78. // Autosave periodically
  79. if (settings::autosaveInterval > 0.0) {
  80. double time = system::getTime();
  81. if (time - internal->lastAutosaveTime >= settings::autosaveInterval) {
  82. internal->lastAutosaveTime = time;
  83. APP->patch->saveAutosave();
  84. settings::save();
  85. }
  86. }
  87. // Scroll RackScrollWidget with arrow keys
  88. math::Vec arrowDelta;
  89. if (internal->heldArrowKeys[0]) {
  90. arrowDelta.x -= 1;
  91. }
  92. if (internal->heldArrowKeys[1]) {
  93. arrowDelta.x += 1;
  94. }
  95. if (internal->heldArrowKeys[2]) {
  96. arrowDelta.y -= 1;
  97. }
  98. if (internal->heldArrowKeys[3]) {
  99. arrowDelta.y += 1;
  100. }
  101. if (!arrowDelta.isZero()) {
  102. int mods = APP->window->getMods();
  103. float arrowSpeed = 32.f;
  104. if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL)
  105. arrowSpeed /= 4.f;
  106. if ((mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT)
  107. arrowSpeed *= 4.f;
  108. if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT))
  109. arrowSpeed /= 16.f;
  110. rackScroll->offset += arrowDelta * arrowSpeed;
  111. }
  112. Widget::step();
  113. }
  114. void Scene::draw(const DrawArgs& args) {
  115. Widget::draw(args);
  116. }
  117. void Scene::onHover(const HoverEvent& e) {
  118. mousePos = e.pos;
  119. if (mousePos.y < menuBar->box.size.y) {
  120. menuBar->show();
  121. }
  122. OpaqueWidget::onHover(e);
  123. }
  124. void Scene::onDragHover(const DragHoverEvent& e) {
  125. mousePos = e.pos;
  126. OpaqueWidget::onDragHover(e);
  127. }
  128. void Scene::onHoverKey(const HoverKeyEvent& e) {
  129. // Key commands that override children
  130. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  131. // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str());
  132. if (e.key == GLFW_KEY_N && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  133. APP->patch->loadTemplateDialog();
  134. e.consume(this);
  135. }
  136. if (e.key == GLFW_KEY_Q && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  137. APP->window->close();
  138. e.consume(this);
  139. }
  140. if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  141. APP->patch->loadDialog();
  142. e.consume(this);
  143. }
  144. if (e.key == GLFW_KEY_O && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  145. APP->patch->revertDialog();
  146. e.consume(this);
  147. }
  148. if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  149. APP->patch->saveDialog();
  150. e.consume(this);
  151. }
  152. if (e.key == GLFW_KEY_S && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  153. APP->patch->saveAsDialog();
  154. e.consume(this);
  155. }
  156. if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  157. APP->history->undo();
  158. e.consume(this);
  159. }
  160. if (e.key == GLFW_KEY_Z && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  161. APP->history->redo();
  162. e.consume(this);
  163. }
  164. if ((e.key == GLFW_KEY_MINUS || e.key == GLFW_KEY_KP_SUBTRACT) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  165. float zoom = std::log2(APP->scene->rackScroll->getZoom());
  166. zoom *= 2;
  167. zoom = std::ceil(zoom - 0.01f) - 1;
  168. zoom /= 2;
  169. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  170. e.consume(this);
  171. }
  172. // Numpad has a "+" key, but the main keyboard section hides it under "="
  173. if ((e.key == GLFW_KEY_EQUAL || e.key == GLFW_KEY_KP_ADD) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  174. float zoom = std::log2(APP->scene->rackScroll->getZoom());
  175. zoom *= 2;
  176. zoom = std::floor(zoom + 0.01f) + 1;
  177. zoom /= 2;
  178. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  179. e.consume(this);
  180. }
  181. if ((e.key == GLFW_KEY_0 || e.key == GLFW_KEY_KP_0) && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  182. APP->scene->rackScroll->setZoom(1.f);
  183. e.consume(this);
  184. }
  185. if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) {
  186. system::openBrowser("https://vcvrack.com/manual/");
  187. e.consume(this);
  188. }
  189. if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) {
  190. settings::cpuMeter ^= true;
  191. e.consume(this);
  192. }
  193. if (e.key == GLFW_KEY_F4 && (e.mods & RACK_MOD_MASK) == 0) {
  194. APP->scene->rackScroll->zoomToModules();
  195. e.consume(this);
  196. }
  197. if (e.key == GLFW_KEY_F11 && (e.mods & RACK_MOD_MASK) == 0) {
  198. APP->window->setFullScreen(!APP->window->isFullScreen());
  199. // The MenuBar will be hidden when the mouse moves over the RackScrollWidget.
  200. // menuBar->hide();
  201. e.consume(this);
  202. }
  203. // Module selections
  204. if (e.key == GLFW_KEY_A && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  205. rack->selectAll();
  206. e.consume(this);
  207. }
  208. if (e.key == GLFW_KEY_A && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  209. rack->deselectAll();
  210. e.consume(this);
  211. }
  212. if (e.key == GLFW_KEY_C && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  213. if (rack->hasSelection()) {
  214. rack->copyClipboardSelection();
  215. e.consume(this);
  216. }
  217. }
  218. if (e.key == GLFW_KEY_I && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  219. if (rack->hasSelection()) {
  220. rack->resetSelectionAction();
  221. e.consume(this);
  222. }
  223. }
  224. if (e.key == GLFW_KEY_R && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  225. if (rack->hasSelection()) {
  226. rack->randomizeSelectionAction();
  227. e.consume(this);
  228. }
  229. }
  230. if (e.key == GLFW_KEY_U && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  231. if (rack->hasSelection()) {
  232. rack->disconnectSelectionAction();
  233. e.consume(this);
  234. }
  235. }
  236. if (e.key == GLFW_KEY_E && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  237. if (rack->hasSelection()) {
  238. rack->bypassSelectionAction(!rack->isSelectionBypassed());
  239. e.consume(this);
  240. }
  241. }
  242. if (e.key == GLFW_KEY_D && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  243. if (rack->hasSelection()) {
  244. rack->cloneSelectionAction(false);
  245. e.consume(this);
  246. }
  247. }
  248. if (e.key == GLFW_KEY_D && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  249. if (rack->hasSelection()) {
  250. rack->cloneSelectionAction(true);
  251. e.consume(this);
  252. }
  253. }
  254. if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) {
  255. if (rack->hasSelection()) {
  256. rack->deleteSelectionAction();
  257. e.consume(this);
  258. }
  259. }
  260. }
  261. // Scroll RackScrollWidget with arrow keys
  262. if (e.action == GLFW_PRESS || e.action == GLFW_RELEASE) {
  263. if (e.key == GLFW_KEY_LEFT) {
  264. internal->heldArrowKeys[0] = (e.action == GLFW_PRESS);
  265. e.consume(this);
  266. }
  267. if (e.key == GLFW_KEY_RIGHT) {
  268. internal->heldArrowKeys[1] = (e.action == GLFW_PRESS);
  269. e.consume(this);
  270. }
  271. if (e.key == GLFW_KEY_UP) {
  272. internal->heldArrowKeys[2] = (e.action == GLFW_PRESS);
  273. e.consume(this);
  274. }
  275. if (e.key == GLFW_KEY_DOWN) {
  276. internal->heldArrowKeys[3] = (e.action == GLFW_PRESS);
  277. e.consume(this);
  278. }
  279. }
  280. if (e.isConsumed())
  281. return;
  282. OpaqueWidget::onHoverKey(e);
  283. if (e.isConsumed())
  284. return;
  285. // Key commands that can be overridden by children
  286. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  287. // Alternative key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding.
  288. if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) {
  289. if (APP->window->isFullScreen()) {
  290. APP->window->setFullScreen(false);
  291. e.consume(this);
  292. }
  293. }
  294. if (e.key == GLFW_KEY_V && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  295. rack->pasteClipboardAction();
  296. e.consume(this);
  297. }
  298. if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) {
  299. browser->show();
  300. e.consume(this);
  301. }
  302. }
  303. }
  304. void Scene::onPathDrop(const PathDropEvent& e) {
  305. if (e.paths.size() >= 1) {
  306. const std::string& path = e.paths[0];
  307. std::string extension = system::getExtension(path);
  308. if (extension == ".vcv") {
  309. APP->patch->loadPathDialog(path);
  310. e.consume(this);
  311. return;
  312. }
  313. if (extension == ".vcvs") {
  314. APP->scene->rack->loadSelection(path);
  315. e.consume(this);
  316. return;
  317. }
  318. }
  319. OpaqueWidget::onPathDrop(e);
  320. }
  321. } // namespace app
  322. } // namespace rack