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.

268 lines
7.8KB

  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 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. {
  136. public:
  137. ConcertinaTreeComponent (TreePanelBase* tree, bool hasAddButton = false,
  138. bool hasSettingsButton = false, bool hasFindPanel = false)
  139. : treeToDisplay (tree)
  140. {
  141. if (hasAddButton)
  142. {
  143. addAndMakeVisible (addButton = new IconButton ("Add", &getIcons().plus));
  144. addButton->onClick = [this] { showAddMenu(); };
  145. }
  146. if (hasSettingsButton)
  147. {
  148. addAndMakeVisible (settingsButton = new IconButton ("Settings", &getIcons().settings));
  149. settingsButton->onClick = [this] { showSettings(); };
  150. }
  151. if (hasFindPanel)
  152. {
  153. addAndMakeVisible (findPanel = new FindPanel ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); }));
  154. }
  155. addAndMakeVisible (treeToDisplay);
  156. }
  157. ~ConcertinaTreeComponent()
  158. {
  159. treeToDisplay.reset();
  160. addButton.reset();
  161. findPanel.reset();
  162. settingsButton.reset();
  163. }
  164. void resized() override
  165. {
  166. auto bounds = getLocalBounds();
  167. if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
  168. {
  169. auto bottomSlice = bounds.removeFromBottom (25);
  170. bottomSlice.removeFromRight (3);
  171. if (addButton != nullptr)
  172. addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  173. if (settingsButton != nullptr)
  174. settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  175. if (findPanel != nullptr)
  176. findPanel->setBounds (bottomSlice.reduced (2));
  177. }
  178. treeToDisplay->setBounds (bounds);
  179. }
  180. TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
  181. private:
  182. ScopedPointer<TreePanelBase> treeToDisplay;
  183. ScopedPointer<IconButton> addButton, settingsButton;
  184. ScopedPointer<FindPanel> findPanel;
  185. void showAddMenu()
  186. {
  187. auto numSelected = treeToDisplay->tree.getNumSelectedItems();
  188. if (numSelected > 1)
  189. return;
  190. if (numSelected == 0)
  191. {
  192. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  193. root->showPopupMenu();
  194. }
  195. else
  196. {
  197. if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
  198. item->showAddMenu();
  199. }
  200. }
  201. void showSettings()
  202. {
  203. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  204. {
  205. treeToDisplay->tree.clearSelectedItems();
  206. root->showDocument();
  207. }
  208. }
  209. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
  210. };