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.

311 lines
9.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE examples.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. The code included in this file is provided under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  7. To use, copy, modify, and/or distribute this software for any purpose with or
  8. without fee is hereby granted provided that the above copyright notice and
  9. this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
  11. WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
  12. PURPOSE, ARE DISCLAIMED.
  13. ==============================================================================
  14. */
  15. /*******************************************************************************
  16. The block below describes the properties of this PIP. A PIP is a short snippet
  17. of code that can be read by the Projucer and used to generate a JUCE project.
  18. BEGIN_JUCE_PIP_METADATA
  19. name: ValueTreesDemo
  20. version: 1.0.0
  21. vendor: JUCE
  22. website: http://juce.com
  23. description: Showcases value tree features.
  24. dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
  25. juce_gui_basics
  26. exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
  27. moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
  28. type: Component
  29. mainClass: ValueTreesDemo
  30. useLocalCopy: 1
  31. END_JUCE_PIP_METADATA
  32. *******************************************************************************/
  33. #pragma once
  34. #include "../Assets/DemoUtilities.h"
  35. //==============================================================================
  36. class ValueTreeItem final : public TreeViewItem,
  37. private ValueTree::Listener
  38. {
  39. public:
  40. ValueTreeItem (const ValueTree& v, UndoManager& um)
  41. : tree (v), undoManager (um)
  42. {
  43. tree.addListener (this);
  44. }
  45. String getUniqueName() const override
  46. {
  47. return tree["name"].toString();
  48. }
  49. bool mightContainSubItems() override
  50. {
  51. return tree.getNumChildren() > 0;
  52. }
  53. void paintItem (Graphics& g, int width, int height) override
  54. {
  55. if (isSelected())
  56. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::highlightedFill,
  57. Colours::teal));
  58. g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
  59. Colours::black));
  60. g.setFont (15.0f);
  61. g.drawText (tree["name"].toString(),
  62. 4, 0, width - 4, height,
  63. Justification::centredLeft, true);
  64. }
  65. void itemOpennessChanged (bool isNowOpen) override
  66. {
  67. if (isNowOpen && getNumSubItems() == 0)
  68. refreshSubItems();
  69. else
  70. clearSubItems();
  71. }
  72. var getDragSourceDescription() override
  73. {
  74. return "Drag Demo";
  75. }
  76. bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
  77. {
  78. return dragSourceDetails.description == "Drag Demo";
  79. }
  80. void itemDropped (const DragAndDropTarget::SourceDetails&, int insertIndex) override
  81. {
  82. OwnedArray<ValueTree> selectedTrees;
  83. getSelectedTreeViewItems (*getOwnerView(), selectedTrees);
  84. moveItems (*getOwnerView(), selectedTrees, tree, insertIndex, undoManager);
  85. }
  86. static void moveItems (TreeView& treeView, const OwnedArray<ValueTree>& items,
  87. ValueTree newParent, int insertIndex, UndoManager& undoManager)
  88. {
  89. if (items.size() > 0)
  90. {
  91. std::unique_ptr<XmlElement> oldOpenness (treeView.getOpennessState (false));
  92. for (auto* v : items)
  93. {
  94. if (v->getParent().isValid() && newParent != *v && ! newParent.isAChildOf (*v))
  95. {
  96. if (v->getParent() == newParent && newParent.indexOf (*v) < insertIndex)
  97. --insertIndex;
  98. v->getParent().removeChild (*v, &undoManager);
  99. newParent.addChild (*v, insertIndex, &undoManager);
  100. }
  101. }
  102. if (oldOpenness != nullptr)
  103. treeView.restoreOpennessState (*oldOpenness, false);
  104. }
  105. }
  106. static void getSelectedTreeViewItems (TreeView& treeView, OwnedArray<ValueTree>& items)
  107. {
  108. auto numSelected = treeView.getNumSelectedItems();
  109. for (int i = 0; i < numSelected; ++i)
  110. if (auto* vti = dynamic_cast<ValueTreeItem*> (treeView.getSelectedItem (i)))
  111. items.add (new ValueTree (vti->tree));
  112. }
  113. private:
  114. ValueTree tree;
  115. UndoManager& undoManager;
  116. void refreshSubItems()
  117. {
  118. clearSubItems();
  119. for (int i = 0; i < tree.getNumChildren(); ++i)
  120. addSubItem (new ValueTreeItem (tree.getChild (i), undoManager));
  121. }
  122. void valueTreePropertyChanged (ValueTree&, const Identifier&) override
  123. {
  124. repaintItem();
  125. }
  126. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { treeChildrenChanged (parentTree); }
  127. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { treeChildrenChanged (parentTree); }
  128. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { treeChildrenChanged (parentTree); }
  129. void valueTreeParentChanged (ValueTree&) override {}
  130. void treeChildrenChanged (const ValueTree& parentTree)
  131. {
  132. if (parentTree == tree)
  133. {
  134. refreshSubItems();
  135. treeHasChanged();
  136. setOpen (true);
  137. }
  138. }
  139. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeItem)
  140. };
  141. //==============================================================================
  142. class ValueTreesDemo final : public Component,
  143. public DragAndDropContainer,
  144. private Timer
  145. {
  146. public:
  147. ValueTreesDemo()
  148. {
  149. addAndMakeVisible (tree);
  150. tree.setTitle ("ValueTree");
  151. tree.setDefaultOpenness (true);
  152. tree.setMultiSelectEnabled (true);
  153. rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager));
  154. tree.setRootItem (rootItem.get());
  155. addAndMakeVisible (undoButton);
  156. addAndMakeVisible (redoButton);
  157. undoButton.onClick = [this] { undoManager.undo(); };
  158. redoButton.onClick = [this] { undoManager.redo(); };
  159. startTimer (500);
  160. setSize (500, 500);
  161. }
  162. ~ValueTreesDemo() override
  163. {
  164. tree.setRootItem (nullptr);
  165. }
  166. void paint (Graphics& g) override
  167. {
  168. g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
  169. }
  170. void resized() override
  171. {
  172. auto r = getLocalBounds().reduced (8);
  173. auto buttons = r.removeFromBottom (22);
  174. undoButton.setBounds (buttons.removeFromLeft (100));
  175. buttons.removeFromLeft (6);
  176. redoButton.setBounds (buttons.removeFromLeft (100));
  177. r.removeFromBottom (4);
  178. tree.setBounds (r);
  179. }
  180. static ValueTree createTree (const String& desc)
  181. {
  182. ValueTree t ("Item");
  183. t.setProperty ("name", desc, nullptr);
  184. return t;
  185. }
  186. static ValueTree createRootValueTree()
  187. {
  188. auto vt = createTree ("This demo displays a ValueTree as a treeview.");
  189. vt.appendChild (createTree ("You can drag around the nodes to rearrange them"), nullptr);
  190. vt.appendChild (createTree ("..and press 'delete' or 'backspace' to delete them"), nullptr);
  191. vt.appendChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), nullptr);
  192. int n = 1;
  193. vt.appendChild (createRandomTree (n, 0), nullptr);
  194. return vt;
  195. }
  196. static ValueTree createRandomTree (int& counter, int depth)
  197. {
  198. auto t = createTree ("Item " + String (counter++));
  199. if (depth < 3)
  200. for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
  201. t.appendChild (createRandomTree (counter, depth + 1), nullptr);
  202. return t;
  203. }
  204. void deleteSelectedItems()
  205. {
  206. OwnedArray<ValueTree> selectedItems;
  207. ValueTreeItem::getSelectedTreeViewItems (tree, selectedItems);
  208. for (auto* v : selectedItems)
  209. {
  210. if (v->getParent().isValid())
  211. v->getParent().removeChild (*v, &undoManager);
  212. }
  213. }
  214. bool keyPressed (const KeyPress& key) override
  215. {
  216. if (key == KeyPress::deleteKey || key == KeyPress::backspaceKey)
  217. {
  218. deleteSelectedItems();
  219. return true;
  220. }
  221. if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
  222. {
  223. undoManager.undo();
  224. return true;
  225. }
  226. if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
  227. {
  228. undoManager.redo();
  229. return true;
  230. }
  231. return Component::keyPressed (key);
  232. }
  233. private:
  234. TreeView tree;
  235. TextButton undoButton { "Undo" },
  236. redoButton { "Redo" };
  237. std::unique_ptr<ValueTreeItem> rootItem;
  238. UndoManager undoManager;
  239. void timerCallback() override
  240. {
  241. undoManager.beginNewTransaction();
  242. }
  243. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo)
  244. };