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.

357 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 7 technical preview.
  4. Copyright (c) 2022 - 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 the technical preview this file cannot be licensed commercially.
  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 FileGroupInformationComponent : public Component,
  16. private ListBoxModel,
  17. private ValueTree::Listener
  18. {
  19. public:
  20. FileGroupInformationComponent (const Project::Item& group)
  21. : item (group),
  22. header (item.getName(), { getIcons().openFolder, Colours::transparentBlack })
  23. {
  24. list.setHeaderComponent (std::make_unique<ListBoxHeader> (Array<String> { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" },
  25. Array<float> { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f }));
  26. list.setModel (this);
  27. list.setColour (ListBox::backgroundColourId, Colours::transparentBlack);
  28. addAndMakeVisible (list);
  29. list.updateContent();
  30. list.setRowHeight (30);
  31. item.state.addListener (this);
  32. lookAndFeelChanged();
  33. addAndMakeVisible (header);
  34. }
  35. ~FileGroupInformationComponent() override
  36. {
  37. item.state.removeListener (this);
  38. }
  39. //==============================================================================
  40. void paint (Graphics& g) override
  41. {
  42. g.setColour (findColour (secondaryBackgroundColourId));
  43. g.fillRect (getLocalBounds().reduced (12, 0));
  44. }
  45. void resized() override
  46. {
  47. auto bounds = getLocalBounds().reduced (12, 0);
  48. header.setBounds (bounds.removeFromTop (40));
  49. list.setBounds (bounds.reduced (10, 4));
  50. }
  51. void parentSizeChanged() override
  52. {
  53. setSize (jmax (550, getParentWidth()), getParentHeight());
  54. }
  55. int getNumRows() override
  56. {
  57. return item.getNumChildren();
  58. }
  59. void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool /*rowIsSelected*/) override
  60. {
  61. g.setColour (findColour (rowNumber % 2 == 0 ? widgetBackgroundColourId
  62. : secondaryWidgetBackgroundColourId));
  63. g.fillRect (0, 0, width, height - 1);
  64. }
  65. Component* refreshComponentForRow (int rowNumber, bool /*isRowSelected*/, Component* existingComponentToUpdate) override
  66. {
  67. std::unique_ptr<Component> existing (existingComponentToUpdate);
  68. if (rowNumber < getNumRows())
  69. {
  70. auto child = item.getChild (rowNumber);
  71. if (existingComponentToUpdate == nullptr
  72. || dynamic_cast<FileOptionComponent*> (existing.get())->item != child)
  73. {
  74. existing.reset();
  75. existing.reset (new FileOptionComponent (child, dynamic_cast<ListBoxHeader*> (list.getHeaderComponent())));
  76. }
  77. }
  78. return existing.release();
  79. }
  80. String getGroupPath() const { return item.getFile().getFullPathName(); }
  81. //==============================================================================
  82. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { itemChanged(); }
  83. void valueTreeChildAdded (ValueTree&, ValueTree&) override { itemChanged(); }
  84. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { itemChanged(); }
  85. void valueTreeChildOrderChanged (ValueTree&, int, int) override { itemChanged(); }
  86. void valueTreeParentChanged (ValueTree&) override { itemChanged(); }
  87. private:
  88. Project::Item item;
  89. ListBox list;
  90. ContentViewHeader header;
  91. void itemChanged()
  92. {
  93. list.updateContent();
  94. repaint();
  95. }
  96. //==============================================================================
  97. class FileOptionComponent : public Component
  98. {
  99. public:
  100. FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader)
  101. : item (fileItem),
  102. header (listBoxHeader),
  103. compilerFlagSchemeSelector (item)
  104. {
  105. if (item.isFile())
  106. {
  107. auto isSourceFile = item.isSourceFile();
  108. if (isSourceFile)
  109. {
  110. addAndMakeVisible (compileButton);
  111. compileButton.getToggleStateValue().referTo (item.getShouldCompileValue());
  112. compileButton.onStateChange = [this] { compileEnablementChanged(); };
  113. }
  114. addAndMakeVisible (binaryResourceButton);
  115. binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue());
  116. addAndMakeVisible (xcodeResourceButton);
  117. xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue());
  118. if (isSourceFile)
  119. {
  120. addChildComponent (skipPCHButton);
  121. skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue());
  122. addChildComponent (compilerFlagSchemeSelector);
  123. compileEnablementChanged();
  124. }
  125. }
  126. }
  127. void paint (Graphics& g) override
  128. {
  129. if (header != nullptr)
  130. {
  131. auto textBounds = getLocalBounds().removeFromLeft (roundToInt (header->getProportionAtIndex (0) * (float) getWidth()));
  132. auto iconBounds = textBounds.removeFromLeft (25);
  133. if (item.isImageFile())
  134. iconBounds.reduce (5, 5);
  135. item.getIcon().withColour (findColour (treeIconColourId)).draw (g, iconBounds.toFloat(), item.isIconCrossedOut());
  136. g.setColour (findColour (widgetTextColourId));
  137. g.drawText (item.getName(), textBounds, Justification::centredLeft);
  138. }
  139. }
  140. void resized() override
  141. {
  142. if (header != nullptr)
  143. {
  144. auto bounds = getLocalBounds();
  145. auto width = (float) getWidth();
  146. bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width));
  147. binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width)));
  148. xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width)));
  149. compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width)));
  150. skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width)));
  151. compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width)));
  152. }
  153. }
  154. Project::Item item;
  155. private:
  156. //==============================================================================
  157. class CompilerFlagSchemeSelector : public Component,
  158. private Value::Listener
  159. {
  160. public:
  161. CompilerFlagSchemeSelector (Project::Item& it)
  162. : item (it)
  163. {
  164. schemeBox.setTextWhenNothingSelected ("None");
  165. updateCompilerFlagSchemeComboBox();
  166. schemeBox.onChange = [this] { handleComboBoxSelection(); };
  167. addAndMakeVisible (schemeBox);
  168. addChildComponent (newSchemeLabel);
  169. newSchemeLabel.setEditable (true);
  170. newSchemeLabel.setJustificationType (Justification::centredLeft);
  171. newSchemeLabel.onEditorHide = [this]
  172. {
  173. newSchemeLabel.setVisible (false);
  174. schemeBox.setVisible (true);
  175. auto newScheme = newSchemeLabel.getText();
  176. item.project.addCompilerFlagScheme (newScheme);
  177. if (item.getCompilerFlagSchemeString().isEmpty())
  178. item.setCompilerFlagScheme (newScheme);
  179. updateCompilerFlagSchemeComboBox();
  180. };
  181. selectScheme (item.getCompilerFlagSchemeString());
  182. projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes);
  183. projectCompilerFlagSchemesValue.addListener (this);
  184. lookAndFeelChanged();
  185. }
  186. void resized() override
  187. {
  188. auto b = getLocalBounds();
  189. schemeBox.setBounds (b);
  190. newSchemeLabel.setBounds (b);
  191. }
  192. private:
  193. void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); }
  194. void lookAndFeelChanged() override
  195. {
  196. schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack);
  197. schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId));
  198. }
  199. void updateCompilerFlagSchemeComboBox()
  200. {
  201. auto itemScheme = item.getCompilerFlagSchemeString();
  202. auto allSchemes = item.project.getCompilerFlagSchemes();
  203. if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme))
  204. {
  205. item.clearCurrentCompilerFlagScheme();
  206. itemScheme = {};
  207. }
  208. schemeBox.clear();
  209. schemeBox.addItemList (allSchemes, 1);
  210. schemeBox.addSeparator();
  211. schemeBox.addItem ("Add a new scheme...", -1);
  212. schemeBox.addItem ("Delete selected scheme", -2);
  213. schemeBox.addItem ("Clear", -3);
  214. selectScheme (itemScheme);
  215. }
  216. void handleComboBoxSelection()
  217. {
  218. auto selectedID = schemeBox.getSelectedId();
  219. if (selectedID > 0)
  220. {
  221. item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1));
  222. }
  223. else if (selectedID == -1)
  224. {
  225. newSchemeLabel.setText ("NewScheme", dontSendNotification);
  226. schemeBox.setVisible (false);
  227. newSchemeLabel.setVisible (true);
  228. newSchemeLabel.showEditor();
  229. if (auto* ed = newSchemeLabel.getCurrentTextEditor())
  230. ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_");
  231. }
  232. else if (selectedID == -2)
  233. {
  234. auto currentScheme = item.getCompilerFlagSchemeString();
  235. if (currentScheme.isNotEmpty())
  236. {
  237. item.project.removeCompilerFlagScheme (currentScheme);
  238. item.clearCurrentCompilerFlagScheme();
  239. }
  240. updateCompilerFlagSchemeComboBox();
  241. }
  242. else if (selectedID == -3)
  243. {
  244. schemeBox.setSelectedId (0);
  245. item.clearCurrentCompilerFlagScheme();
  246. }
  247. }
  248. void selectScheme (const String& schemeToSelect)
  249. {
  250. if (schemeToSelect.isNotEmpty())
  251. {
  252. for (int i = 0; i < schemeBox.getNumItems(); ++i)
  253. {
  254. if (schemeBox.getItemText (i) == schemeToSelect)
  255. {
  256. schemeBox.setSelectedItemIndex (i);
  257. return;
  258. }
  259. }
  260. }
  261. schemeBox.setSelectedId (0);
  262. }
  263. Project::Item& item;
  264. Value projectCompilerFlagSchemesValue;
  265. ComboBox schemeBox;
  266. Label newSchemeLabel;
  267. };
  268. void compileEnablementChanged()
  269. {
  270. auto shouldBeCompiled = compileButton.getToggleState();
  271. skipPCHButton.setVisible (shouldBeCompiled);
  272. compilerFlagSchemeSelector.setVisible (shouldBeCompiled);
  273. }
  274. //==============================================================================
  275. ListBoxHeader* header;
  276. ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton;
  277. CompilerFlagSchemeSelector compilerFlagSchemeSelector;
  278. };
  279. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent)
  280. };