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:
parent
e813531d9b
commit
45409bb4e6
9 changed files with 105 additions and 95 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(); }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
152
modules/juce_gui_basics/native/juce_win32_FileChooser.cpp
Normal file → Executable 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue