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.

169 lines
3.4KB

  1. #include "widgets.hpp"
  2. #include "app.hpp"
  3. #include <algorithm>
  4. namespace rack {
  5. Widget::~Widget() {
  6. // You should only delete orphaned widgets
  7. assert(!parent);
  8. // Stop dragging and hovering this widget
  9. if (gHoveredWidget == this) gHoveredWidget = NULL;
  10. if (gDraggedWidget == this) gDraggedWidget = NULL;
  11. if (gDragHoveredWidget == this) gDragHoveredWidget = NULL;
  12. if (gFocusedWidget == this) gFocusedWidget = NULL;
  13. if (gTempWidget == this) gTempWidget = NULL;
  14. clearChildren();
  15. }
  16. math::Rect Widget::getChildrenBoundingBox() {
  17. math::Rect bound;
  18. for (Widget *child : children) {
  19. if (child == children.front()) {
  20. bound = child->box;
  21. }
  22. else {
  23. bound = bound.expand(child->box);
  24. }
  25. }
  26. return bound;
  27. }
  28. math::Vec Widget::getRelativeOffset(math::Vec v, Widget *relative) {
  29. if (this == relative) {
  30. return v;
  31. }
  32. v = v.plus(box.pos);
  33. if (parent) {
  34. v = parent->getRelativeOffset(v, relative);
  35. }
  36. return v;
  37. }
  38. math::Rect Widget::getViewport(math::Rect r) {
  39. math::Rect bound;
  40. if (parent) {
  41. bound = parent->getViewport(box);
  42. }
  43. else {
  44. bound = box;
  45. }
  46. bound.pos = bound.pos.minus(box.pos);
  47. return r.clamp(bound);
  48. }
  49. void Widget::addChild(Widget *widget) {
  50. assert(!widget->parent);
  51. widget->parent = this;
  52. children.push_back(widget);
  53. }
  54. void Widget::removeChild(Widget *widget) {
  55. assert(widget->parent == this);
  56. auto it = std::find(children.begin(), children.end(), widget);
  57. assert(it != children.end());
  58. children.erase(it);
  59. widget->parent = NULL;
  60. }
  61. void Widget::clearChildren() {
  62. for (Widget *child : children) {
  63. child->parent = NULL;
  64. delete child;
  65. }
  66. children.clear();
  67. }
  68. void Widget::finalizeEvents() {
  69. // Stop dragging and hovering this widget
  70. if (gHoveredWidget == this) {
  71. EventMouseLeave e;
  72. gHoveredWidget->onMouseLeave(e);
  73. gHoveredWidget = NULL;
  74. }
  75. if (gDraggedWidget == this) {
  76. EventDragEnd e;
  77. gDraggedWidget->onDragEnd(e);
  78. gDraggedWidget = NULL;
  79. }
  80. if (gDragHoveredWidget == this) {
  81. gDragHoveredWidget = NULL;
  82. }
  83. if (gFocusedWidget == this) {
  84. EventDefocus e;
  85. gFocusedWidget->onDefocus(e);
  86. gFocusedWidget = NULL;
  87. }
  88. for (Widget *child : children) {
  89. child->finalizeEvents();
  90. }
  91. }
  92. void Widget::step() {
  93. for (Widget *child : children) {
  94. child->step();
  95. }
  96. }
  97. void Widget::draw(NVGcontext *vg) {
  98. for (Widget *child : children) {
  99. if (!child->visible)
  100. continue;
  101. nvgSave(vg);
  102. nvgTranslate(vg, child->box.pos.x, child->box.pos.y);
  103. child->draw(vg);
  104. nvgRestore(vg);
  105. }
  106. }
  107. #define RECURSE_EVENT_POSITION(_method) { \
  108. math::Vec pos = e.pos; \
  109. for (auto it = children.rbegin(); it != children.rend(); it++) { \
  110. Widget *child = *it; \
  111. if (!child->visible) \
  112. continue; \
  113. if (child->box.contains(pos)) { \
  114. e.pos = pos.minus(child->box.pos); \
  115. child->_method(e); \
  116. if (e.consumed) \
  117. break; \
  118. } \
  119. } \
  120. e.pos = pos; \
  121. }
  122. void Widget::onMouseDown(EventMouseDown &e) {
  123. RECURSE_EVENT_POSITION(onMouseDown);
  124. }
  125. void Widget::onMouseUp(EventMouseUp &e) {
  126. RECURSE_EVENT_POSITION(onMouseUp);
  127. }
  128. void Widget::onMouseMove(EventMouseMove &e) {
  129. RECURSE_EVENT_POSITION(onMouseMove);
  130. }
  131. void Widget::onHoverKey(EventHoverKey &e) {
  132. RECURSE_EVENT_POSITION(onHoverKey);
  133. }
  134. void Widget::onScroll(EventScroll &e) {
  135. RECURSE_EVENT_POSITION(onScroll);
  136. }
  137. void Widget::onPathDrop(EventPathDrop &e) {
  138. RECURSE_EVENT_POSITION(onPathDrop);
  139. }
  140. void Widget::onZoom(EventZoom &e) {
  141. for (auto it = children.rbegin(); it != children.rend(); it++) {
  142. Widget *child = *it;
  143. child->onZoom(e);
  144. }
  145. }
  146. } // namespace rack