Collection of DPF-based plugins for packaging
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.

198 lines
5.7KB

  1. /*
  2. * Resize handle for DPF
  3. * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #pragma once
  17. #include "NanoVG.hpp"
  18. START_NAMESPACE_DGL
  19. /** Resize handle for DPF windows, will sit on bottom-right. */
  20. class ResizeHandle : public NanoTopLevelWidget
  21. {
  22. public:
  23. /** Overloaded constructor, will fetch the window from an existing top-level widget. */
  24. explicit ResizeHandle(TopLevelWidget* const tlw)
  25. : NanoTopLevelWidget(tlw->getWindow()),
  26. handleSize(16),
  27. hasCursor(false),
  28. isResizing(false)
  29. {
  30. resetArea();
  31. }
  32. /** Set the handle size, minimum 16.
  33. * Scale factor is automatically applied on top of this size as needed */
  34. void setHandleSize(const uint size)
  35. {
  36. handleSize = std::max(16u, size);
  37. resetArea();
  38. }
  39. protected:
  40. void onNanoDisplay() override
  41. {
  42. const double lineWidth = 1.0 * getScaleFactor();
  43. strokeWidth(lineWidth);
  44. // draw white lines, 1px wide
  45. strokeColor(Color(1.0f, 1.0f, 1.0f));
  46. drawLine(l1);
  47. drawLine(l2);
  48. drawLine(l3);
  49. // draw black lines, offset by 1px and 1px wide
  50. strokeColor(Color(0.0f, 0.0f, 0.0f));
  51. Line<double> l1b(l1), l2b(l2), l3b(l3);
  52. l1b.moveBy(lineWidth, lineWidth);
  53. l2b.moveBy(lineWidth, lineWidth);
  54. l3b.moveBy(lineWidth, lineWidth);
  55. drawLine(l1b);
  56. drawLine(l2b);
  57. drawLine(l3b);
  58. }
  59. void drawLine(const Line<double>& line)
  60. {
  61. beginPath();
  62. moveTo(line.getStartPos().getX(), line.getStartPos().getY());
  63. lineTo(line.getEndPos().getX(), line.getEndPos().getY());
  64. stroke();
  65. }
  66. bool onMouse(const MouseEvent& ev) override
  67. {
  68. if (ev.button != 1)
  69. return false;
  70. if (ev.press && area.contains(ev.pos))
  71. {
  72. isResizing = true;
  73. resizingSize = Size<double>(getWidth(), getHeight());
  74. lastResizePoint = ev.pos;
  75. return true;
  76. }
  77. if (isResizing && ! ev.press)
  78. {
  79. isResizing = false;
  80. recheckCursor(ev.pos);
  81. return true;
  82. }
  83. return false;
  84. }
  85. bool onMotion(const MotionEvent& ev) override
  86. {
  87. if (! isResizing)
  88. {
  89. recheckCursor(ev.pos);
  90. return false;
  91. }
  92. const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(),
  93. ev.pos.getY() - lastResizePoint.getY());
  94. resizingSize += offset;
  95. lastResizePoint = ev.pos;
  96. // TODO keepAspectRatio
  97. bool keepAspectRatio;
  98. const Size<uint> minSize(getWindow().getGeometryConstraints(keepAspectRatio));
  99. const uint minWidth = minSize.getWidth();
  100. const uint minHeight = minSize.getHeight();
  101. if (resizingSize.getWidth() < minWidth)
  102. resizingSize.setWidth(minWidth);
  103. if (resizingSize.getWidth() > 16384)
  104. resizingSize.setWidth(16384);
  105. if (resizingSize.getHeight() < minHeight)
  106. resizingSize.setHeight(minHeight);
  107. if (resizingSize.getHeight() > 16384)
  108. resizingSize.setHeight(16384);
  109. setSize(resizingSize.getWidth(), resizingSize.getHeight());
  110. return true;
  111. }
  112. void onResize(const ResizeEvent& ev) override
  113. {
  114. TopLevelWidget::onResize(ev);
  115. resetArea();
  116. }
  117. private:
  118. Rectangle<uint> area;
  119. Line<double> l1, l2, l3;
  120. uint handleSize;
  121. // event handling state
  122. bool hasCursor, isResizing;
  123. Point<double> lastResizePoint;
  124. Size<double> resizingSize;
  125. void recheckCursor(const Point<double>& pos)
  126. {
  127. const bool shouldHaveCursor = area.contains(pos);
  128. if (shouldHaveCursor == hasCursor)
  129. return;
  130. hasCursor = shouldHaveCursor;
  131. setCursor(shouldHaveCursor ? kMouseCursorDiagonal : kMouseCursorArrow);
  132. }
  133. void resetArea()
  134. {
  135. const double scaleFactor = getScaleFactor();
  136. const uint margin = 1.5 * scaleFactor;
  137. const uint size = handleSize * scaleFactor;
  138. area = Rectangle<uint>(getWidth() - size - margin,
  139. getHeight() - size - margin,
  140. size, size);
  141. recreateLines(area.getX(), area.getY(), size);
  142. }
  143. void recreateLines(const uint x, const uint y, const uint size)
  144. {
  145. uint linesize = size;
  146. uint offset = 0;
  147. // 1st line, full diagonal size
  148. l1.setStartPos(x + size, y);
  149. l1.setEndPos(x, y + size);
  150. // 2nd line, bit more to the right and down, cropped
  151. offset += size / 3;
  152. linesize -= size / 3;
  153. l2.setStartPos(x + linesize + offset, y + offset);
  154. l2.setEndPos(x + offset, y + linesize + offset);
  155. // 3rd line, even more right and down
  156. offset += size / 3;
  157. linesize -= size / 3;
  158. l3.setStartPos(x + linesize + offset, y + offset);
  159. l3.setEndPos(x + offset, y + linesize + offset);
  160. }
  161. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle)
  162. };
  163. END_NAMESPACE_DGL