TreeView tutorial?

I’m trying to follow the example in the JuceDemo but i’m not understanding how the treeViewItems are instantiated/stored so they are destroyed safely.

I have a customTreeViewItem class, which is really simple, it’s just a name and generates a button when createItemComponent() is called.

class CustomTreeViewItem : public TreeViewItem {
public:
    CustomTreeViewItem(const String& s) : name(s){}
    CustomTreeViewItem(const CustomTreeViewItem& other) { name=other.name;}
    //~CustomTreeViewItem(){}

    CustomTreeViewItem& operator=(const CustomTreeViewItem& other) {
        this->name = other.name;
        return *this;
    }


    bool mightContainSubItems() override {return true;}
    String getUniqueName() const override {return "CustomTreeViewItem";}
    void itemOpennessChanged(bool isNowOpen) override {;}
    //int getItemWidth() const override {return 50; }
    //int getItemHeight() const override {return 30; }
    Component* createItemComponent() override {
        TextButton *t = new TextButton();
        t->setButtonText(name);
        return t;
    }
private:
    String name;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CustomTreeViewItem)
};

I’m sure there are lots of design flaws in that, but that’s the least of my problems.

here’s how i’m using it:

CustomTreeViewItem* HistoryViewComponent::makeCustomTreeViewItem(const String n) {
    CustomTreeViewItem t(n);
    treeItems.push_back(t); //a std::vector<CustomTreeViewItem>
    return &(treeItems[0]);
}

and in another method somewhere:

tree.setRootItem( rootItem = new CustomTreeViewItem("rootItem") );
tree.setColour(TreeView::backgroundColourId, Colours::white);
tree.setMultiSelectEnabled(false);
treeItems.clear();

tree.getRootItem()->addSubItem( makeCustomTreeViewItem() );
addAndMakeVisible(tree);

So, what’s killing me is every time I close my app window, I get LeakDetector jasserts. I don’t understand where my CustomTreeViewItems should be kept, because the way that they’re used in JuceDemo is pretty confusing. So, can we get a real basic tutorial showing how to create a TreeView with TreeViewItems(String s="") as a starting point, that draws the tree-view in the same way that JuceDemo for TreeViews/ValueTrees draws?

Is your rootItem object a raw pointer? It won’t be deleted by the TreeView when it is destructed so this could be the reason your app is leaking - make sure you call delete on it or, even better, make it a ScopedPointer.

There are also a few big issues with the rest of your code. First of all you’ve used the JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR macro in your CustomTreeViewItem class but provided a copy assignment operator so I’d be surprised if this will even compile as there will be two conflicting definitions for the operator.

Also, using a std::vector of CustomTreeViewItem objects is unnecessary - the TreeView stores the sub items in an OwnedArray and will handle the lifetime of the objects so change your makeCustomTreeViewItem() method to just return a new CustomTreeViewItem and get rid of the std::vector. Also you really don’t want to be using a std::vector of CustomTreeViewItem objects as when elements get added or removed std::vector will internally move and copy these objects in memory so at some points of execution in your code you may have many more CustomTreeViewItem objects that you expect. It is much better to just store pointers to these objects.

Hope this helps.
Ed

From what i’ve figured out, the rootItem needs to be passed a data structure that will be turned into all of the subItems. The actual subItem generation happens inside void itemOpennessChanged(bool isNowOpen)

You don’t have to generate your subitems in the itemOpennessChanged call. You just need to have your root item call addSubItem at some point. For a simple tree you can do so in the constructor of your root item (and so on recursively down the tree).

can you post an example?

This is the simplest usage. It doesn’t leak, as far as I can tell. You can pass more complex structures to the TreeViewItem implementation, and you can optionally move the creation of the children to the itemOpennessChanged call, but that’s an optional performance improvement.

class SimpleTreeItem : public TreeViewItem
{
public:
	SimpleTreeItem(int level)
	{
		if (level > 0)
		{
			level--; 
			for (int i = 0; i < 4; ++i)
				addSubItem(new SimpleTreeItem(level));
		}
	}

	bool mightContainSubItems() override
	{
		return getNumSubItems() != 0;
	}

	void paintItem(Graphics& g, int width, int height) override
	{
		g.fillAll(Colours::grey);
		g.setColour(Colours::black);
		g.drawText("item", 0, 0, width, height, Justification::left);
	}

	JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SimpleTreeItem)
};


class TreeHolder : public Component
{
public:
	TreeHolder()
	{
		addAndMakeVisible(tree);
		tree.setRootItem(new SimpleTreeItem(3));
	}

	~TreeHolder()
	{
		tree.deleteRootItem(); // this deletes the children too...
	}

	void resized() override { tree.setBounds(getLocalBounds()); }

	TreeView tree;
};
3 Likes