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.

422 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  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. #include "../../Application/jucer_Headers.h"
  14. #include "jucer_PaintElementImage.h"
  15. PaintElementImage::PaintElementImage (PaintRoutine* pr)
  16. : PaintElement (pr, "Image"),
  17. opacity (1.0),
  18. mode (stretched)
  19. {
  20. }
  21. PaintElementImage::~PaintElementImage() {}
  22. const Drawable* PaintElementImage::getDrawable()
  23. {
  24. if (JucerDocument* const document = getDocument())
  25. return document->getResources().getDrawable (resourceName);
  26. return nullptr;
  27. }
  28. void PaintElementImage::draw (Graphics& g, const ComponentLayout* layout, const Rectangle<int>& parentArea)
  29. {
  30. const Rectangle<int> r (position.getRectangle (parentArea, layout));
  31. if (const Drawable* const image = getDrawable())
  32. {
  33. image->drawWithin (g, r.toFloat(),
  34. mode == stretched ? RectanglePlacement::stretchToFit
  35. : (mode == proportionalReducingOnly ? (RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize)
  36. : RectanglePlacement::centred),
  37. (float) opacity);
  38. }
  39. else
  40. {
  41. g.setColour (Colours::grey.withAlpha (0.5f));
  42. g.fillRect (r);
  43. g.setColour (Colours::black);
  44. g.drawText ("(image missing)",
  45. r.getX(), r.getY(), r.getWidth(), r.getHeight(),
  46. Justification::centred, true);
  47. }
  48. }
  49. //==============================================================================
  50. void PaintElementImage::getEditableProperties (Array <PropertyComponent*>& props, bool multipleSelected)
  51. {
  52. PaintElement::getEditableProperties (props, multipleSelected);
  53. props.add (new ImageElementResourceProperty (this));
  54. props.add (new StretchModeProperty (this));
  55. props.add (new OpacityProperty (this));
  56. props.add (new ResetSizeProperty (this));
  57. }
  58. void PaintElementImage::fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode)
  59. {
  60. if (opacity > 0)
  61. {
  62. String x, y, w, h, r;
  63. positionToCode (position, getDocument()->getComponentLayout(), x, y, w, h);
  64. r << "{\n"
  65. << " int x = " << x << ", y = " << y << ", width = " << w << ", height = " << h << ";\n"
  66. << " //[UserPaintCustomArguments] Customize the painting arguments here..\n"
  67. << customPaintCode
  68. << " //[/UserPaintCustomArguments]\n";
  69. if (dynamic_cast<const DrawableImage*> (getDrawable()))
  70. {
  71. const String imageVariable ("cachedImage_" + resourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix()));
  72. code.addImageResourceLoader (imageVariable, resourceName);
  73. if (opacity >= 254.0 / 255.0)
  74. r << " g.setColour (juce::Colours::black);\n";
  75. else
  76. r << " g.setColour (juce::Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  77. if (mode == stretched)
  78. {
  79. r << " g.drawImage (" << imageVariable << ",\n"
  80. << " x, y, width, height,\n"
  81. << " 0, 0, " << imageVariable << ".getWidth(), " << imageVariable << ".getHeight());\n";
  82. }
  83. else
  84. {
  85. r << " g.drawImageWithin (" << imageVariable << ",\n"
  86. << " x, y, width, height,\n"
  87. << " ";
  88. if (mode == proportionalReducingOnly)
  89. r << "juce::RectanglePlacement::centred | juce::RectanglePlacement::onlyReduceInSize";
  90. else
  91. r << "juce::RectanglePlacement::centred";
  92. r << ",\n"
  93. << " false);\n";
  94. }
  95. }
  96. else
  97. {
  98. if (resourceName.isNotEmpty())
  99. {
  100. const String imageVariable ("drawable" + String (code.getUniqueSuffix()));
  101. code.privateMemberDeclarations
  102. << "std::unique_ptr<juce::Drawable> " << imageVariable << ";\n";
  103. code.constructorCode
  104. << imageVariable << " = juce::Drawable::createFromImageData ("
  105. << resourceName << ", " << resourceName << "Size);\n";
  106. code.destructorCode
  107. << imageVariable << " = nullptr;\n";
  108. if (opacity >= 254.0 / 255.0)
  109. r << " g.setColour (juce::Colours::black);\n";
  110. else
  111. r << " g.setColour (juce::Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n";
  112. r << " jassert (" << imageVariable << " != nullptr);\n"
  113. << " if (" << imageVariable << " != nullptr)\n"
  114. << " " << imageVariable << "->drawWithin (g, juce::Rectangle<int> (x, y, width, height).toFloat(),\n"
  115. << " " << String::repeatedString (" ", imageVariable.length() + 18)
  116. << (mode == stretched ? "juce::RectanglePlacement::stretchToFit"
  117. : (mode == proportionalReducingOnly ? "juce::RectanglePlacement::centred | juce::RectanglePlacement::onlyReduceInSize"
  118. : "juce::RectanglePlacement::centred"))
  119. << ", " << CodeHelpers::floatLiteral (opacity, 3) << ");\n";
  120. }
  121. }
  122. r << "}\n\n";
  123. paintMethodCode += r;
  124. }
  125. }
  126. void PaintElementImage::applyCustomPaintSnippets (StringArray& snippets)
  127. {
  128. customPaintCode.clear();
  129. if (! snippets.isEmpty() && opacity > 0)
  130. {
  131. customPaintCode = snippets[0];
  132. snippets.remove (0);
  133. }
  134. }
  135. //==============================================================================
  136. PaintElementImage::SetResourceAction::SetResourceAction (PaintElementImage* const element, const String& newResource_)
  137. : PaintElementUndoableAction <PaintElementImage> (element),
  138. newResource (newResource_)
  139. {
  140. oldResource = element->getResource();
  141. }
  142. bool PaintElementImage::SetResourceAction::perform()
  143. {
  144. showCorrectTab();
  145. getElement()->setResource (newResource, false);
  146. return true;
  147. }
  148. bool PaintElementImage::SetResourceAction::undo()
  149. {
  150. showCorrectTab();
  151. getElement()->setResource (oldResource, false);
  152. return true;
  153. }
  154. void PaintElementImage::setResource (const String& newName, const bool undoable)
  155. {
  156. if (resourceName != newName)
  157. {
  158. if (undoable)
  159. {
  160. perform (new SetResourceAction (this, newName),
  161. "Change image resource");
  162. }
  163. else
  164. {
  165. resourceName = newName;
  166. changed();
  167. }
  168. }
  169. repaint();
  170. }
  171. String PaintElementImage::getResource() const
  172. {
  173. return resourceName;
  174. }
  175. //==============================================================================
  176. PaintElementImage::SetOpacityAction::SetOpacityAction (PaintElementImage* const element, const double newOpacity_)
  177. : PaintElementUndoableAction <PaintElementImage> (element),
  178. newOpacity (newOpacity_)
  179. {
  180. oldOpacity = element->getOpacity();
  181. }
  182. bool PaintElementImage::SetOpacityAction::perform()
  183. {
  184. showCorrectTab();
  185. getElement()->setOpacity (newOpacity, false);
  186. return true;
  187. }
  188. bool PaintElementImage::SetOpacityAction::undo()
  189. {
  190. showCorrectTab();
  191. getElement()->setOpacity (oldOpacity, false);
  192. return true;
  193. }
  194. void PaintElementImage::setOpacity (double newOpacity, const bool undoable)
  195. {
  196. newOpacity = jlimit (0.0, 1.0, newOpacity);
  197. if (opacity != newOpacity)
  198. {
  199. if (undoable)
  200. {
  201. perform (new SetOpacityAction (this, newOpacity),
  202. "Change image opacity");
  203. }
  204. else
  205. {
  206. opacity = newOpacity;
  207. changed();
  208. }
  209. }
  210. }
  211. double PaintElementImage::getOpacity() const noexcept { return opacity; }
  212. //==============================================================================
  213. const char* PaintElementImage::getTagName() noexcept { return "IMAGE"; }
  214. void PaintElementImage::resetToImageSize()
  215. {
  216. if (const Drawable* const image = getDrawable())
  217. {
  218. if (PaintRoutineEditor* ed = dynamic_cast<PaintRoutineEditor*> (getParentComponent()))
  219. {
  220. const Rectangle<int> parentArea (ed->getComponentArea());
  221. Rectangle<int> r (getCurrentBounds (parentArea));
  222. Rectangle<float> b (image->getDrawableBounds());
  223. r.setSize ((int) (b.getWidth() + 0.999f),
  224. (int) (b.getHeight() + 0.999f));
  225. setCurrentBounds (r, parentArea, true);
  226. }
  227. }
  228. }
  229. //==============================================================================
  230. PaintElementImage::SetStretchModeAction::SetStretchModeAction (PaintElementImage* const element, const StretchMode newValue_)
  231. : PaintElementUndoableAction <PaintElementImage> (element),
  232. newValue (newValue_)
  233. {
  234. oldValue = element->getStretchMode();
  235. }
  236. bool PaintElementImage::SetStretchModeAction::perform()
  237. {
  238. showCorrectTab();
  239. getElement()->setStretchMode (newValue, false);
  240. return true;
  241. }
  242. bool PaintElementImage::SetStretchModeAction::undo()
  243. {
  244. showCorrectTab();
  245. getElement()->setStretchMode (oldValue, false);
  246. return true;
  247. }
  248. PaintElementImage::StretchMode PaintElementImage::getStretchMode() const noexcept { return mode; }
  249. void PaintElementImage::setStretchMode (const StretchMode newMode, const bool undoable)
  250. {
  251. if (mode != newMode)
  252. {
  253. if (undoable)
  254. {
  255. perform (new SetStretchModeAction (this, newMode),
  256. "Change image mode");
  257. }
  258. else
  259. {
  260. mode = newMode;
  261. changed();
  262. }
  263. }
  264. }
  265. //==============================================================================
  266. XmlElement* PaintElementImage::createXml() const
  267. {
  268. XmlElement* e = new XmlElement (getTagName());
  269. position.applyToXml (*e);
  270. e->setAttribute ("resource", resourceName);
  271. e->setAttribute ("opacity", opacity);
  272. e->setAttribute ("mode", (int) mode);
  273. return e;
  274. }
  275. bool PaintElementImage::loadFromXml (const XmlElement& xml)
  276. {
  277. if (xml.hasTagName (getTagName()))
  278. {
  279. position.restoreFromXml (xml, position);
  280. resourceName = xml.getStringAttribute ("resource", String());
  281. opacity = xml.getDoubleAttribute ("opacity", 1.0);
  282. mode = (StretchMode) xml.getIntAttribute ("mode", (int) stretched);
  283. repaint();
  284. return true;
  285. }
  286. jassertfalse;
  287. return false;
  288. }
  289. //==============================================================================
  290. PaintElementImage::ImageElementResourceProperty::ImageElementResourceProperty (PaintElementImage* const e)
  291. : ImageResourceProperty <PaintElementImage> (e, "image source")
  292. {
  293. }
  294. void PaintElementImage::ImageElementResourceProperty::setResource (const String& newName)
  295. {
  296. if (element != nullptr)
  297. element->setResource (newName, true);
  298. }
  299. String PaintElementImage::ImageElementResourceProperty::getResource() const
  300. {
  301. if (element != nullptr)
  302. return element->getResource();
  303. return {};
  304. }
  305. //==============================================================================
  306. PaintElementImage::OpacityProperty::OpacityProperty (PaintElementImage* const e)
  307. : SliderPropertyComponent ("opacity", 0.0, 1.0, 0.001),
  308. listener (e)
  309. {
  310. listener.setPropertyToRefresh (*this);
  311. }
  312. void PaintElementImage::OpacityProperty::setValue (double newValue)
  313. {
  314. listener.owner->getDocument()->getUndoManager().undoCurrentTransactionOnly();
  315. listener.owner->setOpacity (newValue, true);
  316. }
  317. double PaintElementImage::OpacityProperty::getValue() const
  318. {
  319. return listener.owner->getOpacity();
  320. }
  321. PaintElementImage::StretchModeProperty::StretchModeProperty (PaintElementImage* const e)
  322. : ChoicePropertyComponent ("stretch mode"),
  323. listener (e)
  324. {
  325. listener.setPropertyToRefresh (*this);
  326. choices.add ("Stretched to fit");
  327. choices.add ("Maintain aspect ratio");
  328. choices.add ("Maintain aspect ratio, only reduce in size");
  329. }
  330. void PaintElementImage::StretchModeProperty::setIndex (int newIndex)
  331. {
  332. listener.owner->setStretchMode ((StretchMode) newIndex, true);
  333. }
  334. int PaintElementImage::StretchModeProperty::getIndex() const
  335. {
  336. return (int) listener.owner->getStretchMode();
  337. }
  338. PaintElementImage::ResetSizeProperty::ResetSizeProperty (PaintElementImage* const e)
  339. : ButtonPropertyComponent ("reset", false),
  340. element (e)
  341. {
  342. }
  343. void PaintElementImage::ResetSizeProperty::buttonClicked()
  344. {
  345. element->resetToImageSize();
  346. }
  347. String PaintElementImage::ResetSizeProperty::getButtonText() const { return "reset to image size"; }