diff --git a/libraries/lib-basic-ui/BasicUI.cpp b/libraries/lib-basic-ui/BasicUI.cpp index faa914580..515a39886 100644 --- a/libraries/lib-basic-ui/BasicUI.cpp +++ b/libraries/lib-basic-ui/BasicUI.cpp @@ -8,7 +8,6 @@ Paul Licameli **********************************************************************/ #include "BasicUI.h" -#include "Internat.h" #include #include diff --git a/libraries/lib-basic-ui/BasicUI.h b/libraries/lib-basic-ui/BasicUI.h index a055a4505..6714fc64d 100644 --- a/libraries/lib-basic-ui/BasicUI.h +++ b/libraries/lib-basic-ui/BasicUI.h @@ -13,8 +13,7 @@ Paul Licameli #include #include "Identifier.h" - -class TranslatableString; +#include "Internat.h" namespace BasicUI { @@ -74,6 +73,66 @@ struct ErrorDialogOptions { //! "Message", suitably translated BASIC_UI_API TranslatableString DefaultCaption(); +enum class Icon { + None, + Warning, + Error, + Question, + Information, +}; + +enum class Button { + Default, //!< Like Ok, except maybe minor difference of dialog position + Ok, //!< One button + YesNo //!< Two buttons +}; + +struct MessageBoxOptions { + //! @name Chain-call style initializers + //! @{ + + MessageBoxOptions &&Parent(WindowPlacement *parent_) && + { parent = parent_; return std::move(*this); } + + MessageBoxOptions &&Caption(TranslatableString caption_) && + { caption = std::move(caption_); return std::move(*this); } + + MessageBoxOptions &&IconStyle(Icon style) && + { iconStyle = style; return std::move(*this); } + + MessageBoxOptions &&ButtonStyle(Button style) && + { buttonStyle = style; return std::move(*this); } + + //! Override the usual defaulting to Yes; affects only the YesNo case + MessageBoxOptions &&DefaultIsNo() && + { yesOrOkDefaultButton = false; return std::move(*this); } + + MessageBoxOptions &&CancelButton() && + { cancelButton = true; return std::move(*this); } + + //! Center the dialog on its parent window, if any + MessageBoxOptions &&Centered() && + { centered = true; return std::move(*this); } + + //! @} + + WindowPlacement *parent{ nullptr }; + TranslatableString caption{ DefaultCaption() }; + Icon iconStyle{ Icon::None }; + Button buttonStyle{ Button::Default }; + bool yesOrOkDefaultButton{ true }; + bool cancelButton{ false }; + bool centered{ false }; +}; + +enum class MessageBoxResult : int { + None, //!< May be returned if no Services are installed + Yes, + No, + Ok, + Cancel, +}; + //! @} //! Abstract class defines a few user interface services, not mentioning particular toolkits @@ -90,6 +149,9 @@ public: const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options) = 0; + virtual MessageBoxResult DoMessageBox( + const TranslatableString& message, + MessageBoxOptions options) = 0; }; //! Fetch the global instance, or nullptr if none is yet installed @@ -132,6 +194,19 @@ inline void ShowErrorDialog( p->DoShowErrorDialog(placement, dlogTitle, message, helpPage, options); } +//! Show a modal message box with either Ok or Yes and No, and optionally Cancel +/*! + @return indicates which button was pressed + */ +inline MessageBoxResult ShowMessageBox( const TranslatableString &message, + MessageBoxOptions options = {}) +{ + if (auto p = Get()) + return p->DoMessageBox(message, std::move(options)); + else + return MessageBoxResult::None; +} + //! @} } diff --git a/src/widgets/wxWidgetsBasicUI.cpp b/src/widgets/wxWidgetsBasicUI.cpp index 823dcbcb8..839734c6a 100644 --- a/src/widgets/wxWidgetsBasicUI.cpp +++ b/src/widgets/wxWidgetsBasicUI.cpp @@ -13,6 +13,7 @@ Paul Licameli #ifdef HAS_SENTRY_REPORTING #include "widgets/ErrorReportDialog.h" #endif +#include "widgets/AudacityMessageBox.h" #include using namespace BasicUI; @@ -88,3 +89,72 @@ void wxWidgetsBasicUI::DoShowErrorDialog( pDlog.release(); // not a memory leak, because it has a parent } } + +BasicUI::MessageBoxResult +wxWidgetsBasicUI::DoMessageBox( + const TranslatableString &message, + MessageBoxOptions options) +{ + // Compute the style argument to pass to wxWidgets + long style = 0; + switch (options.iconStyle) { + case Icon::Warning : + style = wxICON_WARNING; + break; + case Icon::Error : + style = wxICON_ERROR; + break; + case Icon::Question : + style = wxICON_QUESTION; + break; + case Icon::Information : + style = wxICON_INFORMATION; + break; + default: + break; + } + switch (options.buttonStyle) { + case Button::Ok : + style |= wxOK; + break; + case Button::YesNo : + style |= wxYES_NO; + break; + default: + break; + } + if (!options.yesOrOkDefaultButton && options.buttonStyle == Button::YesNo) + style |= wxNO_DEFAULT; + if (options.cancelButton) + style |= wxCANCEL; + if (options.centered) + style |= wxCENTER; + + // Preserving the default style AudacityMessageBox had, + // when none of the above were explicitly specified + if (!style) + style = wxOK | wxCENTRE; + + // This calls through to ::wxMessageBox: + auto wxResult = + ::AudacityMessageBox(message, options.caption, style, + options.parent ? GetParent(*options.parent) : nullptr); + // This switch exhausts all possibilities for the return from::wxMessageBox. + // see utilscmn.cpp in wxWidgets. + // Remap to our toolkit-neutral enumeration. + switch (wxResult) { + case wxID_YES: + return MessageBoxResult::Yes; + case wxID_NO: + return MessageBoxResult::No; + case wxID_OK: + return MessageBoxResult::Ok; + case wxID_CANCEL: + return MessageBoxResult::Cancel; + case wxID_HELP: + // should not happen, because we don't ever pass wxHELP + default: + wxASSERT(false); + return MessageBoxResult::None; + } +} diff --git a/src/widgets/wxWidgetsBasicUI.h b/src/widgets/wxWidgetsBasicUI.h index 65c1a03d4..8093d4ec8 100644 --- a/src/widgets/wxWidgetsBasicUI.h +++ b/src/widgets/wxWidgetsBasicUI.h @@ -41,6 +41,9 @@ protected: const TranslatableString &message, const ManualPageID &helpPage, const BasicUI::ErrorDialogOptions &options) override; + BasicUI::MessageBoxResult DoMessageBox( + const TranslatableString &message, + BasicUI::MessageBoxOptions options) override; }; #endif