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