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

NativeMessageBox: Remove support for legacy message boxes

Now that JUCE supports only Windows 10 onwards, TaskDialogIndirect will
always be available.
This commit is contained in:
reuk 2024-08-15 11:51:57 +01:00
parent a9e04c1a1e
commit 1f024f9c08
No known key found for this signature in database
GPG key ID: FCB43929F012EE5C
2 changed files with 26 additions and 202 deletions

View file

@ -114,6 +114,16 @@
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "dwmapi.lib")
// Link a newer version of the side-by-side comctl32 dll.
// Required to enable the newer native message box and visual styles on vista and above.
#pragma comment(linker, \
"\"/MANIFESTDEPENDENCY:type='Win32' " \
"name='Microsoft.Windows.Common-Controls' " \
"version='6.0.0.0' " \
"processorArchitecture='*' " \
"publicKeyToken='6595b64144ccf1df' " \
"language='*'\"" \
)
#if JUCE_OPENGL
#pragma comment(lib, "OpenGL32.Lib")
#pragma comment(lib, "GlU32.Lib")

View file

@ -35,28 +35,13 @@
namespace juce::detail
{
#if JUCE_MSVC
// required to enable the newer dialog box on vista and above
#pragma comment(linker, \
"\"/MANIFESTDEPENDENCY:type='Win32' " \
"name='Microsoft.Windows.Common-Controls' " \
"version='6.0.0.0' " \
"processorArchitecture='*' " \
"publicKeyToken='6595b64144ccf1df' " \
"language='*'\"" \
)
#endif
template <int... ids>
constexpr int intArray[] { ids... };
std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (const MessageBoxOptions& options)
{
class WindowsMessageBoxBase : public ScopedMessageBoxInterface
class WindowsTaskDialog : public ScopedMessageBoxInterface
{
public:
explicit WindowsMessageBoxBase (Component* comp)
: associatedComponent (comp) {}
explicit WindowsTaskDialog (const MessageBoxOptions& opts)
: associatedComponent (opts.getAssociatedComponent()), options (opts) {}
void runAsync (std::function<void (int)> recipient) override
{
@ -109,158 +94,7 @@ std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (co
'this' is guaranteed to be alive when the returned function is called.
*/
virtual std::function<int()> getShowMessageBoxForParent (HWND parent) = 0;
Component::SafePointer<Component> associatedComponent;
std::atomic<HWND> windowHandle { nullptr };
std::future<void> future;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsMessageBoxBase)
};
class PreVistaMessageBox final : public WindowsMessageBoxBase
{
public:
PreVistaMessageBox (const MessageBoxOptions& opts, UINT extraFlags)
: WindowsMessageBoxBase (opts.getAssociatedComponent()),
flags (extraFlags | getMessageBoxFlags (opts.getIconType())),
title (opts.getTitle()), message (opts.getMessage()) {}
private:
std::function<int()> getShowMessageBoxForParent (const HWND parent) override
{
JUCE_ASSERT_MESSAGE_THREAD
static std::map<DWORD, PreVistaMessageBox*> map;
static std::mutex mapMutex;
return [this, parent]
{
const auto threadId = GetCurrentThreadId();
{
const std::scoped_lock scope { mapMutex };
map.emplace (threadId, this);
}
const ScopeGuard eraseFromMap { [threadId]
{
const std::scoped_lock scope { mapMutex };
map.erase (threadId);
} };
const auto hookCallback = [] (int nCode, const WPARAM wParam, const LPARAM lParam)
{
auto* params = reinterpret_cast<CWPSTRUCT*> (lParam);
if (nCode >= 0
&& params != nullptr
&& (params->message == WM_INITDIALOG || params->message == WM_DESTROY))
{
const auto callbackThreadId = GetCurrentThreadId();
const std::scoped_lock scope { mapMutex };
if (const auto iter = map.find (callbackThreadId); iter != map.cend())
iter->second->setDialogWindowHandle (params->message == WM_INITDIALOG ? params->hwnd : nullptr);
}
return CallNextHookEx ({}, nCode, wParam, lParam);
};
const auto hook = SetWindowsHookEx (WH_CALLWNDPROC,
hookCallback,
(HINSTANCE) juce::Process::getCurrentModuleInstanceHandle(),
threadId);
const ScopeGuard removeHook { [hook] { UnhookWindowsHookEx (hook); } };
const auto result = MessageBox (parent, message.toWideCharPointer(), title.toWideCharPointer(), flags);
const auto buttons = [&]() -> Span<const int>
{
switch (flags & 0xf)
{
case MB_OK: return intArray<IDOK>;
case MB_OKCANCEL: return intArray<IDOK, IDCANCEL>;
case MB_ABORTRETRYIGNORE: return intArray<IDABORT, IDRETRY, IDIGNORE>;
case MB_YESNOCANCEL: return intArray<IDYES, IDNO, IDCANCEL>;
case MB_YESNO: return intArray<IDYES, IDNO>;
case MB_RETRYCANCEL: return intArray<IDRETRY, IDCANCEL>;
case MB_CANCELTRYCONTINUE: return intArray<IDCANCEL, IDTRYAGAIN, IDCONTINUE>;
}
return {};
}();
return (int) std::distance (buttons.begin(), std::find (buttons.begin(), buttons.end(), result));
};
}
static UINT getMessageBoxFlags (MessageBoxIconType iconType) noexcept
{
// this window can get lost behind JUCE windows which are set to be alwaysOnTop
// so if there are any set it to be topmost
const auto topmostFlag = WindowUtils::areThereAnyAlwaysOnTopWindows() ? MB_TOPMOST : 0;
const auto iconFlags = [&]() -> decltype (topmostFlag)
{
switch (iconType)
{
case MessageBoxIconType::QuestionIcon: return MB_ICONQUESTION;
case MessageBoxIconType::WarningIcon: return MB_ICONWARNING;
case MessageBoxIconType::InfoIcon: return MB_ICONINFORMATION;
case MessageBoxIconType::NoIcon: break;
}
return 0;
}();
return static_cast<UINT> (MB_TASKMODAL | MB_SETFOREGROUND | topmostFlag | iconFlags);
}
const UINT flags;
const String title, message;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PreVistaMessageBox)
};
class WindowsTaskDialog final : public WindowsMessageBoxBase
{
static auto getTaskDialogFunc()
{
using TaskDialogIndirectFunc = HRESULT (WINAPI*) (const TASKDIALOGCONFIG*, INT*, INT*, BOOL*);
static const auto result = [&]() -> TaskDialogIndirectFunc
{
const auto comctl = "Comctl32.dll";
LoadLibraryA (comctl);
const auto comctlModule = GetModuleHandleA (comctl);
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type")
if (comctlModule != nullptr)
return (TaskDialogIndirectFunc) GetProcAddress (comctlModule, "TaskDialogIndirect");
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
return nullptr;
}();
return result;
}
public:
explicit WindowsTaskDialog (const MessageBoxOptions& opts)
: WindowsMessageBoxBase (opts.getAssociatedComponent()),
iconType (opts.getIconType()),
title (opts.getTitle()), message (opts.getMessage()),
buttons { opts.getButtonText (0), opts.getButtonText (1), opts.getButtonText (2) } {}
static bool isAvailable()
{
return getTaskDialogFunc() != nullptr;
}
private:
std::function<int()> getShowMessageBoxForParent (const HWND parent) override
std::function<int()> getShowMessageBoxForParent (const HWND parent)
{
JUCE_ASSERT_MESSAGE_THREAD
@ -270,8 +104,8 @@ std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (co
config.cbSize = sizeof (config);
config.hwndParent = parent;
config.pszWindowTitle = title.toWideCharPointer();
config.pszContent = message.toWideCharPointer();
config.pszWindowTitle = options.getTitle().toWideCharPointer();
config.pszContent = options.getMessage().toWideCharPointer();
config.hInstance = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
config.lpCallbackData = reinterpret_cast<LONG_PTR> (this);
config.pfCallback = [] (HWND hwnd, UINT msg, WPARAM, LPARAM, LONG_PTR lpRefData)
@ -294,7 +128,7 @@ std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (co
return S_OK;
};
if (iconType == MessageBoxIconType::QuestionIcon)
if (options.getIconType() == MessageBoxIconType::QuestionIcon)
{
if (auto* questionIcon = LoadIcon (nullptr, IDI_QUESTION))
{
@ -306,7 +140,7 @@ std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (co
{
config.pszMainIcon = [&]() -> LPWSTR
{
switch (iconType)
switch (options.getIconType())
{
case MessageBoxIconType::WarningIcon: return TD_WARNING_ICON;
case MessageBoxIconType::InfoIcon: return TD_INFORMATION_ICON;
@ -322,49 +156,29 @@ std::unique_ptr<ScopedMessageBoxInterface> ScopedMessageBoxInterface::create (co
std::vector<TASKDIALOG_BUTTON> buttonLabels;
for (const auto& buttonText : buttons)
if (buttonText.isNotEmpty())
for (auto i = 0; i < options.getNumButtons(); ++i)
if (const auto buttonText = options.getButtonText (i); buttonText.isNotEmpty())
buttonLabels.push_back ({ (int) buttonLabels.size(), buttonText.toWideCharPointer() });
config.pButtons = buttonLabels.data();
config.cButtons = (UINT) buttonLabels.size();
int buttonIndex = 0;
if (auto* func = getTaskDialogFunc())
func (&config, &buttonIndex, nullptr, nullptr);
else
jassertfalse;
TaskDialogIndirect (&config, &buttonIndex, nullptr, nullptr);
return buttonIndex;
};
}
const MessageBoxIconType iconType;
const String title, message;
const std::array<String, 3> buttons;
Component::SafePointer<Component> associatedComponent;
std::atomic<HWND> windowHandle { nullptr };
std::future<void> future;
MessageBoxOptions options;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTaskDialog)
};
if (WindowsTaskDialog::isAvailable())
return std::make_unique<WindowsTaskDialog> (options);
const auto extraFlags = [&options]
{
const auto numButtons = options.getNumButtons();
if (numButtons == 3)
return MB_YESNOCANCEL;
if (numButtons == 2)
return options.getButtonText (0) == "OK" ? MB_OKCANCEL
: MB_YESNO;
return MB_OK;
}();
return std::make_unique<PreVistaMessageBox> (options, (UINT) extraFlags);
return std::make_unique<WindowsTaskDialog> (options);
}
} // namespace juce::detail