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.

380 lines
9.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. 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 keyName '%s' mods %02x", e.key, e.key, e.scancode, e.keyName.c_str(), e.mods);
  132. if (e.isKeyCommand(GLFW_KEY_N, RACK_MOD_CTRL)) {
  133. APP->patch->loadTemplateDialog();
  134. e.consume(this);
  135. }
  136. if (e.isKeyCommand(GLFW_KEY_Q, RACK_MOD_CTRL)) {
  137. APP->window->close();
  138. e.consume(this);
  139. }
  140. if (e.isKeyCommand(GLFW_KEY_O, RACK_MOD_CTRL)) {
  141. APP->patch->loadDialog();
  142. e.consume(this);
  143. }
  144. if (e.isKeyCommand(GLFW_KEY_O, RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  145. APP->patch->revertDialog();
  146. e.consume(this);
  147. }
  148. if (e.isKeyCommand(GLFW_KEY_S, RACK_MOD_CTRL)) {
  149. APP->patch->saveDialog();
  150. e.consume(this);
  151. }
  152. if (e.isKeyCommand(GLFW_KEY_S, RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  153. APP->patch->saveAsDialog();
  154. e.consume(this);
  155. }
  156. if (e.isKeyCommand(GLFW_KEY_Z, RACK_MOD_CTRL)) {
  157. APP->history->undo();
  158. e.consume(this);
  159. }
  160. if (e.isKeyCommand(GLFW_KEY_Z, RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  161. APP->history->redo();
  162. e.consume(this);
  163. }
  164. if (e.isKeyCommand(GLFW_KEY_MINUS, RACK_MOD_CTRL) || e.isKeyCommand(GLFW_KEY_KP_SUBTRACT, 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. if (e.isKeyCommand(GLFW_KEY_EQUAL, RACK_MOD_CTRL)
  173. // The user might hold shift to access the + character
  174. || e.isKeyCommand(GLFW_KEY_EQUAL, RACK_MOD_CTRL | GLFW_MOD_SHIFT)
  175. // Numpad + key
  176. || e.isKeyCommand(GLFW_KEY_KP_ADD, RACK_MOD_CTRL)
  177. // Some layouts (e.g. QWERTZ) have a + key, but GLFW doesn't have a macro for it
  178. || e.isKeyCommand('+', RACK_MOD_CTRL)) {
  179. float zoom = std::log2(APP->scene->rackScroll->getZoom());
  180. zoom *= 2;
  181. zoom = std::floor(zoom + 0.01f) + 1;
  182. zoom /= 2;
  183. APP->scene->rackScroll->setZoom(std::pow(2.f, zoom));
  184. e.consume(this);
  185. }
  186. if (e.isKeyCommand(GLFW_KEY_0, RACK_MOD_CTRL) || e.isKeyCommand(GLFW_KEY_KP_0, RACK_MOD_CTRL)) {
  187. APP->scene->rackScroll->setZoom(1.f);
  188. e.consume(this);
  189. }
  190. if (e.isKeyCommand(GLFW_KEY_F1)) {
  191. system::openBrowser("https://vcvrack.com/manual/");
  192. e.consume(this);
  193. }
  194. if (e.isKeyCommand(GLFW_KEY_F3)) {
  195. settings::cpuMeter ^= true;
  196. e.consume(this);
  197. }
  198. if (e.isKeyCommand(GLFW_KEY_F4)) {
  199. APP->scene->rackScroll->zoomToModules();
  200. e.consume(this);
  201. }
  202. if (e.isKeyCommand(GLFW_KEY_F11)) {
  203. APP->window->setFullScreen(!APP->window->isFullScreen());
  204. // The MenuBar will be hidden when the mouse moves over the RackScrollWidget.
  205. // menuBar->hide();
  206. e.consume(this);
  207. }
  208. // Module selections
  209. if (e.isKeyCommand(GLFW_KEY_A, RACK_MOD_CTRL)) {
  210. rack->selectAll();
  211. e.consume(this);
  212. }
  213. if (e.isKeyCommand(GLFW_KEY_A, RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  214. rack->deselectAll();
  215. e.consume(this);
  216. }
  217. if (e.isKeyCommand(GLFW_KEY_C, RACK_MOD_CTRL)) {
  218. if (rack->hasSelection()) {
  219. rack->copyClipboardSelection();
  220. e.consume(this);
  221. }
  222. }
  223. if (e.isKeyCommand(GLFW_KEY_I, RACK_MOD_CTRL)) {
  224. if (rack->hasSelection()) {
  225. rack->resetSelectionAction();
  226. e.consume(this);
  227. }
  228. }
  229. if (e.isKeyCommand(GLFW_KEY_R, RACK_MOD_CTRL)) {
  230. if (rack->hasSelection()) {
  231. rack->randomizeSelectionAction();
  232. e.consume(this);
  233. }
  234. }
  235. if (e.isKeyCommand(GLFW_KEY_U, RACK_MOD_CTRL)) {
  236. if (rack->hasSelection()) {
  237. rack->disconnectSelectionAction();
  238. e.consume(this);
  239. }
  240. }
  241. if (e.isKeyCommand(GLFW_KEY_E, RACK_MOD_CTRL)) {
  242. if (rack->hasSelection()) {
  243. rack->bypassSelectionAction(!rack->isSelectionBypassed());
  244. e.consume(this);
  245. }
  246. }
  247. if (e.isKeyCommand(GLFW_KEY_D, RACK_MOD_CTRL)) {
  248. if (rack->hasSelection()) {
  249. rack->cloneSelectionAction(false);
  250. e.consume(this);
  251. }
  252. }
  253. if (e.isKeyCommand(GLFW_KEY_D, RACK_MOD_CTRL | GLFW_MOD_SHIFT)) {
  254. if (rack->hasSelection()) {
  255. rack->cloneSelectionAction(true);
  256. e.consume(this);
  257. }
  258. }
  259. if (e.isKeyCommand(GLFW_KEY_DELETE) || e.isKeyCommand(GLFW_KEY_BACKSPACE)) {
  260. if (rack->hasSelection()) {
  261. rack->deleteSelectionAction();
  262. e.consume(this);
  263. }
  264. }
  265. }
  266. // Scroll RackScrollWidget with arrow keys
  267. if (e.action == GLFW_PRESS || e.action == GLFW_RELEASE) {
  268. if (e.key == GLFW_KEY_LEFT) {
  269. internal->heldArrowKeys[0] = (e.action == GLFW_PRESS);
  270. e.consume(this);
  271. }
  272. if (e.key == GLFW_KEY_RIGHT) {
  273. internal->heldArrowKeys[1] = (e.action == GLFW_PRESS);
  274. e.consume(this);
  275. }
  276. if (e.key == GLFW_KEY_UP) {
  277. internal->heldArrowKeys[2] = (e.action == GLFW_PRESS);
  278. e.consume(this);
  279. }
  280. if (e.key == GLFW_KEY_DOWN) {
  281. internal->heldArrowKeys[3] = (e.action == GLFW_PRESS);
  282. e.consume(this);
  283. }
  284. }
  285. if (e.isConsumed())
  286. return;
  287. OpaqueWidget::onHoverKey(e);
  288. if (e.isConsumed())
  289. return;
  290. // Key commands that can be overridden by children
  291. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  292. // Alternative key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding.
  293. if (e.isKeyCommand(GLFW_KEY_ESCAPE, 0)) {
  294. if (APP->window->isFullScreen()) {
  295. APP->window->setFullScreen(false);
  296. e.consume(this);
  297. }
  298. }
  299. if (e.isKeyCommand(GLFW_KEY_V, RACK_MOD_CTRL)) {
  300. rack->pasteClipboardAction();
  301. e.consume(this);
  302. }
  303. if (e.isKeyCommand(GLFW_KEY_ENTER) || e.isKeyCommand(GLFW_KEY_KP_ENTER)) {
  304. browser->show();
  305. e.consume(this);
  306. }
  307. }
  308. }
  309. void Scene::onPathDrop(const PathDropEvent& e) {
  310. if (e.paths.size() >= 1) {
  311. const std::string& path = e.paths[0];
  312. std::string extension = system::getExtension(path);
  313. if (extension == ".vcv") {
  314. APP->patch->loadPathDialog(path);
  315. e.consume(this);
  316. return;
  317. }
  318. if (extension == ".vcvs") {
  319. APP->scene->rack->loadSelection(path);
  320. e.consume(this);
  321. return;
  322. }
  323. }
  324. OpaqueWidget::onPathDrop(e);
  325. }
  326. } // namespace app
  327. } // namespace rack