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.

390 lines
13KB

  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. //==============================================================================
  20. class ButtonHandler : public ComponentTypeHandler
  21. {
  22. public:
  23. ButtonHandler (const String& typeDescription_,
  24. const String& className_,
  25. const std::type_info& componentClass,
  26. const int defaultWidth_,
  27. const int defaultHeight_)
  28. : ComponentTypeHandler (typeDescription_, className_, componentClass,
  29. defaultWidth_, defaultHeight_)
  30. {}
  31. void getEditableProperties (Component* component, JucerDocument& document,
  32. Array<PropertyComponent*>& props, bool multipleSelected) override
  33. {
  34. ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
  35. if (multipleSelected)
  36. return;
  37. if (auto* b = dynamic_cast<Button*> (component))
  38. {
  39. props.add (new ButtonTextProperty (b, document));
  40. props.add (new ButtonCallbackProperty (b, document));
  41. props.add (new ButtonRadioGroupProperty (b, document));
  42. props.add (new ButtonConnectedEdgeProperty ("connected left", Button::ConnectedOnLeft, b, document));
  43. props.add (new ButtonConnectedEdgeProperty ("connected right", Button::ConnectedOnRight, b, document));
  44. props.add (new ButtonConnectedEdgeProperty ("connected top", Button::ConnectedOnTop, b, document));
  45. props.add (new ButtonConnectedEdgeProperty ("connected bottom", Button::ConnectedOnBottom, b, document));
  46. }
  47. }
  48. XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
  49. {
  50. Button* const b = dynamic_cast<Button*> (comp);
  51. XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
  52. e->setAttribute ("buttonText", b->getButtonText());
  53. e->setAttribute ("connectedEdges", b->getConnectedEdgeFlags());
  54. e->setAttribute ("needsCallback", needsButtonListener (b));
  55. e->setAttribute ("radioGroupId", b->getRadioGroupId());
  56. return e;
  57. }
  58. bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
  59. {
  60. Button* const b = dynamic_cast<Button*> (comp);
  61. if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
  62. return false;
  63. b->setButtonText (xml.getStringAttribute ("buttonText", b->getButtonText()));
  64. b->setConnectedEdges (xml.getIntAttribute ("connectedEdges", 0));
  65. setNeedsButtonListener (b, xml.getBoolAttribute ("needsCallback", true));
  66. b->setRadioGroupId (xml.getIntAttribute ("radioGroupId", 0));
  67. return true;
  68. }
  69. String getCreationParameters (GeneratedCode&, Component* component) override
  70. {
  71. return quotedString (component->getName(), false);
  72. }
  73. void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
  74. {
  75. ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
  76. Button* const b = dynamic_cast<Button*> (component);
  77. if (b->getButtonText() != b->getName())
  78. {
  79. code.constructorCode
  80. << memberVariableName << "->setButtonText ("
  81. << quotedString (b->getButtonText(), code.shouldUseTransMacro()) << ");\n";
  82. }
  83. if (b->getConnectedEdgeFlags() != 0)
  84. {
  85. StringArray flags;
  86. if (b->isConnectedOnLeft())
  87. flags.add ("juce::Button::ConnectedOnLeft");
  88. if (b->isConnectedOnRight())
  89. flags.add ("juce::Button::ConnectedOnRight");
  90. if (b->isConnectedOnTop())
  91. flags.add ("juce::Button::ConnectedOnTop");
  92. if (b->isConnectedOnBottom())
  93. flags.add ("juce::Button::ConnectedOnBottom");
  94. String s;
  95. s << memberVariableName << "->setConnectedEdges ("
  96. << flags.joinIntoString (" | ") << ");\n";
  97. code.constructorCode += s;
  98. }
  99. if (b->getRadioGroupId() != 0)
  100. code.constructorCode << memberVariableName << "->setRadioGroupId ("
  101. << b->getRadioGroupId() << ");\n";
  102. if (needsButtonListener (component))
  103. code.constructorCode << memberVariableName << "->addListener (this);\n";
  104. }
  105. void fillInGeneratedCode (Component* component, GeneratedCode& code) override
  106. {
  107. ComponentTypeHandler::fillInGeneratedCode (component, code);
  108. if (needsButtonListener (component))
  109. {
  110. String& callback = code.getCallbackCode ("public juce::Button::Listener",
  111. "void",
  112. "buttonClicked (juce::Button* buttonThatWasClicked)",
  113. true);
  114. if (callback.isNotEmpty())
  115. callback << "else ";
  116. const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
  117. const String userCodeComment ("UserButtonCode_" + memberVariableName);
  118. callback
  119. << "if (buttonThatWasClicked == " << memberVariableName << ".get())\n"
  120. << "{\n //[" << userCodeComment << "] -- add your button handler code here..\n //[/" << userCodeComment << "]\n}\n";
  121. }
  122. }
  123. static bool needsButtonListener (Component* button)
  124. {
  125. return button->getProperties().getWithDefault ("generateListenerCallback", true);
  126. }
  127. static void setNeedsButtonListener (Component* button, const bool shouldDoCallback)
  128. {
  129. button->getProperties().set ("generateListenerCallback", shouldDoCallback);
  130. }
  131. private:
  132. //==============================================================================
  133. class ButtonTextProperty : public ComponentTextProperty <Button>
  134. {
  135. public:
  136. ButtonTextProperty (Button* button_, JucerDocument& doc)
  137. : ComponentTextProperty <Button> ("text", 100, false, button_, doc)
  138. {
  139. }
  140. void setText (const String& newText) override
  141. {
  142. document.perform (new ButtonTextChangeAction (component, *document.getComponentLayout(), newText),
  143. "Change button text");
  144. }
  145. String getText() const override
  146. {
  147. return component->getButtonText();
  148. }
  149. private:
  150. class ButtonTextChangeAction : public ComponentUndoableAction <Button>
  151. {
  152. public:
  153. ButtonTextChangeAction (Button* const comp, ComponentLayout& l, const String& newName_)
  154. : ComponentUndoableAction <Button> (comp, l),
  155. newName (newName_)
  156. {
  157. oldName = comp->getButtonText();
  158. }
  159. bool perform()
  160. {
  161. showCorrectTab();
  162. getComponent()->setButtonText (newName);
  163. changed();
  164. return true;
  165. }
  166. bool undo()
  167. {
  168. showCorrectTab();
  169. getComponent()->setButtonText (oldName);
  170. changed();
  171. return true;
  172. }
  173. String newName, oldName;
  174. };
  175. };
  176. class ButtonCallbackProperty : public ComponentBooleanProperty <Button>
  177. {
  178. public:
  179. ButtonCallbackProperty (Button* b, JucerDocument& doc)
  180. : ComponentBooleanProperty <Button> ("callback", "Generate ButtonListener", "Generate ButtonListener", b, doc)
  181. {
  182. }
  183. void setState (bool newState)
  184. {
  185. document.perform (new ButtonCallbackChangeAction (component, *document.getComponentLayout(), newState),
  186. "Change button callback");
  187. }
  188. bool getState() const { return needsButtonListener (component); }
  189. private:
  190. class ButtonCallbackChangeAction : public ComponentUndoableAction <Button>
  191. {
  192. public:
  193. ButtonCallbackChangeAction (Button* const comp, ComponentLayout& l, const bool newState_)
  194. : ComponentUndoableAction <Button> (comp, l),
  195. newState (newState_)
  196. {
  197. oldState = needsButtonListener (comp);
  198. }
  199. bool perform()
  200. {
  201. showCorrectTab();
  202. setNeedsButtonListener (getComponent(), newState);
  203. changed();
  204. return true;
  205. }
  206. bool undo()
  207. {
  208. showCorrectTab();
  209. setNeedsButtonListener (getComponent(), oldState);
  210. changed();
  211. return true;
  212. }
  213. bool newState, oldState;
  214. };
  215. };
  216. class ButtonRadioGroupProperty : public ComponentTextProperty <Button>
  217. {
  218. public:
  219. ButtonRadioGroupProperty (Button* const button_, JucerDocument& doc)
  220. : ComponentTextProperty <Button> ("radio group", 10, false, button_, doc)
  221. {
  222. }
  223. void setText (const String& newText) override
  224. {
  225. document.perform (new ButtonRadioGroupChangeAction (component, *document.getComponentLayout(), newText.getIntValue()),
  226. "Change radio group ID");
  227. }
  228. String getText() const override
  229. {
  230. return String (component->getRadioGroupId());
  231. }
  232. private:
  233. class ButtonRadioGroupChangeAction : public ComponentUndoableAction <Button>
  234. {
  235. public:
  236. ButtonRadioGroupChangeAction (Button* const comp, ComponentLayout& l, const int newId_)
  237. : ComponentUndoableAction <Button> (comp, l),
  238. newId (newId_)
  239. {
  240. oldId = comp->getRadioGroupId();
  241. }
  242. bool perform()
  243. {
  244. showCorrectTab();
  245. getComponent()->setRadioGroupId (newId);
  246. changed();
  247. return true;
  248. }
  249. bool undo()
  250. {
  251. showCorrectTab();
  252. getComponent()->setRadioGroupId (oldId);
  253. changed();
  254. return true;
  255. }
  256. int newId, oldId;
  257. };
  258. };
  259. class ButtonConnectedEdgeProperty : public ComponentBooleanProperty <Button>
  260. {
  261. public:
  262. ButtonConnectedEdgeProperty (const String& name, const int flag_,
  263. Button* b, JucerDocument& doc)
  264. : ComponentBooleanProperty <Button> (name, "Connected", "Connected", b, doc),
  265. flag (flag_)
  266. {
  267. }
  268. void setState (bool newState)
  269. {
  270. document.perform (new ButtonConnectedChangeAction (component, *document.getComponentLayout(), flag, newState),
  271. "Change button connected edges");
  272. }
  273. bool getState() const
  274. {
  275. return (component->getConnectedEdgeFlags() & flag) != 0;
  276. }
  277. private:
  278. const int flag;
  279. class ButtonConnectedChangeAction : public ComponentUndoableAction <Button>
  280. {
  281. public:
  282. ButtonConnectedChangeAction (Button* const comp, ComponentLayout& l, const int flag_, const bool newState_)
  283. : ComponentUndoableAction <Button> (comp, l),
  284. flag (flag_),
  285. newState (newState_)
  286. {
  287. oldState = ((comp->getConnectedEdgeFlags() & flag) != 0);
  288. }
  289. bool perform()
  290. {
  291. showCorrectTab();
  292. if (newState)
  293. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
  294. else
  295. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
  296. changed();
  297. return true;
  298. }
  299. bool undo()
  300. {
  301. showCorrectTab();
  302. if (oldState)
  303. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
  304. else
  305. getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
  306. changed();
  307. return true;
  308. }
  309. const int flag;
  310. bool newState, oldState;
  311. };
  312. };
  313. };