Programmatically open FileTreeComponent

I want to open a FileTreeComponent and selected a certain file or directory. I thought

fileTreeComp->setSelectedFile(File("C:\\Midi\\Beatles\bearable"))

would do the job, but it won't do more than just open the C:\Midi node.

Is setSelectedFile() not supposed to also open relevant tree branches?

Or does it only set a selected file if that particular tree node is already open?

 

I don't know the answer, but if that string is taken straight from your code, have you spotted the typo?:

..."Beatles\bearable" instead of ..."Beatles\\bearable"" "?

No, it's not, it's a typo in the forum, but thanks anyway for the suggestion.

In fact I checked setSelectedFile() in the debugger, and it will recursively find the right node even when the path is very long. And set it to selected. But it won't open the tree. Maybe it's not supposed to do that, but then what's the purpose of this function?

It does open the tree if it can do, but bear in mind that opening these folders isn't trivial - it involves recursively scanning potentially huge folders for child files, so there are timeouts and background threads involved, etc. There's another whole thread about this somewhere. If it doesn't work, try debugging into the method and see what's going on.

1 Like

You mean this?

http://www.juce.com/forum/topic/interesting-observations

Well, after a few monts the outcome of that thread seemed to be... inconclusive. I'm obvisouly in deep sh... uncharted code territory. I don't feel I have the mental strength to take up that thread rigth now. Instead I've decided to enter the Hack of the month competition with the following entry:

class SelectDeeplyNestedFile : public Timer
    {
    public:
        SelectDeeplyNestedFile(FileTreeComponent *fileTreeComp_, String fileName) :
            fileTreeComp(fileTreeComp_)
        {
            StringArray check = StringArray::fromTokens(fileName, File::separatorString, String::empty);
            countDown = check.size();

            file = File(fileName),

            startTimer(100);
        }
        
    private:
        int countDown;
        File file;
        FileTreeComponent *fileTreeComp;
                
        void timerCallback() override
        {
            fileTreeComp->setSelectedFile(file);
            TreeViewItem *tvi = fileTreeComp->getSelectedItem(0);
            if (tvi)
                tvi->setOpen(true);

            if (--countDown <= 0)
            {
                if (tvi)
                    fileTreeComp->scrollToKeepItemVisible (tvi);

                delete this;
            }
        }
    };

    String lastSelectedFile = getStringSetting("lastSelectedFile");
    if (lastSelectedFile.isNotEmpty())
        new SelectDeeplyNestedFile(fileTreeComp, lastSelectedFile);
2 Likes

A very respectable hack!

Great, thanks!

 

I just changed it a bit by using a safe pointer :

 


class SelectDeeplyNestedFile : public Timer
{
public:
    //==============================================================================
    SelectDeeplyNestedFile (FileTreeComponent* fileTreeComp_, String fileName)
    : fileTreeComp (fileTreeComp_)
    {
        StringArray check = StringArray::fromTokens (fileName, File::separatorString, String::empty);
        countDown = check.size();
        
        file = File (fileName),
        
        startTimer (100);
    }
    
private:
    //==============================================================================
    int countDown;
    File file;
    Component::SafePointer<FileTreeComponent> fileTreeComp;
    
    void timerCallback() override
    {
        if (fileTreeComp == nullptr)
        {
            delete this;
        }
        else
        {
            fileTreeComp->setSelectedFile (file);
            TreeViewItem *tvi = fileTreeComp->getSelectedItem (0);
            
            if (tvi)
                tvi->setOpen (true);
            
            if (--countDown <= 0)
            {
                if (tvi)
                    fileTreeComp->scrollToKeepItemVisible (tvi);
                
                delete this;
            }
        }
    }
};

2 Likes

Is this still the way to go or are there better solutions available in the meantime?

I’m afraid that’s the case. I just tested to just doing fileTreeComp->setSelectedFile(file); and that didn’t do it.

1 Like

Thanks for looking at it. I came to the same conclusion. It still needs a background thread that waits for it.

It would be great if there would be a way to wait async without blocking the UI thread (the function that fills the tree would return a await handle / task). I think that would be possible in C++ in the meantime.

It would be great way to handle problems like this.

nothing is blocking the UI in the solution above, timer acts asynchronously

A timer or also another thread that checks for the file and select it does not block, but it’s some kind of old fashioned and also error prone.

In other languages you get a task handle when you execute asynchronous code:

await fileTree.setSelectedFolder(folder); // waits asynchronous
fileTree.setSelectedFile(file); 

I think this would also be a great concept in JUCE at some places if not already used. Not sure if its supported by C++.