The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

268 lines
8.4KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include "jucer_ColouredElement.h"
  20. #include "../jucer_UtilityFunctions.h"
  21. //==============================================================================
  22. class PaintElementRoundedRectangle : public ColouredElement
  23. {
  24. public:
  25. PaintElementRoundedRectangle (PaintRoutine* pr)
  26. : ColouredElement (pr, "Rounded Rectangle", true, false)
  27. {
  28. cornerSize = 10.0;
  29. }
  30. void draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea) override
  31. {
  32. double x, y, w, h;
  33. position.getRectangleDouble (x, y, w, h, parentArea, layout);
  34. fillType.setFillType (g, getDocument(), parentArea);
  35. g.fillRoundedRectangle ((float) x, (float) y, (float) w, (float) h, (float) cornerSize);
  36. if (isStrokePresent)
  37. {
  38. strokeType.fill.setFillType (g, getDocument(), parentArea);
  39. g.drawRoundedRectangle ((float) x, (float) y, (float) w, (float) h, (float) cornerSize,
  40. getStrokeType().stroke.getStrokeThickness());
  41. }
  42. }
  43. void getEditableProperties (Array<PropertyComponent*>& props, bool multipleSelected) override
  44. {
  45. props.add (new CornerSizeProperty (this));
  46. ColouredElement::getEditableProperties (props, multipleSelected);
  47. props.add (new ShapeToPathProperty (this));
  48. }
  49. //==============================================================================
  50. class SetCornerSizeAction : public PaintElementUndoableAction <PaintElementRoundedRectangle>
  51. {
  52. public:
  53. SetCornerSizeAction (PaintElementRoundedRectangle* const element, const double newSize_)
  54. : PaintElementUndoableAction <PaintElementRoundedRectangle> (element),
  55. newSize (newSize_)
  56. {
  57. oldSize = element->getCornerSize();
  58. }
  59. bool perform()
  60. {
  61. showCorrectTab();
  62. getElement()->setCornerSize (newSize, false);
  63. return true;
  64. }
  65. bool undo()
  66. {
  67. showCorrectTab();
  68. getElement()->setCornerSize (oldSize, false);
  69. return true;
  70. }
  71. private:
  72. double newSize, oldSize;
  73. };
  74. void setCornerSize (const double newSize, const bool undoable)
  75. {
  76. if (! approximatelyEqual (newSize, cornerSize))
  77. {
  78. if (undoable)
  79. {
  80. perform (new SetCornerSizeAction (this, newSize),
  81. "Change rounded rectangle corner size");
  82. }
  83. else
  84. {
  85. cornerSize = newSize;
  86. changed();
  87. }
  88. }
  89. }
  90. double getCornerSize() const noexcept { return cornerSize; }
  91. //==============================================================================
  92. void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) override
  93. {
  94. if (fillType.isInvisible() && (strokeType.isInvisible() || ! isStrokePresent))
  95. return;
  96. String x, y, w, h, s;
  97. positionToCode (position, code.document->getComponentLayout(), x, y, w, h);
  98. s << "{\n"
  99. << " float x = " << castToFloat (x) << ", y = " << castToFloat (y) << ", "
  100. << "width = " << castToFloat (w) << ", height = " << castToFloat (h) << ";\n";
  101. if (! fillType.isInvisible())
  102. s << " " << fillType.generateVariablesCode ("fill");
  103. if (isStrokePresent && ! strokeType.isInvisible())
  104. s << " " << strokeType.fill.generateVariablesCode ("stroke");
  105. s << " //[UserPaintCustomArguments] Customize the painting arguments here..\n"
  106. << customPaintCode
  107. << " //[/UserPaintCustomArguments]\n";
  108. if (! fillType.isInvisible())
  109. {
  110. s << " ";
  111. fillType.fillInGeneratedCode ("fill", position, code, s);
  112. s << " g.fillRoundedRectangle (x, y, width, height, " << CodeHelpers::floatLiteral (cornerSize, 3) << ");\n";
  113. }
  114. if (isStrokePresent && ! strokeType.isInvisible())
  115. {
  116. s << " ";
  117. strokeType.fill.fillInGeneratedCode ("stroke", position, code, s);
  118. s << " g.drawRoundedRectangle (x, y, width, height, " << CodeHelpers::floatLiteral (cornerSize, 3)
  119. << ", " << CodeHelpers::floatLiteral (strokeType.stroke.getStrokeThickness(), 3) << ");\n";
  120. }
  121. s << "}\n\n";
  122. paintMethodCode += s;
  123. }
  124. void applyCustomPaintSnippets (StringArray& snippets) override
  125. {
  126. customPaintCode.clear();
  127. if (! snippets.isEmpty() && (! fillType.isInvisible() || (isStrokePresent && ! strokeType.isInvisible())))
  128. {
  129. customPaintCode = snippets[0];
  130. snippets.remove (0);
  131. }
  132. }
  133. static const char* getTagName() noexcept { return "ROUNDRECT"; }
  134. XmlElement* createXml() const override
  135. {
  136. XmlElement* const e = new XmlElement (getTagName());
  137. position.applyToXml (*e);
  138. e->setAttribute ("cornerSize", cornerSize);
  139. addColourAttributes (e);
  140. return e;
  141. }
  142. bool loadFromXml (const XmlElement& xml) override
  143. {
  144. if (xml.hasTagName (getTagName()))
  145. {
  146. position.restoreFromXml (xml, position);
  147. cornerSize = xml.getDoubleAttribute ("cornerSize", 10.0);
  148. loadColourAttributes (xml);
  149. return true;
  150. }
  151. jassertfalse;
  152. return false;
  153. }
  154. void convertToPath()
  155. {
  156. double x, y, w, h;
  157. getCurrentAbsoluteBoundsDouble (x, y, w, h);
  158. Path path;
  159. path.addRoundedRectangle ((float) x, (float) y, (float) w, (float) h, (float) cornerSize);
  160. convertToNewPathElement (path);
  161. }
  162. private:
  163. double cornerSize;
  164. String customPaintCode;
  165. //==============================================================================
  166. class CornerSizeProperty : public SliderPropertyComponent,
  167. private juce::ChangeListener
  168. {
  169. public:
  170. CornerSizeProperty (PaintElementRoundedRectangle* const owner_)
  171. : SliderPropertyComponent ("corner size", 1.0, 200.0, 0.5, 0.4),
  172. owner (owner_)
  173. {
  174. owner->getDocument()->addChangeListener (this);
  175. }
  176. ~CornerSizeProperty() override
  177. {
  178. owner->getDocument()->removeChangeListener (this);
  179. }
  180. void setValue (double newValue) override
  181. {
  182. owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  183. owner->setCornerSize (newValue, true);
  184. }
  185. double getValue() const override { return owner->getCornerSize(); }
  186. private:
  187. void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
  188. PaintElementRoundedRectangle* const owner;
  189. };
  190. //==============================================================================
  191. class ShapeToPathProperty : public ButtonPropertyComponent
  192. {
  193. public:
  194. ShapeToPathProperty (PaintElementRoundedRectangle* const e)
  195. : ButtonPropertyComponent ("path", false),
  196. element (e)
  197. {
  198. }
  199. void buttonClicked()
  200. {
  201. element->convertToPath();
  202. }
  203. String getButtonText() const
  204. {
  205. return "convert to a path";
  206. }
  207. private:
  208. PaintElementRoundedRectangle* const element;
  209. };
  210. };