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