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.

255 lines
7.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. //==============================================================================
  20. class ConcertinaHeader : public Component,
  21. public ChangeBroadcaster
  22. {
  23. public:
  24. ConcertinaHeader (String n, Path p)
  25. : Component (n), name (n), iconPath (p)
  26. {
  27. panelIcon = Icon (iconPath, Colours::white);
  28. nameLabel.setText (name, dontSendNotification);
  29. nameLabel.setJustificationType (Justification::centredLeft);
  30. nameLabel.setInterceptsMouseClicks (false, false);
  31. nameLabel.setColour (Label::textColourId, Colours::white);
  32. addAndMakeVisible (nameLabel);
  33. }
  34. void resized() override
  35. {
  36. auto b = getLocalBounds().toFloat();
  37. iconBounds = b.removeFromLeft (b.getHeight()).reduced (7, 7);
  38. arrowBounds = b.removeFromRight (b.getHeight());
  39. nameLabel.setBounds (b.toNearestInt());
  40. }
  41. void paint (Graphics& g) override
  42. {
  43. g.setColour (findColour (defaultButtonBackgroundColourId));
  44. g.fillRoundedRectangle (getLocalBounds().reduced (2, 3).toFloat(), 2.0f);
  45. g.setColour (Colours::white);
  46. g.fillPath (arrowPath = ProjucerLookAndFeel::getArrowPath (arrowBounds,
  47. getParentComponent()->getBoundsInParent().getY() == yPosition ? 2 : 0,
  48. true, Justification::centred));
  49. panelIcon.draw (g, iconBounds.toFloat(), false);
  50. }
  51. void mouseUp (const MouseEvent& e) override
  52. {
  53. if (! e.mouseWasDraggedSinceMouseDown())
  54. sendChangeMessage();
  55. }
  56. int direction = 0;
  57. int yPosition = 0;
  58. private:
  59. String name;
  60. Label nameLabel;
  61. Path iconPath;
  62. Icon panelIcon;
  63. Rectangle<float> arrowBounds, iconBounds;
  64. Path arrowPath;
  65. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
  66. };
  67. //==============================================================================
  68. class FindPanel : public Component,
  69. private Timer,
  70. private FocusChangeListener
  71. {
  72. public:
  73. FindPanel (std::function<void (const String&)> cb)
  74. : callback (cb)
  75. {
  76. addAndMakeVisible (editor);
  77. editor.onTextChange = [this] { startTimer (250); };
  78. editor.onFocusLost = [this]
  79. {
  80. isFocused = false;
  81. repaint();
  82. };
  83. Desktop::getInstance().addFocusChangeListener (this);
  84. lookAndFeelChanged();
  85. }
  86. ~FindPanel() override
  87. {
  88. Desktop::getInstance().removeFocusChangeListener (this);
  89. }
  90. void paintOverChildren (Graphics& g) override
  91. {
  92. if (! isFocused)
  93. return;
  94. g.setColour (findColour (defaultHighlightColourId));
  95. Path p;
  96. p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
  97. g.strokePath (p, PathStrokeType (2.0f));
  98. }
  99. void resized() override
  100. {
  101. editor.setBounds (getLocalBounds().reduced (2));
  102. }
  103. private:
  104. TextEditor editor;
  105. bool isFocused = false;
  106. std::function<void (const String&)> callback;
  107. //==============================================================================
  108. void lookAndFeelChanged() override
  109. {
  110. editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
  111. }
  112. void globalFocusChanged (Component* focusedComponent) override
  113. {
  114. if (focusedComponent == &editor)
  115. {
  116. isFocused = true;
  117. repaint();
  118. }
  119. }
  120. void timerCallback() override
  121. {
  122. stopTimer();
  123. callback (editor.getText());
  124. }
  125. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
  126. };
  127. //==============================================================================
  128. class ConcertinaTreeComponent : public Component
  129. {
  130. public:
  131. ConcertinaTreeComponent (TreePanelBase* tree, bool hasAddButton = false,
  132. bool hasSettingsButton = false, bool hasFindPanel = false)
  133. : treeToDisplay (tree)
  134. {
  135. if (hasAddButton)
  136. {
  137. addButton = std::make_unique<IconButton> ("Add", getIcons().plus);
  138. addAndMakeVisible (addButton.get());
  139. addButton->onClick = [this] { showAddMenu(); };
  140. }
  141. if (hasSettingsButton)
  142. {
  143. settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings);
  144. addAndMakeVisible (settingsButton.get());
  145. settingsButton->onClick = [this] { showSettings(); };
  146. }
  147. if (hasFindPanel)
  148. {
  149. findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); });
  150. addAndMakeVisible (findPanel.get());
  151. }
  152. addAndMakeVisible (treeToDisplay.get());
  153. }
  154. void resized() override
  155. {
  156. auto bounds = getLocalBounds();
  157. if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
  158. {
  159. auto bottomSlice = bounds.removeFromBottom (25);
  160. bottomSlice.removeFromRight (3);
  161. if (addButton != nullptr)
  162. addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  163. if (settingsButton != nullptr)
  164. settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
  165. if (findPanel != nullptr)
  166. findPanel->setBounds (bottomSlice.reduced (2));
  167. }
  168. treeToDisplay->setBounds (bounds);
  169. }
  170. TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
  171. private:
  172. std::unique_ptr<TreePanelBase> treeToDisplay;
  173. std::unique_ptr<IconButton> addButton, settingsButton;
  174. std::unique_ptr<FindPanel> findPanel;
  175. void showAddMenu()
  176. {
  177. auto numSelected = treeToDisplay->tree.getNumSelectedItems();
  178. if (numSelected > 1)
  179. return;
  180. if (numSelected == 0)
  181. {
  182. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  183. root->showPopupMenu();
  184. }
  185. else
  186. {
  187. if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
  188. item->showAddMenu();
  189. }
  190. }
  191. void showSettings()
  192. {
  193. if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
  194. {
  195. treeToDisplay->tree.clearSelectedItems();
  196. root->showDocument();
  197. }
  198. }
  199. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
  200. };