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.

261 lines
7.7KB

  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 (! e.mouseWasDraggedSinceMouseDown())
  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 Timer,
  71. private FocusChangeListener
  72. {
  73. public:
  74. FindPanel (std::function<void (const String&)> cb)
  75. : callback (cb)
  76. {
  77. addAndMakeVisible (editor);
  78. editor.onTextChange = [this] { startTimer (250); };
  79. editor.onFocusLost = [this]
  80. {
  81. isFocused = false;
  82. repaint();
  83. };
  84. Desktop::getInstance().addFocusChangeListener (this);
  85. lookAndFeelChanged();
  86. }
  87. ~FindPanel()
  88. {
  89. Desktop::getInstance().removeFocusChangeListener (this);
  90. }
  91. void paintOverChildren (Graphics& g) override
  92. {
  93. if (! isFocused)
  94. return;
  95. g.setColour (findColour (defaultHighlightColourId));
  96. Path p;
  97. p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
  98. g.strokePath (p, PathStrokeType (2.0f));
  99. }
  100. void resized() override
  101. {
  102. editor.setBounds (getLocalBounds().reduced (2));
  103. }
  104. private:
  105. TextEditor editor;
  106. bool isFocused = false;
  107. std::function<void (const String&)> callback;
  108. //==============================================================================
  109. void lookAndFeelChanged() override
  110. {
  111. editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
  112. }
  113. void globalFocusChanged (Component* focusedComponent) override
  114. {
  115. if (focusedComponent == &editor)
  116. {
  117. isFocused = true;
  118. repaint();
  119. }
  120. }
  121. void timerCallback() override
  122. {
  123. stopTimer();
  124. callback (editor.getText());
  125. }
  126. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
  127. };
  128. //==============================================================================
  129. class ConcertinaTreeComponent : public Component
  130. {
  131. public:
  132. ConcertinaTreeComponent (TreePanelBase* tree, bool hasAddButton = false,
  133. bool hasSettingsButton = false, bool hasFindPanel = false)
  134. : treeToDisplay (tree)
  135. {
  136. if (hasAddButton)
  137. {
  138. addAndMakeVisible (addButton = new IconButton ("Add", &getIcons().plus));
  139. addButton->onClick = [this] { showAddMenu(); };
  140. }
  141. if (hasSettingsButton)
  142. {
  143. addAndMakeVisible (settingsButton = new IconButton ("Settings", &getIcons().settings));
  144. settingsButton->onClick = [this] { showSettings(); };
  145. }
  146. if (hasFindPanel)
  147. {
  148. addAndMakeVisible (findPanel = new FindPanel ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }));
  149. }
  150. addAndMakeVisible (treeToDisplay);
  151. }
  152. ~ConcertinaTreeComponent()
  153. {
  154. treeToDisplay.reset();
  155. addButton.reset();
  156. findPanel.reset();
  157. settingsButton.reset();
  158. }
  159. void resized() override
  160. {
  161. auto bounds = getLocalBounds();
  162. if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
  163. {
  164. auto bottomSlice = bounds.removeFromBottom (25);
  165. bottomSlice.removeFromRight (3);
  166. if (addButton != nullptr)
  167. addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  168. if (settingsButton != nullptr)
  169. settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  170. if (findPanel != nullptr)
  171. findPanel->setBounds (bottomSlice.reduced (2));
  172. }
  173. treeToDisplay->setBounds (bounds);
  174. }
  175. TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
  176. private:
  177. ScopedPointer<TreePanelBase> treeToDisplay;
  178. ScopedPointer<IconButton> addButton, settingsButton;
  179. ScopedPointer<FindPanel> findPanel;
  180. void showAddMenu()
  181. {
  182. auto numSelected = treeToDisplay->tree.getNumSelectedItems();
  183. if (numSelected > 1)
  184. return;
  185. if (numSelected == 0)
  186. {
  187. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  188. root->showPopupMenu();
  189. }
  190. else
  191. {
  192. if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
  193. item->showAddMenu();
  194. }
  195. }
  196. void showSettings()
  197. {
  198. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  199. {
  200. treeToDisplay->tree.clearSelectedItems();
  201. root->showDocument();
  202. }
  203. }
  204. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
  205. };