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.

383 lines
13KB

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