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.

277 lines
8.0KB

  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. #pragma once
  20. //==============================================================================
  21. class ConcertinaHeader : public Component,
  22. public ChangeBroadcaster
  23. {
  24. public:
  25. ConcertinaHeader (String n, Path p)
  26. : Component (n), name (n), iconPath (p)
  27. {
  28. panelIcon = Icon (iconPath, Colours::white);
  29. nameLabel.setText (name, dontSendNotification);
  30. nameLabel.setJustificationType (Justification::centredLeft);
  31. nameLabel.setInterceptsMouseClicks (false, false);
  32. nameLabel.setColour (Label::textColourId, Colours::white);
  33. addAndMakeVisible (nameLabel);
  34. }
  35. void resized() override
  36. {
  37. auto b = getLocalBounds().toFloat();
  38. iconBounds = b.removeFromLeft (b.getHeight()).reduced (7, 7);
  39. arrowBounds = b.removeFromRight (b.getHeight());
  40. nameLabel.setBounds (b.toNearestInt());
  41. }
  42. void paint (Graphics& g) override
  43. {
  44. g.setColour (findColour (defaultButtonBackgroundColourId));
  45. g.fillRoundedRectangle (getLocalBounds().reduced (2, 3).toFloat(), 2.0f);
  46. g.setColour (Colours::white);
  47. g.fillPath (arrowPath = ProjucerLookAndFeel::getArrowPath (arrowBounds,
  48. getParentComponent()->getBoundsInParent().getY() == yPosition ? 2 : 0,
  49. true, Justification::centred));
  50. panelIcon.draw (g, iconBounds.toFloat(), false);
  51. }
  52. void mouseUp (const MouseEvent& e) override
  53. {
  54. if (arrowPath.getBounds().expanded (3).contains (e.getPosition().toFloat()))
  55. sendChangeMessage();
  56. }
  57. int direction = 0;
  58. int yPosition = 0;
  59. private:
  60. String name;
  61. Label nameLabel;
  62. Path iconPath;
  63. Icon panelIcon;
  64. Rectangle<float> arrowBounds, iconBounds;
  65. Path arrowPath;
  66. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
  67. };
  68. //==============================================================================
  69. class FindPanel : public Component,
  70. private TextEditor::Listener,
  71. private Timer,
  72. private FocusChangeListener
  73. {
  74. public:
  75. FindPanel (std::function<void (const String&)> cb)
  76. : callback (cb)
  77. {
  78. addAndMakeVisible (editor);
  79. editor.addListener (this);
  80. Desktop::getInstance().addFocusChangeListener (this);
  81. lookAndFeelChanged();
  82. }
  83. ~FindPanel()
  84. {
  85. Desktop::getInstance().removeFocusChangeListener (this);
  86. }
  87. void paintOverChildren (Graphics& g) override
  88. {
  89. if (! isFocused)
  90. return;
  91. g.setColour (findColour (defaultHighlightColourId));
  92. Path p;
  93. p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
  94. g.strokePath (p, PathStrokeType (2.0f));
  95. }
  96. void resized() override
  97. {
  98. editor.setBounds (getLocalBounds().reduced (2));
  99. }
  100. private:
  101. TextEditor editor;
  102. bool isFocused = false;
  103. std::function<void (const String&)> callback;
  104. //==============================================================================
  105. void lookAndFeelChanged() override
  106. {
  107. editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
  108. }
  109. void textEditorTextChanged (TextEditor&) override
  110. {
  111. startTimer (250);
  112. }
  113. void textEditorFocusLost (TextEditor&) override
  114. {
  115. isFocused = false;
  116. repaint();
  117. }
  118. void globalFocusChanged (Component* focusedComponent) override
  119. {
  120. if (focusedComponent == &editor)
  121. {
  122. isFocused = true;
  123. repaint();
  124. }
  125. }
  126. void timerCallback() override
  127. {
  128. stopTimer();
  129. callback (editor.getText());
  130. }
  131. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
  132. };
  133. //==============================================================================
  134. class ConcertinaTreeComponent : public Component,
  135. private Button::Listener
  136. {
  137. public:
  138. ConcertinaTreeComponent (TreePanelBase* tree, bool hasAddButton = false,
  139. bool hasSettingsButton = false, bool hasFindPanel = false)
  140. : treeToDisplay (tree)
  141. {
  142. if (hasAddButton)
  143. {
  144. addAndMakeVisible (addButton = new IconButton ("Add", &getIcons().plus));
  145. addButton->addListener (this);
  146. }
  147. if (hasSettingsButton)
  148. {
  149. addAndMakeVisible (settingsButton = new IconButton ("Settings", &getIcons().settings));
  150. settingsButton->addListener (this);
  151. }
  152. if (hasFindPanel)
  153. {
  154. addAndMakeVisible (findPanel = new FindPanel ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }));
  155. }
  156. addAndMakeVisible (treeToDisplay);
  157. }
  158. ~ConcertinaTreeComponent()
  159. {
  160. treeToDisplay.reset();
  161. addButton.reset();
  162. findPanel.reset();
  163. settingsButton.reset();
  164. }
  165. void resized() override
  166. {
  167. auto bounds = getLocalBounds();
  168. if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
  169. {
  170. auto bottomSlice = bounds.removeFromBottom (25);
  171. bottomSlice.removeFromRight (3);
  172. if (addButton != nullptr)
  173. addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  174. if (settingsButton != nullptr)
  175. settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  176. if (findPanel != nullptr)
  177. findPanel->setBounds (bottomSlice.reduced (2));
  178. }
  179. treeToDisplay->setBounds (bounds);
  180. }
  181. TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
  182. private:
  183. ScopedPointer<TreePanelBase> treeToDisplay;
  184. ScopedPointer<IconButton> addButton, settingsButton;
  185. ScopedPointer<FindPanel> findPanel;
  186. void buttonClicked (Button* b) override
  187. {
  188. if (b == addButton)
  189. showAddMenu();
  190. else if (b == settingsButton)
  191. showSettings();
  192. }
  193. void showAddMenu()
  194. {
  195. auto numSelected = treeToDisplay->tree.getNumSelectedItems();
  196. if (numSelected > 1)
  197. return;
  198. if (numSelected == 0)
  199. {
  200. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  201. root->showPopupMenu();
  202. }
  203. else
  204. {
  205. if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
  206. item->showAddMenu();
  207. }
  208. }
  209. void showSettings()
  210. {
  211. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  212. {
  213. treeToDisplay->tree.clearSelectedItems();
  214. root->showDocument();
  215. }
  216. }
  217. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
  218. };