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.

261 lines
8.1KB

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