Hello,
I don’t understand the UndoManager Tutorial completly. I understand how to add an UndoManager but I don’t understand the structure of the code. I actually don’t need an undoManager but I need the TreeList. For my app I need a list of Tracknames which can be dragged around and deleted. That is actually all implemented in the TreeView in the undomanager tutorial. I’m stuck to transfer the code into my project. So far, I have the component visible in my app but the functionality is wrong. If I just drag an item from top to bottom, all the items underneath are deleted.
If I don’t use the ValueTree created in my project and create a new ValueTree, the functionality works fine.
FileViewComponent::FileViewComponent(juce::ValueTree& vT)
{
addAndMakeVisible(tree);
tree.setDefaultOpenness(true);
tree.setMultiSelectEnabled(true);
vT.setProperty("name", "ValueTree", nullptr);
//rootItem.reset(new FileViewItem(vT)); <---- Using my Project Value Tree(Buggy)
rootItem.reset(new FileViewItem(createTree("TEST"))); // <----- Test Value Tree(works fine)
tree.setRootItem(rootItem.get());
setSize(600, 400);
}
Here is my FileViewItem:
FileViewItem::FileViewItem(const juce::ValueTree& v): tree(v)
{
tree.addListener(this);
}
juce::String FileViewItem::getUniqueName() const
{
return tree["name"].toString();
}
bool FileViewItem::mightContainSubItems()
{
return tree.getNumChildren() > 0;
}
void FileViewItem::paintItem(juce::Graphics& g, int width, int height)
{
g.setColour(juce::Colours::white);
g.setFont(15.0f);
g.drawText(tree["name"].toString(),
4, 0, width - 4, height,
juce::Justification::centredLeft, true);
}
void FileViewItem::itemOpennessChanged(bool isNowOpen)
{
if (isNowOpen && getNumSubItems() == 0)
refreshSubItems();
else
clearSubItems();
}
juce::var FileViewItem::getDragSourceDescription()
{
return "Drag Source";
}
bool FileViewItem::isInterestedInDragSource(const juce::DragAndDropTarget::SourceDetails& dragSourceDetails)
{
return dragSourceDetails.description == "Drag Source";
}
void FileViewItem::itemDropped(const juce::DragAndDropTarget::SourceDetails&, int insertIndex)
{
juce::OwnedArray<juce::ValueTree> selectedTrees;
getSelectedTreeViewItems(*getOwnerView(), selectedTrees);
moveItems(*getOwnerView(), selectedTrees, tree, insertIndex);
}
void FileViewItem::moveItems(juce::TreeView& treeView, const juce::OwnedArray<juce::ValueTree>& items,
juce::ValueTree newParent, int insertIndex)
{
DBG("Das ist der Tree vor dem Move: " << items[0]->getParent().toXmlString());
if (items.size() > 0)
{
std::unique_ptr<juce::XmlElement> oldOpenness(treeView.getOpennessState(false));
DBG("This is old Openness: " << oldOpenness->toString());
for (auto i = items.size(); --i >= 0;)
{
auto& v = *items.getUnchecked(i);
DBG("This is unchecked: " << v.toXmlString());
if (v.getParent().isValid() && newParent != v && !newParent.isAChildOf(v))
{
if (v.getParent() == newParent && newParent.indexOf(v) < insertIndex)
--insertIndex;
v.getParent().removeChild(v, nullptr);
newParent.addChild(v, insertIndex, nullptr);
}
}
if (oldOpenness != nullptr)
treeView.restoreOpennessState(*oldOpenness, false);
}
DBG("Tree after movin: " << items[0]->getParent().toXmlString());
}
void FileViewItem::getSelectedTreeViewItems(juce::TreeView& treeView, juce::OwnedArray<juce::ValueTree>& items)
{
auto numSelected = treeView.getNumSelectedItems();
for (auto i = 0; i < numSelected; ++i)
if (auto* vti = dynamic_cast<FileViewItem*> (treeView.getSelectedItem(i)))
items.add(new juce::ValueTree(vti->tree));
}
void FileViewItem::refreshSubItems()
{
clearSubItems();
for (auto i = 0; i < tree.getNumChildren(); ++i)
addSubItem(new FileViewItem(tree.getChild(i)));
}
void FileViewItem::valueTreePropertyChanged(juce::ValueTree&, const juce::Identifier&)
{
repaintItem();
}
void FileViewItem::treeChildrenChanged(const juce::ValueTree& parentTree)
{
if (parentTree == tree)
{
refreshSubItems();
treeHasChanged();
setOpen(true);
}
}
void FileViewItem::addItem(juce::ValueTree vT)
{
DBG("Adding Item: " << vT.getType().toString() << " to: " << tree.getType().toString());
tree.addChild(vT, -1, nullptr);
}
So as you can see, I actually didn’t change anything compared to the tutorial. The issue of deleting the bottom childs arises somewhere in the moveItem function, I guess. As you can see my DBG marcos, I was checking how the Tree looks before and after. The following shows the console outs for the project ValueTree. This is not as intended:
<Channel_0 name="ValueTree" ChannelNumber="1" State="Stopped" Output="4"
Input="2">
<Item name="path\to\file.wav"/>
<Item name="path\to\file.wav"/>
<Item name="path\to\file.wav"/>
</Channel_0>
Das ist die alte Openness: <?xml version="1.0" encoding="UTF-8"?>
<OPEN id="ValueTree">
<SELECTED id="/ValueTree/path\to\file.wav"/>
</OPEN>
Das sind die unchecked: <?xml version="1.0" encoding="UTF-8"?>
<Item name=""path\to\file.wav""/>
Das ist der Tree nach dem Move: <?xml version="1.0" encoding="UTF-8"?>
<Channel_0 name="ValueTree" ChannelNumber="1" State="Stopped" Output="4"
Input="2">
<Item name=""path\to\file.wav""/>
</Channel_0>
But when I use the Test Tree, it’s all as expected:
Tree before Move: <?xml version="1.0" encoding="UTF-8"?>
<Item name="TEST">
<Item name="C:\Users\Lazlo\Desktop\5 Drumgroove 1.wav"/>
<Item name="C:\Users\Lazlo\Desktop\6 Drumgroove 2.wav"/>
<Item name="C:\Users\Lazlo\Desktop\7 cello.wav"/>
<Item name="C:\Users\Lazlo\Desktop\cello.wav"/>
</Item>
Das ist die alte Openness: <?xml version="1.0" encoding="UTF-8"?>
<OPEN id="TESTEN">
<SELECTED id="/TESTEN/Path/TO/FILE"/>
</OPEN>
Das sind die unchecked: <?xml version="1.0" encoding="UTF-8"?>
<Item name="path\to\file.wav"/>
Das ist der Tree nach dem Move: <?xml version="1.0" encoding="UTF-8"?>
<Item name="TEST">
<Item name="path\to\file.wav"/>
<Item name="path\to\file.wav"/>
<Item name="path\to\file.wav"/>
<Item name="path\to\file.wav"/>
</Item>
I can’t find any differences apart from that my Project ValueTree doesn’t have the Name “Item” but also can not verify that it’s really the issue. Does anybody see the issue or has a hint for me? I’m stuck here since 3 days now ![]()
Cheers,
LaZzle
