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.

298 lines
9.1KB

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