1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

FileChooser: Hide chooser when it leaves scope on Windows

This commit is contained in:
reuk 2021-02-26 23:42:47 +00:00
parent e813531d9b
commit 45409bb4e6
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
9 changed files with 105 additions and 95 deletions

View file

@ -495,7 +495,7 @@ int main (int argc, char** argv)
std::transform (argv, argv + argc, std::back_inserter (arguments), getString);
juce::ArgumentList argumentList { arguments.front(),
juce::StringArray (arguments.data() + 1, arguments.size() - 1) };
juce::StringArray (arguments.data() + 1, (int) arguments.size() - 1) };
using Fn = typename std::add_lvalue_reference<decltype (writeBinaryData)>::type;

View file

@ -2179,10 +2179,7 @@ public:
operator ComponentType*() const noexcept { return getComponent(); }
/** Returns the component that this pointer refers to, or null if the component no longer exists. */
ComponentType* operator->() noexcept { return getComponent(); }
/** Returns the component that this pointer refers to, or null if the component no longer exists. */
const ComponentType* operator->() const noexcept { return getComponent(); }
ComponentType* operator->() const noexcept { return getComponent(); }
/** If the component is valid, this deletes it and sets this pointer to null. */
void deleteAndZero() { delete getComponent(); }

View file

@ -158,7 +158,7 @@ bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previ
{
FocusRestorer focusRestorer;
pimpl.reset (createPimpl (flags, previewComp));
pimpl = createPimpl (flags, previewComp);
pimpl->runModally();
// ensure that the finished function was invoked
@ -179,12 +179,12 @@ void FileChooser::launchAsync (int flags, std::function<void (const FileChooser&
asyncCallback = std::move (callback);
pimpl.reset (createPimpl (flags, previewComp));
pimpl = createPimpl (flags, previewComp);
pimpl->launch();
}
FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp)
std::unique_ptr<FileChooser::Pimpl> FileChooser::createPimpl (int flags, FilePreviewComponent* previewComp)
{
results.clear();
@ -214,10 +214,8 @@ FileChooser::Pimpl* FileChooser::createPimpl (int flags, FilePreviewComponent* p
{
return showPlatformDialog (*this, flags, previewComp);
}
else
{
return new NonNative (*this, flags, previewComp);
}
return std::make_unique<NonNative> (*this, flags, previewComp);
}
Array<File> FileChooser::getResults() const noexcept

View file

@ -325,12 +325,11 @@ private:
virtual void runModally() = 0;
};
std::unique_ptr<Pimpl> pimpl;
std::shared_ptr<Pimpl> pimpl;
//==============================================================================
Pimpl* createPimpl (int, FilePreviewComponent*);
static Pimpl* showPlatformDialog (FileChooser&, int,
FilePreviewComponent*);
std::unique_ptr<Pimpl> createPimpl (int, FilePreviewComponent*);
static std::unique_ptr<Pimpl> showPlatformDialog (FileChooser&, int, FilePreviewComponent*);
class NonNative;
friend class NonNative;

View file

@ -219,11 +219,11 @@ private:
FileChooser::Native* FileChooser::Native::currentFileChooser = nullptr;
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent*)
std::unique_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent*)
{
if (FileChooser::Native::currentFileChooser == nullptr)
return new FileChooser::Native (owner, flags);
return std::make_unique<FileChooser::Native> (owner, flags);
// there can only be one file chooser on Android at a once
jassertfalse;

View file

@ -379,10 +379,10 @@ bool FileChooser::isPlatformDialogAvailable()
#endif
}
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent*)
std::unique_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent*)
{
return new FileChooser::Native (owner, flags);
return std::make_unique<FileChooser::Native> (owner, flags);
}
#if JUCE_DEPRECATION_IGNORED

View file

@ -256,9 +256,9 @@ bool FileChooser::isPlatformDialogAvailable()
#endif
}
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*)
std::unique_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags, FilePreviewComponent*)
{
return new Native (owner, flags);
return std::make_unique<Native> (owner, flags);
}
} // namespace juce

View file

@ -377,10 +377,10 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Native)
};
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent* preview)
std::unique_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent* preview)
{
return new FileChooser::Native (owner, flags, preview);
return std::make_unique<FileChooser::Native> (owner, flags, preview);
}
bool FileChooser::isPlatformDialogAvailable()

152
modules/juce_gui_basics/native/juce_win32_FileChooser.cpp Normal file → Executable file
View file

