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.

162 lines
4.3KB

  1. #pragma once
  2. #include "widgets/OpaqueWidget.hpp"
  3. #include "ui/common.hpp"
  4. #include "event.hpp"
  5. namespace rack {
  6. /** Parent must be a ScrollWidget */
  7. struct ScrollBar : OpaqueWidget {
  8. enum Orientation {
  9. VERTICAL,
  10. HORIZONTAL
  11. };
  12. Orientation orientation;
  13. BNDwidgetState state = BND_DEFAULT;
  14. float offset = 0.0;
  15. float size = 0.0;
  16. ScrollBar() {
  17. box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT);
  18. }
  19. void draw(NVGcontext *vg) override {
  20. bndScrollBar(vg, 0.0, 0.0, box.size.x, box.size.y, state, offset, size);
  21. }
  22. void onDragStart(event::DragStart &e) override {
  23. state = BND_ACTIVE;
  24. windowCursorLock();
  25. }
  26. void onDragMove(event::DragMove &e) override;
  27. void onDragEnd(event::DragEnd &e) override {
  28. state = BND_DEFAULT;
  29. windowCursorUnlock();
  30. }
  31. };
  32. /** Handles a container with ScrollBar */
  33. struct ScrollWidget : OpaqueWidget {
  34. Widget *container;
  35. ScrollBar *horizontalScrollBar;
  36. ScrollBar *verticalScrollBar;
  37. Vec offset;
  38. ScrollWidget() {
  39. container = new Widget;
  40. addChild(container);
  41. horizontalScrollBar = new ScrollBar;
  42. horizontalScrollBar->orientation = ScrollBar::HORIZONTAL;
  43. horizontalScrollBar->visible = false;
  44. addChild(horizontalScrollBar);
  45. verticalScrollBar = new ScrollBar;
  46. verticalScrollBar->orientation = ScrollBar::VERTICAL;
  47. verticalScrollBar->visible = false;
  48. addChild(verticalScrollBar);
  49. }
  50. void scrollTo(Rect r) {
  51. Rect bound = Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos);
  52. offset = offset.clampBetween(bound);
  53. }
  54. void draw(NVGcontext *vg) override {
  55. nvgScissor(vg, 0, 0, box.size.x, box.size.y);
  56. Widget::draw(vg);
  57. nvgResetScissor(vg);
  58. }
  59. void step() override {
  60. Widget::step();
  61. // Clamp scroll offset
  62. Vec containerCorner = container->getChildrenBoundingBox().getBottomRight();
  63. Rect containerBox = Rect(Vec(0, 0), containerCorner.minus(box.size));
  64. offset = offset.clamp(containerBox);
  65. // Lock offset to top/left if no scrollbar will display
  66. if (containerBox.size.x < 0.0)
  67. offset.x = 0.0;
  68. if (containerBox.size.y < 0.0)
  69. offset.y = 0.0;
  70. // Update the container's positions from the offset
  71. container->box.pos = offset.neg().round();
  72. // Update scrollbar offsets and sizes
  73. Vec viewportSize = container->getChildrenBoundingBox().getBottomRight();
  74. Vec scrollbarOffset = offset.div(viewportSize.minus(box.size));
  75. Vec scrollbarSize = box.size.div(viewportSize);
  76. horizontalScrollBar->visible = (0.0 < scrollbarSize.x && scrollbarSize.x < 1.0);
  77. verticalScrollBar->visible = (0.0 < scrollbarSize.y && scrollbarSize.y < 1.0);
  78. horizontalScrollBar->offset = scrollbarOffset.x;
  79. verticalScrollBar->offset = scrollbarOffset.y;
  80. horizontalScrollBar->size = scrollbarSize.x;
  81. verticalScrollBar->size = scrollbarSize.y;
  82. // Resize scroll bars
  83. Vec inner = Vec(box.size.x - verticalScrollBar->box.size.x, box.size.y - horizontalScrollBar->box.size.y);
  84. horizontalScrollBar->box.pos.y = inner.y;
  85. verticalScrollBar->box.pos.x = inner.x;
  86. horizontalScrollBar->box.size.x = verticalScrollBar->visible ? inner.x : box.size.x;
  87. verticalScrollBar->box.size.y = horizontalScrollBar->visible ? inner.y : box.size.y;
  88. }
  89. void onHover(event::Hover &e) override {
  90. // Scroll with arrow keys
  91. if (!event::gContext->selectedWidget) {
  92. float arrowSpeed = 30.0;
  93. if (windowIsShiftPressed() && windowIsModPressed())
  94. arrowSpeed /= 16.0;
  95. else if (windowIsShiftPressed())
  96. arrowSpeed *= 4.0;
  97. else if (windowIsModPressed())
  98. arrowSpeed /= 4.0;
  99. if (glfwGetKey(gWindow, GLFW_KEY_LEFT) == GLFW_PRESS) {
  100. offset.x -= arrowSpeed;
  101. }
  102. if (glfwGetKey(gWindow, GLFW_KEY_RIGHT) == GLFW_PRESS) {
  103. offset.x += arrowSpeed;
  104. }
  105. if (glfwGetKey(gWindow, GLFW_KEY_UP) == GLFW_PRESS) {
  106. offset.y -= arrowSpeed;
  107. }
  108. if (glfwGetKey(gWindow, GLFW_KEY_DOWN) == GLFW_PRESS) {
  109. offset.y += arrowSpeed;
  110. }
  111. }
  112. OpaqueWidget::onHover(e);
  113. }
  114. void onHoverScroll(event::HoverScroll &e) override {
  115. offset = offset.minus(e.scrollDelta);
  116. e.target = this;
  117. }
  118. void onHoverKey(event::HoverKey &e) override {
  119. OpaqueWidget::onHoverKey(e);
  120. }
  121. };
  122. inline void ScrollBar::onDragMove(event::DragMove &e) {
  123. ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent);
  124. assert(scrollWidget);
  125. if (orientation == HORIZONTAL)
  126. scrollWidget->offset.x += e.mouseDelta.x;
  127. else
  128. scrollWidget->offset.y += e.mouseDelta.y;
  129. }
  130. } // namespace rack