TreeView drag drop space


#1

Hi
Does anyone know of a clever way to make some room at the bottom of a treeview when dragging.
Here is the case; I have a tree view with many items, so scrollbar is active.
When I drag a new item in to the list, it scrolls to the bottom (like it should) but I would like there to be maybe one item height of free space at the bottom, so the user can drop a item at the end. Right now he/she has to drop it very precisely to add it to the end.
Does anyone know of a way to create such a extra space?


#2

You could create a “fake” transparent item at the bottom of the list when the user starts dragging and then remove it when the drag ends. Something like this (I haven’t tested it very thoroughly though and it doesn’t move the items around when dragging):

class MainContentComponent   : public Component,
                               public DragAndDropContainer
{
public:
    //==============================================================================
    MainContentComponent()
    {
        addAndMakeVisible (treeView);
        
        treeView.setRootItem (treeRoot = new RootItem());
        
        for (auto i = 0; i < 20; ++ i)
            treeView.getRootItem()->addSubItem (new SubItem());
        
        setSize (600, 400);
    }
    ~MainContentComponent()
    {
        treeView.setRootItem (nullptr);
    }

    void paint (Graphics& g) override
    {
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
    }
    
    void resized() override
    {
        treeView.setBounds (getLocalBounds().reduced (5));
    }
    
    void dragOperationStarted (const DragAndDropTarget::SourceDetails&) override
    {
        treeView.getRootItem()->addSubItem (new FakeItem());
    }
    
    void dragOperationEnded (const DragAndDropTarget::SourceDetails&) override
    {
        auto* root = treeView.getRootItem();
        for (auto i = root->getNumSubItems(); --i >= 0;)
        {
            if (auto* fakeItem = dynamic_cast<FakeItem*> (root->getSubItem (i)))
                root->removeSubItem (i);
        }
    }

private:
    TreeView treeView;
    
    struct RootItem    : public TreeViewItem
    {
        RootItem()
        {
        }
        
        bool mightContainSubItems() override    { return true; }
        
        void paintItem (Graphics& g, int width, int height) override
        {
            g.setColour (Colours::hotpink);
            g.fillRect (0, 0, width, height);
        }
    };
    
    struct SubItem     : public TreeViewItem
    {
        SubItem()
        {
            r.setSeedRandomly();
            colourToUse = colours [r.nextInt (4)];
        }
        
        bool mightContainSubItems() override    { return false; }
        
        void paintItem (Graphics& g, int width, int height) override
        {
            g.setColour (colourToUse);
            g.fillRect (0, 0, width, height);
        }
        
        var getDragSourceDescription() override { return "SubItem"; }
        
        Random r;
        Colour colourToUse;
        int dragDescription;
        Array<Colour> colours  { Colours::blue, Colours::red, Colours::green, Colours::purple };
    };
    
    struct FakeItem    : public TreeViewItem
    {
        FakeItem()
        {
        }
        bool mightContainSubItems() override    { return false; }
    };
    
    ScopedPointer<RootItem> treeRoot;
    
    //==============================================================================
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

#3

Thanks @ed95!
That might work, I’ll try to implement it in a real-world setting.