@ -26,32 +26,23 @@
namespace juce
{
// Win32NativeFileChooser needs to be a reference counted object as there
// is no way for the parent to know when the dialog HWND has actually been
// created without pumping the message thread (which is forbidden when modal
// loops are disabled). However, the HWND pointer is the only way to cancel
// the dialog box. This means that the actual native FileChooser HWND may
// not have been created yet when the user deletes JUCE's FileChooser class. If this
// occurs the Win32NativeFileChooser will still have a reference count of 1 and will
// simply delete itself immediately once the HWND will have been created a while later.
class Win32NativeFileChooser : public ReferenceCountedObject,
class Win32NativeFileChooser : public std::enable_shared_from_this<Win32NativeFileChooser>,
private Thread
{
public:
using Ptr = ReferenceCountedObjectPtr<Win32NativeFileChooser>;
enum { charsAvailableForResult = 32768 };
Win32NativeFileChooser (Component* parent, int flags, FilePreviewComponent* previewComp,
const File& startingFile, const String& titleToUse,
const String& filtersToUse)
: Thread ("Native Win32 FileChooser"),
owner (parent), title (titleToUse), filtersString (filtersToUse.replaceCharacter (',', ';')),
owner (parent),
title (titleToUse),
filtersString (filtersToUse.replaceCharacter (',', ';')),
selectsDirectories ((flags & FileBrowserComponent::canSelectDirectories) != 0),
isSave ((flags & FileBrowserComponent::saveMode) != 0),
warnAboutOverwrite ((flags & FileBrowserComponent::warnAboutOverwriting) != 0),
selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0),
nativeDialogRef (nullptr), shouldCancel (0)
selectMultiple ((flags & FileBrowserComponent::canSelectMultipleItems) != 0)
{
auto parentDirectory = startingFile.getParentDirectory();
@ -95,10 +86,7 @@ public:
{
jassert (! isThreadRunning());
threadHasReference.reset();
startThread();
threadHasReference.wait (-1);
}
else
{
@ -112,7 +100,7 @@ public:
ScopedLock lock (deletingDialog);
customComponent = nullptr;
shouldCancel.set (1);
shouldCancel = true;
if (auto hwnd = nativeDialogRef.get())
EndDialog (hwnd, 0);
@ -151,12 +139,11 @@ private:
};
//==============================================================================
Component::SafePointer<Component> owner;
const Component::SafePointer<Component> owner;
String title, filtersString;
std::unique_ptr<CustomComponentHolder> customComponent;
String initialPath, returnedString;
WaitableEvent threadHasReference;
CriticalSection deletingDialog;
bool selectsDirectories, isSave, warnAboutOverwrite, selectMultiple;
@ -164,15 +151,15 @@ private:
HeapBlock<WCHAR> files;
HeapBlock<WCHAR> filters;
Atomic<HWND> nativeDialogRef;
Atomic<int> shouldCancel;
Atomic<HWND> nativeDialogRef { nullptr };
bool shouldCancel = false;
struct FreeLPWSTR
{
void operator() (LPWSTR ptr) const noexcept { CoTaskMemFree (ptr); }
};
bool showDialog (IFileDialog& dialog, bool async) const
bool showDialog (IFileDialog& dialog, bool async)
{
FILEOPENDIALOGOPTIONS flags = {};
@ -235,7 +222,49 @@ private:
if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec)))
return false;
return dialog.Show (static_cast<HWND> (async ? nullptr : owner->getWindowHandle())) == S_OK;
struct Events : public ComBaseClassHelper<IFileDialogEvents>
{
explicit Events (Win32NativeFileChooser& o) : owner (o) {}
JUCE_COMRESULT OnTypeChange (IFileDialog* d) override
{
HWND hwnd = nullptr;
IUnknown_GetWindow (d, &hwnd);
ScopedLock lock (owner.deletingDialog);
if (hwnd != nullptr)
owner.nativeDialogRef = hwnd;
return owner.shouldCancel ? S_FALSE : S_OK;
}
JUCE_COMRESULT OnFolderChanging (IFileDialog*, IShellItem*) override { return S_OK; }
JUCE_COMRESULT OnFileOk (IFileDialog*) override { return S_OK; }
JUCE_COMRESULT OnFolderChange (IFileDialog*) override { return S_OK; }
JUCE_COMRESULT OnSelectionChange (IFileDialog*) override { return S_OK; }
JUCE_COMRESULT OnShareViolation (IFileDialog*, IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) override { return S_OK; }
JUCE_COMRESULT OnOverwrite (IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) override { return S_OK; }
Win32NativeFileChooser& owner;
};
DWORD cookie = 0;
dialog.Advise (new Events { *this }, &cookie);
{
ScopedLock lock (deletingDialog);
if (shouldCancel)
return false;
}
const auto result = dialog.Show (async ? nullptr : static_cast<HWND> (owner->getWindowHandle())) == S_OK;
ScopedLock lock (deletingDialog);
nativeDialogRef = nullptr;
return result;
}
//==============================================================================
@ -447,33 +476,21 @@ private:
void run() override
{
// We use a functor rather than a lambda here because
// we want to move ownership of the Ptr into the function
// object, and C++11 doesn't support general lambda capture
struct AsyncCallback
// IUnknown_GetWindow will only succeed when instantiated in a single-thread apartment
CoInitializeEx (nullptr, COINIT_APARTMENTTHREADED);
auto resultsCopy = openDialog (true);
auto safeOwner = owner;
std::weak_ptr<Win32NativeFileChooser> weakThis = shared_from_this();
MessageManager::callAsync ([resultsCopy, safeOwner, weakThis]
{
AsyncCallback (Ptr p, Array<URL> r)
: ptr (std::move (p)),
results (std::move (r)) {}
if (auto locked = weakThis.lock())
locked->results = resultsCopy;
void operator()()
{
ptr->results = std::move (results);
if (ptr->owner != nullptr)
ptr->owner->exitModalState (ptr->results.size() > 0 ? 1 : 0);
}
Ptr ptr;
Array<URL> results;
};
// as long as the thread is running, don't delete this class
Ptr safeThis (this);
threadHasReference.signal();
auto r = openDialog (true);
MessageManager::callAsync (AsyncCallback (std::move (safeThis), std::move (r)));
if (safeOwner != nullptr)
safeOwner->exitModalState (resultsCopy.size() > 0 ? 1 : 0);
});
}
static HashMap<HWND, Win32NativeFileChooser*>& getNativeDialogList()
@ -482,9 +499,9 @@ private:
return dialogs;
}
static Win32NativeFileChooser* getNativePointerForDialog (HWND hWnd)
static Win32NativeFileChooser* getNativePointerForDialog (HWND hwnd)
{
return getNativeDialogList()[hWnd];
return getNativeDialogList()[hwnd];
}
//==============================================================================
@ -552,7 +569,7 @@ private:
ScopedLock lock (deletingDialog);
getNativeDialogList().set (hdlg, this);
if (shouldCancel.get() != 0)
if (shouldCancel)
{
EndDialog (hdlg, 0);
}
@ -617,7 +634,7 @@ private:
{
ScopedLock lock (deletingDialog);
if (customComponent != nullptr && shouldCancel.get() == 0)
if (customComponent != nullptr && ! shouldCancel)
{
if (FilePreviewComponent* comp = dynamic_cast<FilePreviewComponent*> (customComponent->getChildComponent (0)))
{
@ -715,14 +732,15 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32NativeFileChooser)
};
class FileChooser::Native : public Component,
class FileChooser::Native : public std::enable_shared_from_this<Native>,
public Component,
public FileChooser::Pimpl
{
public:
Native (FileChooser& fileChooser, int flags, FilePreviewComponent* previewComp)
: owner (fileChooser),
nativeFileChooser (new Win32NativeFileChooser (this, flags, previewComp, fileChooser.startingFile,
fileChooser.title, fileChooser.filters))
nativeFileChooser (std::make_shared<Win32NativeFileChooser> (this, flags, previewComp, fileChooser.startingFile,
fileChooser.title, fileChooser.filters))
{
auto mainMon = Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
@ -739,19 +757,17 @@ public:
{
exitModalState (0);
nativeFileChooser->cancel();
nativeFileChooser = nullptr;
}
void launch() override
{
SafePointer<Native> safeThis (this);
std::weak_ptr<Native> safeThis = shared_from_this();
enterModalState (true, ModalCallbackFunction::create (
[safeThis] (int)
{
if (safeThis != nullptr)
safeThis->owner.finished (safeThis->nativeFileChooser->results);
}));
enterModalState (true, ModalCallbackFunction::create ([safeThis] (int)
{
if (auto locked = safeThis.lock())
locked->owner.finished (locked->nativeFileChooser->results);
}));
nativeFileChooser->open (true);
}
@ -783,7 +799,7 @@ public:
private:
FileChooser& owner;
Win32NativeFileChooser::Ptr nativeFileChooser;
std::shared_ptr<Win32NativeFileChooser> nativeFileChooser;
};
//==============================================================================
@ -796,10 +812,10 @@ bool FileChooser::isPlatformDialogAvailable()
#endif
}
FileChooser::Pimpl* FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent* preview)
std::unique_ptr<FileChooser::Pimpl> FileChooser::showPlatformDialog (FileChooser& owner, int flags,
FilePreviewComponent* preview)
{
return new FileChooser::Native (owner, flags, preview);
return std::make_unique<FileChooser::Native> (owner, flags, preview);
}
} // namespace juce