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.

206 lines
5.0KB

  1. #include <app/RackScrollWidget.hpp>
  2. #include <app/Scene.hpp>
  3. #include <app/RackWidget.hpp>
  4. #include <app/ModuleWidget.hpp>
  5. #include <app/PortWidget.hpp>
  6. #include <window/Window.hpp>
  7. #include <context.hpp>
  8. #include <settings.hpp>
  9. namespace rack {
  10. namespace app {
  11. struct RackScrollWidget::Internal {
  12. /** For viewport expanding */
  13. float oldZoom = 0.f;
  14. math::Vec oldOffset;
  15. };
  16. RackScrollWidget::RackScrollWidget() {
  17. internal = new Internal;
  18. zoomWidget = new widget::ZoomWidget;
  19. container->addChild(zoomWidget);
  20. rackWidget = new RackWidget;
  21. rackWidget->box.size = RACK_OFFSET.mult(2);
  22. zoomWidget->addChild(rackWidget);
  23. reset();
  24. }
  25. RackScrollWidget::~RackScrollWidget() {
  26. delete internal;
  27. }
  28. void RackScrollWidget::reset() {
  29. offset = RACK_OFFSET * zoomWidget->getZoom() - math::Vec(30, 30);
  30. }
  31. math::Vec RackScrollWidget::getGridOffset() {
  32. return (offset / zoomWidget->getZoom() - RACK_OFFSET) / RACK_GRID_SIZE;
  33. }
  34. void RackScrollWidget::setGridOffset(math::Vec gridOffset) {
  35. offset = (gridOffset * RACK_GRID_SIZE + RACK_OFFSET) * zoomWidget->getZoom();
  36. }
  37. float RackScrollWidget::getZoom() {
  38. return zoomWidget->getZoom();
  39. }
  40. void RackScrollWidget::setZoom(float zoom) {
  41. setZoom(zoom, getSize().div(2));
  42. }
  43. void RackScrollWidget::setZoom(float zoom, math::Vec pivot) {
  44. zoom = math::clamp(zoom, std::pow(2.f, -2), std::pow(2.f, 2));
  45. offset = (offset + pivot) * (zoom / zoomWidget->getZoom()) - pivot;
  46. zoomWidget->setZoom(zoom);
  47. }
  48. void RackScrollWidget::zoomToModules() {
  49. widget::Widget* moduleContainer = rackWidget->getModuleContainer();
  50. math::Rect bound = moduleContainer->getChildrenBoundingBox();
  51. zoomToBound(bound);
  52. }
  53. void RackScrollWidget::zoomToBound(math::Rect bound) {
  54. if (!bound.pos.isFinite())
  55. return;
  56. bound = bound.grow(math::Vec(24, 24));
  57. math::Vec size = getSize();
  58. float zoom = std::min(size.x / bound.size.x, size.y / bound.size.y);
  59. offset = bound.getCenter() * zoom - size / 2;
  60. zoomWidget->setZoom(zoom);
  61. }
  62. void RackScrollWidget::step() {
  63. float zoom = getZoom();
  64. // Compute module bounding box
  65. math::Rect moduleBox = rackWidget->getModuleContainer()->getChildrenBoundingBox();
  66. if (!moduleBox.size.isFinite())
  67. moduleBox = math::Rect(RACK_OFFSET, math::Vec(0, 0));
  68. // Expand moduleBox by a screen size
  69. math::Rect scrollBox = moduleBox;
  70. scrollBox.pos = scrollBox.pos.mult(zoom);
  71. scrollBox.size = scrollBox.size.mult(zoom);
  72. scrollBox = scrollBox.grow(box.size.mult(0.9));
  73. // Expand to the current viewport box so that moving modules (and thus changing the module bounding box) doesn't clamp the scroll offset.
  74. if (zoom == internal->oldZoom) {
  75. math::Rect viewportBox;
  76. viewportBox.pos = internal->oldOffset;
  77. viewportBox.size = box.size;
  78. scrollBox = scrollBox.expand(viewportBox);
  79. }
  80. // Reposition widgets
  81. zoomWidget->box = scrollBox;
  82. rackWidget->box.pos = scrollBox.pos.div(zoom).neg();
  83. // Scroll rack if dragging certain widgets near the edge of the screen
  84. math::Vec pos = APP->scene->mousePos - box.pos;
  85. math::Rect viewport = getViewport(box.zeroPos());
  86. widget::Widget* dw = APP->event->getDraggedWidget();
  87. if (dw && APP->event->dragButton == GLFW_MOUSE_BUTTON_LEFT &&
  88. (dynamic_cast<RackWidget*>(dw) || dynamic_cast<ModuleWidget*>(dw) || dynamic_cast<PortWidget*>(dw))) {
  89. float margin = 1.0;
  90. float speed = 15.0;
  91. if (pos.x <= viewport.pos.x + margin)
  92. offset.x -= speed;
  93. if (pos.x >= viewport.pos.x + viewport.size.x - margin)
  94. offset.x += speed;
  95. if (pos.y <= viewport.pos.y + margin)
  96. offset.y -= speed;
  97. if (pos.y >= viewport.pos.y + viewport.size.y - margin)
  98. offset.y += speed;
  99. }
  100. // Hide scrollbars if fullscreen
  101. hideScrollbars = APP->window->isFullScreen();
  102. ScrollWidget::step();
  103. internal->oldOffset = offset;
  104. internal->oldZoom = zoom;
  105. }
  106. void RackScrollWidget::draw(const DrawArgs& args) {
  107. ScrollWidget::draw(args);
  108. }
  109. void RackScrollWidget::onHoverKey(const HoverKeyEvent& e) {
  110. ScrollWidget::onHoverKey(e);
  111. }
  112. void RackScrollWidget::onHoverScroll(const HoverScrollEvent& e) {
  113. if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  114. // Increase zoom
  115. float zoomDelta = e.scrollDelta.y / 50 / 4;
  116. if (settings::invertZoom)
  117. zoomDelta *= -1;
  118. float zoom = getZoom() * std::pow(2.f, zoomDelta);
  119. setZoom(zoom, e.pos);
  120. e.consume(this);
  121. }
  122. if (e.isConsumed())
  123. return;
  124. ScrollWidget::onHoverScroll(e);
  125. }
  126. void RackScrollWidget::onHover(const HoverEvent& e) {
  127. ScrollWidget::onHover(e);
  128. // Hide menu bar if fullscreen and moving mouse over the RackScrollWidget
  129. if (APP->window->isFullScreen()) {
  130. APP->scene->menuBar->hide();
  131. }
  132. }
  133. void RackScrollWidget::onButton(const ButtonEvent& e) {
  134. ScrollWidget::onButton(e);
  135. if (e.isConsumed())
  136. return;
  137. // Zoom in/out with extra mouse buttons
  138. if (e.action == GLFW_PRESS) {
  139. if (e.button == GLFW_MOUSE_BUTTON_4) {
  140. float zoom = getZoom() * std::pow(2.f, -0.5f);
  141. setZoom(zoom, e.pos);
  142. e.consume(this);
  143. }
  144. if (e.button == GLFW_MOUSE_BUTTON_5) {
  145. float zoom = getZoom() * std::pow(2.f, 0.5f);
  146. setZoom(zoom, e.pos);
  147. e.consume(this);
  148. }
  149. }
  150. }
  151. } // namespace app
  152. } // namespace rack