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.

269 lines
8.4KB

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