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.

163 lines
4.8KB

  1. #include <ui/ScrollWidget.hpp>
  2. #include <app.hpp>
  3. namespace rack {
  4. namespace ui {
  5. ScrollWidget::ScrollWidget() {
  6. container = new widget::Widget;
  7. addChild(container);
  8. horizontalScrollBar = new ScrollBar;
  9. horizontalScrollBar->orientation = ScrollBar::HORIZONTAL;
  10. horizontalScrollBar->visible = false;
  11. addChild(horizontalScrollBar);
  12. verticalScrollBar = new ScrollBar;
  13. verticalScrollBar->orientation = ScrollBar::VERTICAL;
  14. verticalScrollBar->visible = false;
  15. addChild(verticalScrollBar);
  16. }
  17. void ScrollWidget::scrollTo(math::Rect r) {
  18. math::Rect bound = math::Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos);
  19. offset = offset.clampSafe(bound);
  20. }
  21. void ScrollWidget::draw(const DrawArgs& args) {
  22. nvgScissor(args.vg, RECT_ARGS(args.clipBox));
  23. Widget::draw(args);
  24. nvgResetScissor(args.vg);
  25. }
  26. void ScrollWidget::step() {
  27. Widget::step();
  28. // Clamp scroll offset
  29. math::Rect containerBox = container->getChildrenBoundingBox();
  30. math::Rect offsetBounds = containerBox;
  31. offsetBounds.size = offsetBounds.size.minus(box.size);
  32. offset = offset.clamp(offsetBounds);
  33. // Update the container's position from the offset
  34. container->box.pos = offset.neg().round();
  35. // Update scrollbar offsets and sizes
  36. math::Vec scrollbarOffset = offset.minus(containerBox.pos).div(offsetBounds.size);
  37. math::Vec scrollbarSize = box.size.div(containerBox.size);
  38. horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0);
  39. verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0);
  40. horizontalScrollBar->offset = scrollbarOffset.x;
  41. verticalScrollBar->offset = scrollbarOffset.y;
  42. horizontalScrollBar->size = scrollbarSize.x;
  43. verticalScrollBar->size = scrollbarSize.y;
  44. // Reposition and resize scroll bars
  45. math::Vec inner = box.size.minus(math::Vec(verticalScrollBar->box.size.x, horizontalScrollBar->box.size.y));
  46. horizontalScrollBar->box.pos.y = inner.y;
  47. verticalScrollBar->box.pos.x = inner.x;
  48. horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x;
  49. verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y;
  50. }
  51. void ScrollWidget::onButton(const event::Button& e) {
  52. Widget::onButton(e);
  53. if (e.isConsumed())
  54. return;
  55. // Consume right button only if the scrollbars are visible
  56. if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
  57. return;
  58. if (e.button == GLFW_MOUSE_BUTTON_MIDDLE) {
  59. e.consume(this);
  60. }
  61. }
  62. void ScrollWidget::onDragStart(const event::DragStart& e) {
  63. if (e.button == GLFW_MOUSE_BUTTON_MIDDLE) {
  64. e.consume(this);
  65. }
  66. }
  67. void ScrollWidget::onDragMove(const event::DragMove& e) {
  68. // Scroll only if the scrollbars are visible
  69. if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
  70. return;
  71. offset = offset.minus(e.mouseDelta);
  72. }
  73. void ScrollWidget::onHoverScroll(const event::HoverScroll& e) {
  74. OpaqueWidget::onHoverScroll(e);
  75. if (e.isConsumed())
  76. return;
  77. // Scroll only if the scrollbars are visible
  78. if (!(horizontalScrollBar->visible || verticalScrollBar->visible))
  79. return;
  80. math::Vec scrollDelta = e.scrollDelta;
  81. // Flip coordinates if shift is held
  82. // Mac (or GLFW?) already does this for us.
  83. #if !defined ARCH_MAC
  84. if ((APP->window->getMods() & RACK_MOD_MASK) == GLFW_MOD_SHIFT)
  85. scrollDelta = scrollDelta.flip();
  86. #endif
  87. offset = offset.minus(scrollDelta);
  88. e.consume(this);
  89. }
  90. void ScrollWidget::onHoverKey(const event::HoverKey& e) {
  91. OpaqueWidget::onHoverKey(e);
  92. if (e.isConsumed())
  93. return;
  94. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  95. if (e.key == GLFW_KEY_PAGE_UP && (e.mods & RACK_MOD_MASK) == 0) {
  96. offset.y -= box.size.y * 0.5;
  97. e.consume(this);
  98. }
  99. else if (e.key == GLFW_KEY_PAGE_UP && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
  100. offset.x -= box.size.x * 0.5;
  101. e.consume(this);
  102. }
  103. else if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == 0) {
  104. offset.y += box.size.y * 0.5;
  105. e.consume(this);
  106. }
  107. else if (e.key == GLFW_KEY_PAGE_DOWN && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
  108. offset.x += box.size.x * 0.5;
  109. e.consume(this);
  110. }
  111. else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == 0) {
  112. math::Rect containerBox = container->getChildrenBoundingBox();
  113. offset.y = containerBox.getTop();
  114. e.consume(this);
  115. }
  116. else if (e.key == GLFW_KEY_HOME && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
  117. math::Rect containerBox = container->getChildrenBoundingBox();
  118. offset.x = containerBox.getLeft();
  119. e.consume(this);
  120. }
  121. else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == 0) {
  122. math::Rect containerBox = container->getChildrenBoundingBox();
  123. offset.y = containerBox.getBottom();
  124. e.consume(this);
  125. }
  126. else if (e.key == GLFW_KEY_END && (e.mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) {
  127. math::Rect containerBox = container->getChildrenBoundingBox();
  128. offset.x = containerBox.getRight();
  129. e.consume(this);
  130. }
  131. }
  132. }
  133. } // namespace ui
  134. } // namespace rack