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.

416 lines
13KB

  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. #include "../../jucer_Headers.h"
  20. #include "jucer_ButtonDocument.h"
  21. #include "../jucer_UtilityFunctions.h"
  22. //==============================================================================
  23. static const int normalOff = 0;
  24. static const int overOff = 1;
  25. static const int downOff = 2;
  26. static const int normalOn = 3;
  27. static const int overOn = 4;
  28. static const int downOn = 5;
  29. static const int background = 6;
  30. //==============================================================================
  31. ButtonDocument::ButtonDocument (SourceCodeDocument* c)
  32. : JucerDocument (c)
  33. {
  34. paintStatesEnabled [normalOff] = true;
  35. paintStatesEnabled [overOff] = true;
  36. paintStatesEnabled [downOff] = true;
  37. paintStatesEnabled [normalOn] = false;
  38. paintStatesEnabled [overOn] = false;
  39. paintStatesEnabled [downOn] = false;
  40. paintStatesEnabled [background] = false;
  41. parentClasses = "public Button";
  42. for (int i = 7; --i >= 0;)
  43. {
  44. paintRoutines[i] = new PaintRoutine();
  45. paintRoutines[i]->setDocument (this);
  46. paintRoutines[i]->setBackgroundColour (Colours::transparentBlack);
  47. }
  48. }
  49. ButtonDocument::~ButtonDocument()
  50. {
  51. }
  52. static const char* const stateNames[] =
  53. {
  54. "normal", "over", "down",
  55. "normal on", "over on", "down on",
  56. "common background"
  57. };
  58. int stateNameToIndex (const String& name)
  59. {
  60. for (int i = 7; --i >= 0;)
  61. if (name.equalsIgnoreCase (stateNames[i]))
  62. return i;
  63. jassertfalse;
  64. return normalOff;
  65. }
  66. int ButtonDocument::getNumPaintRoutines() const
  67. {
  68. int n = 0;
  69. for (int i = 7; --i >= 0;)
  70. if (paintStatesEnabled [i])
  71. ++n;
  72. return n;
  73. }
  74. StringArray ButtonDocument::getPaintRoutineNames() const
  75. {
  76. StringArray s;
  77. for (int i = 0; i < 7; ++i)
  78. if (paintStatesEnabled [i])
  79. s.add (stateNames [i]);
  80. return s;
  81. }
  82. PaintRoutine* ButtonDocument::getPaintRoutine (const int index) const
  83. {
  84. int n = 0;
  85. for (int i = 0; i < 7; ++i)
  86. {
  87. if (paintStatesEnabled [i])
  88. {
  89. if (index == n)
  90. return paintRoutines [i];
  91. else
  92. ++n;
  93. }
  94. }
  95. jassertfalse;
  96. return 0;
  97. }
  98. void ButtonDocument::setStatePaintRoutineEnabled (const int index, bool b)
  99. {
  100. jassert (index > 0 && index < 7);
  101. if (paintStatesEnabled [index] != b)
  102. {
  103. paintStatesEnabled [index] = b;
  104. changed();
  105. }
  106. }
  107. bool ButtonDocument::isStatePaintRoutineEnabled (const int index) const
  108. {
  109. return paintStatesEnabled [index];
  110. }
  111. int ButtonDocument::chooseBestEnabledPaintRoutine (int paintRoutineWanted) const
  112. {
  113. switch (paintRoutineWanted)
  114. {
  115. case normalOff: return normalOff;
  116. case overOff: return paintStatesEnabled [overOff] ? overOff : normalOff;
  117. case downOff: return paintStatesEnabled [downOff] ? downOff : chooseBestEnabledPaintRoutine (overOff);
  118. case normalOn: return paintStatesEnabled [normalOn] ? normalOn : normalOff;
  119. case overOn: return paintStatesEnabled [overOn] ? overOn : (paintStatesEnabled [normalOn] ? normalOn : chooseBestEnabledPaintRoutine (overOff));
  120. case downOn: return paintStatesEnabled [downOn] ? downOn : ((paintStatesEnabled [overOn] || paintStatesEnabled [normalOn])
  121. ? chooseBestEnabledPaintRoutine (overOn)
  122. : chooseBestEnabledPaintRoutine (downOff));
  123. default: jassertfalse; break;
  124. }
  125. return normalOff;
  126. }
  127. //==============================================================================
  128. String ButtonDocument::getTypeName() const
  129. {
  130. return "Button";
  131. }
  132. JucerDocument* ButtonDocument::createCopy()
  133. {
  134. ButtonDocument* newOne = new ButtonDocument (cpp);
  135. newOne->resources = resources;
  136. ScopedPointer<XmlElement> xml (createXml());
  137. newOne->loadFromXml (*xml);
  138. return newOne;
  139. }
  140. XmlElement* ButtonDocument::createXml() const
  141. {
  142. XmlElement* const doc = JucerDocument::createXml();
  143. for (int i = 0; i < 7; ++i)
  144. {
  145. XmlElement* e = paintRoutines [i]->createXml();
  146. e->setAttribute ("buttonState", stateNames [i]);
  147. e->setAttribute ("enabled", paintStatesEnabled [i]);
  148. doc->addChildElement (e);
  149. }
  150. return doc;
  151. }
  152. bool ButtonDocument::loadFromXml (const XmlElement& xml)
  153. {
  154. if (JucerDocument::loadFromXml (xml))
  155. {
  156. for (int i = 7; --i >= 0;)
  157. paintStatesEnabled [i] = false;
  158. forEachXmlChildElementWithTagName (xml, e, PaintRoutine::xmlTagName)
  159. {
  160. const int stateIndex = stateNameToIndex (e->getStringAttribute ("buttonState"));
  161. paintRoutines [stateIndex]->loadFromXml (*e);
  162. paintStatesEnabled [stateIndex] = e->getBoolAttribute ("enabled", stateIndex < normalOn);
  163. }
  164. changed();
  165. getUndoManager().clearUndoHistory();
  166. return true;
  167. }
  168. return false;
  169. }
  170. void ButtonDocument::getOptionalMethods (StringArray& baseClasses,
  171. StringArray& returnValues,
  172. StringArray& methods,
  173. StringArray& initialContents) const
  174. {
  175. JucerDocument::getOptionalMethods (baseClasses, returnValues, methods, initialContents);
  176. addMethod ("Button", "void", "clicked()", "", baseClasses, returnValues, methods, initialContents);
  177. addMethod ("Button", "void", "buttonStateChanged()", "", baseClasses, returnValues, methods, initialContents);
  178. }
  179. //==============================================================================
  180. class ButtonStatePaintEnabledProperty : public BooleanPropertyComponent,
  181. private ChangeListener
  182. {
  183. public:
  184. ButtonStatePaintEnabledProperty (const String& name, ButtonDocument& doc, const int stateMethod_)
  185. : BooleanPropertyComponent (name, "enabled", "disabled"),
  186. document (doc),
  187. stateMethod (stateMethod_)
  188. {
  189. document.addChangeListener (this);
  190. }
  191. ~ButtonStatePaintEnabledProperty()
  192. {
  193. document.removeChangeListener (this);
  194. }
  195. void setState (bool newState)
  196. {
  197. document.setStatePaintRoutineEnabled (stateMethod, newState);
  198. }
  199. bool getState() const
  200. {
  201. return document.isStatePaintRoutineEnabled (stateMethod);
  202. }
  203. private:
  204. void changeListenerCallback (ChangeBroadcaster*)
  205. {
  206. refresh();
  207. }
  208. ButtonDocument& document;
  209. const int stateMethod;
  210. };
  211. void ButtonDocument::addExtraClassProperties (PropertyPanel& panel)
  212. {
  213. Array <PropertyComponent*> props;
  214. for (int i = 1; i < 7; ++i)
  215. props.add (new ButtonStatePaintEnabledProperty (stateNames[i], *this, i));
  216. panel.addSection ("Button paint routines", props);
  217. }
  218. //==============================================================================
  219. class ButtonTestComponent : public Button
  220. {
  221. public:
  222. ButtonTestComponent (ButtonDocument* const doc, const bool fillBackground)
  223. : Button (String()),
  224. document (doc),
  225. alwaysFillBackground (fillBackground)
  226. {
  227. setClickingTogglesState (true);
  228. }
  229. void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
  230. {
  231. if (document->paintStatesEnabled [background])
  232. {
  233. document->paintRoutines [background]->fillWithBackground (g, alwaysFillBackground);
  234. document->paintRoutines [background]->drawElements (g, getLocalBounds());
  235. }
  236. const int stateIndex
  237. = getToggleState()
  238. ? (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOn)
  239. : (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOn)
  240. : document->chooseBestEnabledPaintRoutine (normalOn)))
  241. : (isButtonDown ? document->chooseBestEnabledPaintRoutine (downOff)
  242. : (isMouseOverButton ? document->chooseBestEnabledPaintRoutine (overOff)
  243. : normalOff));
  244. document->paintRoutines [stateIndex]->fillWithBackground (g, ! document->paintStatesEnabled [background]);
  245. document->paintRoutines [stateIndex]->drawElements (g, getLocalBounds());
  246. }
  247. private:
  248. ButtonDocument* const document;
  249. const bool alwaysFillBackground;
  250. };
  251. Component* ButtonDocument::createTestComponent (const bool alwaysFillBackground)
  252. {
  253. return new ButtonTestComponent (this, alwaysFillBackground);
  254. }
  255. //==============================================================================
  256. void ButtonDocument::fillInGeneratedCode (GeneratedCode& code) const
  257. {
  258. JucerDocument::fillInGeneratedCode (code);
  259. code.parentClassInitialiser = "Button (" + quotedString (code.componentName, false) + ")";
  260. code.removeCallback ("void", "paint (Graphics& g)");
  261. }
  262. void ButtonDocument::fillInPaintCode (GeneratedCode& code) const
  263. {
  264. jassert (paintStatesEnabled [normalOff]);
  265. String paintCode [7];
  266. for (int i = 0; i < 7; ++i)
  267. if (paintStatesEnabled [i])
  268. paintRoutines[i]->fillInGeneratedCode (code, paintCode [i]);
  269. String& s = code.getCallbackCode ("public Button",
  270. "void",
  271. "paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown)",
  272. false);
  273. int numPaintRoutines = getNumPaintRoutines();
  274. if (paintStatesEnabled [background])
  275. {
  276. s << paintCode [background] << "\n";
  277. --numPaintRoutines;
  278. }
  279. if (numPaintRoutines == 1)
  280. {
  281. s << paintCode [normalOff];
  282. }
  283. else if (numPaintRoutines == downOff && (paintStatesEnabled [overOff] || paintStatesEnabled [downOff] || paintStatesEnabled [normalOn]))
  284. {
  285. if (paintStatesEnabled [normalOn])
  286. {
  287. s << "if (getToggleState())\n{\n "
  288. << CodeHelpers::indent (paintCode [normalOn], 4, false).trimEnd();
  289. }
  290. else if (paintStatesEnabled [overOff])
  291. {
  292. s << "if (isButtonDown || isMouseOverButton)\n{\n "
  293. << CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd();
  294. }
  295. else
  296. {
  297. s << "if (isButtonDown)\n{\n "
  298. << CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd();
  299. }
  300. s << "\n}\nelse\n{\n "
  301. << CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
  302. << "\n}\n";
  303. }
  304. else if (numPaintRoutines == normalOn && paintStatesEnabled [overOff] && paintStatesEnabled [downOff])
  305. {
  306. s << "if (isButtonDown)\n{\n "
  307. << CodeHelpers::indent (paintCode [downOff], 4, false).trimEnd()
  308. << "\n}\nelse if (isMouseOverButton)\n{\n "
  309. << CodeHelpers::indent (paintCode [overOff], 4, false).trimEnd()
  310. << "\n}\nelse\n{\n "
  311. << CodeHelpers::indent (paintCode [normalOff], 4, false).trimEnd()
  312. << "\n}\n";
  313. }
  314. else
  315. {
  316. if (paintStatesEnabled [normalOn] || paintStatesEnabled [overOn] || paintStatesEnabled [downOn])
  317. {
  318. s << "switch (getToggleState() ? (isButtonDown ? "
  319. << chooseBestEnabledPaintRoutine (downOn) << " : (isMouseOverButton ? "
  320. << chooseBestEnabledPaintRoutine (overOn) << " : "
  321. << chooseBestEnabledPaintRoutine (normalOn) << "))\n : (isButtonDown ? "
  322. << chooseBestEnabledPaintRoutine (downOff) << " : (isMouseOverButton ? "
  323. << chooseBestEnabledPaintRoutine (overOff) << " : 0)))\n{\n";
  324. }
  325. else
  326. {
  327. s << "switch (isButtonDown ? " << chooseBestEnabledPaintRoutine (downOff)
  328. << " : (isMouseOverButton ? " << chooseBestEnabledPaintRoutine (overOff)
  329. << " : 0))\n{\n";
  330. }
  331. for (int i = 0; i < 6; ++i)
  332. {
  333. if (paintStatesEnabled [i])
  334. {
  335. s << "case " << i << ":\n {\n "
  336. << CodeHelpers::indent (paintCode [i], 8, false).trimEnd()
  337. << "\n break;\n }\n\n";
  338. }
  339. }
  340. s << "default:\n break;\n}\n";
  341. }
  342. }