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.

159 lines
4.4KB

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