FileChooser::launchAsync does nothing

Hello,
in my plugins I was always using FileChooser::browseForFileToOpen() to open files.

But after last update of JUCE and Projucer I can’t do that.
It looks like macro JUCE_MODAL_LOOPS_PERMITTED is set to false, so browseForFileToOpen() is not even declared in that case.

I’ve read (link to post) that I should now use launchAsync().

But actually it doesn’t work for me. When I call launchAsync() there is no action. Just nothing happens, no window is opened. I have no idea what I do wrong.

My code looks like that:

String defaultPath = File::getSpecialLocation(File::SpecialLocationType::userApplicationDataDirectory).getFullPathName();
File defaultDirectory = File(defaultPath );

std::unique_ptr<FileChooser> fChooser;
fChooser = std::make_unique<FileChooser>("Choose file", defaultDirectory, "*.txt", true, false, this);

auto folderChooserFlags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;

fChooser->launchAsync(folderChooserFlags, [this](const FileChooser& chooser)
{
    File chosenFile = chooser.getResult();
    // here is my code to handle chosen files data
});

I also tried other variations of folderChooserFlags, and nothing happens, but in one case I get runtime error, when my flags looked like that:
int folderChooserFlags = juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::openMode | FileBrowserComponent::canSelectDirectories;

then I got error:
Exception thrown: read access violation.
in the functional file in line:
return (_Mystorage._Ptrs[_Small_object_num_ptrs - 1]);

Please help me.

For any advice great thanks in advance.

It seems that the FileChooser is deleted before it gets launched. I suggest you store the unique pointer of the FileChooser in your class and then do myFileChooser = std::make_unique<FileChooser>(...). Look at the example in the documentation of the class.

1 Like

Hello,
great thanks. You are right. Your advice helped.

But now I have two more issues :slight_smile: :
But now I wonder how to properly capture if user clicked “cancel”.

I know that if user click “cancel” then FileChooser::getResult() gives me nullptr.
But I am not sure if it’s enough to be sure that user clicked “cancel”. I can imagine situation, that user didn’t click “cancel”, but getResult() can still return nullptr. Am I right? For example if user choose some strange file or file which was deleted before click “open” or anything else. Am I right?

So how to be sure that user clicked “cancel”?

And the second issue is about deleting object pointed by std::unique_ptr<FileChooser> fChooser;:
I need to use FileChooser in many various scenarios. Not only for opening file, but also for example for “save as…”. So I need different comunicate.
So now I should call:
std::make_unique<FileChooser>("Save as...", fPresetsDirectory, "*.txt", true, false, this);
instead:
std::make_unique<FileChooser>("Choose preset", fPresetsDirectory, "*.txt", true, false, this);

So I wonder if I can use for that the same object:
std::unique_ptr<FileChooser> fChooser;

I understand I can do that but before I use std::make_unique<FileChooser> first I should call:
fChooser.reset();

Am I right?
Or is there any better solution?

And is it good idea to call fChooser.reset(); every time on the end of lambda like:

fChooser->launchAsync(folderChooserFlags, [this](const FileChooser& chooser)
{
    File chosenFile = chooser.getResult();
    // here is my code to handle chosen files data
    `fChooser.reset();`
});

Is it good idea?

For any help great thanks in advance.

Best Regards

If the user clicks cancel, getResult() returns an empty juce::File (the path has an empty string) but if you prefer you can also use getResults() (with an s) and if the array is empty, it means that the user clicked cancel and nothing has been selected.

fChooser is a unique_ptr<> so each time you do fChooser = std::make_unique<FileChooser>(...), it deleted the previous pointer stored in fChooser and replaces it with the new one (so there is no need of fChooser.reset()). Of course, you can use the same unique_ptr<> (and if fact most of the time, you should), it ensures that only one FileChooser is displayed at a time.

1 Like

That’s right. I would like to use the same unique_ptr<FileChooser>, but to do that first I need to be able to change “dialogBoxTitle”, which is normally set only in FileChooser constructor. So to have new “dialogBoxTitle” I need to create new unique_ptr<FileChooser>.
I can’t find the solution how to change the “dialogBoxTitle” in existing FileChooser object

Yes, each time you want to change the title, the file patterns, etc. you have to create a new instance. Why would you like to change the title of an existing instance?

1 Like

Why would you like to change the title of an existing instance?
As you suggested before to use the same unique_ptr<> I thought you meant the same object pointed by unique_ptr<>. So to do that I need various “dialogBoxTitle” for various type of window.

But if you meant just the same unique_ptr<> (not object pointed by) so then everything make sense for me.

Thanks for your help.

So what would be the correct way to test for cancelled in this case? File::exists()?

        File myFile (chooser.getResult());
        
        if (myFile.exists())
            DBG("GOOD!" + myFile.getFullPathName());
        else
            DBG("CANCELLED!");

File::exists() returns false immediately if the path string is empty, so it seems like the best choice for detecting cancellation for single file selections IMO. I like the approach of calling FileChooser::getResults() and checking its size for multi-file selections.

It would be nice to have to some explicit indicator that the user cancelled the selection, though. The old modal FileChooser::browseFor* functions returned a bool for this purpose, but there’s no equivalent when using FileChooser::launchAsync.

1 Like

I’m using this to check if it’s cancelled:

if (myFile == File())
    // cancelled`