1 /*!********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file BasicUI.h
6 @brief Toolkit-neutral facade for basic user interface services
7
8 Paul Licameli
9
10 **********************************************************************/
11 #ifndef __AUDACITY_BASIC_UI__
12 #define __AUDACITY_BASIC_UI__
13
14 #include <functional>
15 #include <memory>
16 #include "Identifier.h"
17 #include "Internat.h"
18
19 namespace BasicUI {
20
21 //! @name Types used in the Services interface
22 //! @{
23
24 using Action = std::function<void()>;
25
26 //! Subclasses may hold information such as a parent window pointer for a dialog.
27 /*! The default-constructed empty value of this base class must be accepted by overrides of methods of
28 Services */
29 class BASIC_UI_API WindowPlacement {
30 public:
31 WindowPlacement() = default;
32
33 //! Don't slice
34 WindowPlacement( const WindowPlacement& ) PROHIBITED;
35 //! Don't slice
36 WindowPlacement &operator=( const WindowPlacement& ) PROHIBITED;
37 virtual ~WindowPlacement();
38 };
39
40 enum class ErrorDialogType {
41 ModelessError,
42 ModalError,
43 ModalErrorReport, /*!< If error reporting is enabled, may give option to
44 send; if not, then like ModalError
45 */
46 };
47
48 //! Options for variations of error dialogs; the default is for modal dialogs
49 struct ErrorDialogOptions {
50
51 ErrorDialogOptions() = default;
52 //! Non-explicit
ErrorDialogOptionsErrorDialogOptions53 ErrorDialogOptions( ErrorDialogType type ) : type{ type } {}
54
55 //! @name Chain-call style initializers
56 //! @{
57
ModalHelpErrorDialogOptions58 ErrorDialogOptions &&ModalHelp( bool modalHelp_ ) &&
59 { modalHelp = modalHelp_; return std::move(*this); }
60
LogErrorDialogOptions61 ErrorDialogOptions &&Log( std::wstring log_ ) &&
62 { log = std::move(log_); return std::move(*this); }
63
64 //! @}
65
66 //! Type of help dialog
67 ErrorDialogType type{ ErrorDialogType::ModalError };
68 //! Whether the secondary help dialog with more information should be modal
69 bool modalHelp{ true };
70 //! Optional extra logging information to be shown
71 std::wstring log;
72 };
73
74 //! "Message", suitably translated
75 BASIC_UI_API TranslatableString DefaultCaption();
76
77 enum class Icon {
78 None,
79 Warning,
80 Error,
81 Question,
82 Information,
83 };
84
85 enum class Button {
86 Default, //!< Like Ok, except maybe minor difference of dialog position
87 Ok, //!< One button
88 YesNo //!< Two buttons
89 };
90
91 struct MessageBoxOptions {
92 //! @name Chain-call style initializers
93 //! @{
94
ParentMessageBoxOptions95 MessageBoxOptions &&Parent(WindowPlacement *parent_) &&
96 { parent = parent_; return std::move(*this); }
97
CaptionMessageBoxOptions98 MessageBoxOptions &&Caption(TranslatableString caption_) &&
99 { caption = std::move(caption_); return std::move(*this); }
100
IconStyleMessageBoxOptions101 MessageBoxOptions &&IconStyle(Icon style) &&
102 { iconStyle = style; return std::move(*this); }
103
ButtonStyleMessageBoxOptions104 MessageBoxOptions &&ButtonStyle(Button style) &&
105 { buttonStyle = style; return std::move(*this); }
106
107 //! Override the usual defaulting to Yes; affects only the YesNo case
DefaultIsNoMessageBoxOptions108 MessageBoxOptions &&DefaultIsNo() &&
109 { yesOrOkDefaultButton = false; return std::move(*this); }
110
CancelButtonMessageBoxOptions111 MessageBoxOptions &&CancelButton() &&
112 { cancelButton = true; return std::move(*this); }
113
114 //! Center the dialog on its parent window, if any
CenteredMessageBoxOptions115 MessageBoxOptions &&Centered() &&
116 { centered = true; return std::move(*this); }
117
118 //! @}
119
120 WindowPlacement *parent{ nullptr };
121 TranslatableString caption{ DefaultCaption() };
122 Icon iconStyle{ Icon::None };
123 Button buttonStyle{ Button::Default };
124 bool yesOrOkDefaultButton{ true };
125 bool cancelButton{ false };
126 bool centered{ false };
127 };
128
129 enum class MessageBoxResult : int {
130 None, //!< May be returned if no Services are installed
131 Yes,
132 No,
133 Ok,
134 Cancel,
135 };
136
137 enum ProgressDialogOptions : unsigned {
138 ProgressShowStop = (1 << 0),
139 ProgressShowCancel = (1 << 1),
140 ProgressHideTime = (1 << 2),
141 ProgressConfirmStopOrCancel = (1 << 3),
142 };
143
144 enum class ProgressResult : unsigned
145 {
146 Cancelled = 0, //<! User says that whatever is happening is undesirable and shouldn't have happened at all
147 Success, //<! User says nothing, everything works fine, continue doing whatever we're doing
148 Failed, //<! Something has gone wrong, we should stop and cancel everything we did
149 Stopped //<! Nothing is wrong, but user says we should stop now and leave things as they are now
150 };
151
152 //! Abstraction of a progress dialog with well defined time-to-completion estimate
153 class BASIC_UI_API ProgressDialog
154 {
155 public:
156 virtual ~ProgressDialog();
157
158 //! Update the bar and poll for clicks. Call only on the main thread.
159 virtual ProgressResult Poll(
160 unsigned long long numerator,
161 unsigned long long denominator,
162 const TranslatableString &message = {}) = 0;
163 };
164
165 //! Abstraction of a progress dialog with undefined time-to-completion estimate
166 class BASIC_UI_API GenericProgressDialog
167 {
168 public:
169 virtual ~GenericProgressDialog();
170 //! Give some visual indication of progress. Call only on the main thread.
171 virtual void Pulse() = 0;
172 };
173
174 //! @}
175
176 //! Abstract class defines a few user interface services, not mentioning particular toolkits
177 /*! The intention is that the application supplies a concrete implementation at
178 startup. Most code will not use this class directly, but call the inline
179 functions that follow. */
180 class BASIC_UI_API Services {
181 public:
182 virtual ~Services();
183 virtual void DoCallAfter(const Action &action) = 0;
184 virtual void DoYield() = 0;
185 virtual void DoShowErrorDialog(const WindowPlacement &placement,
186 const TranslatableString &dlogTitle,
187 const TranslatableString &message,
188 const ManualPageID &helpPage,
189 const ErrorDialogOptions &options) = 0;
190 virtual MessageBoxResult DoMessageBox(
191 const TranslatableString& message,
192 MessageBoxOptions options) = 0;
193 virtual std::unique_ptr<ProgressDialog>
194 DoMakeProgress(const TranslatableString &title,
195 const TranslatableString &message,
196 unsigned flag,
197 const TranslatableString &remainingLabelText) = 0;
198 virtual std::unique_ptr<GenericProgressDialog>
199 DoMakeGenericProgress(const WindowPlacement &placement,
200 const TranslatableString &title,
201 const TranslatableString &message) = 0;
202 };
203
204 //! Fetch the global instance, or nullptr if none is yet installed
205 BASIC_UI_API Services *Get();
206
207 //! Install an implementation; return the previously installed instance
208 BASIC_UI_API Services *Install(Services *pInstance);
209
210 /*! @name Functions that invoke global Services
211 These dispatch to the global Services, if supplied. If none was supplied,
212 they are mostly no-ops, with exceptions as noted. All should be called on
213 the main thread only, except as noted.
214 */
215 //! @{
216
217 //! Schedule an action to be done later, and in the main thread
218 /*! This function may be called in other threads than the main. If no Services
219 are yet installed, the action is not lost, but may be dispatched by Yield().
220 The action may itself invoke CallAfter to enqueue other actions.
221 */
222 BASIC_UI_API void CallAfter(Action action);
223
224 //! Dispatch waiting events, including actions enqueued by CallAfter
225 /*! This function must be called by the main thread. Actions enqueued by
226 CallAfter before Services were installed will be dispatched in the sequence
227 they were enqueued, unless an exception thrown by one of them stops the
228 dispatching.
229 */
230 BASIC_UI_API void Yield();
231
232 //! Show an error dialog with a link to the manual for further help
233 inline void ShowErrorDialog(
234 const WindowPlacement &placement, //!< how to parent the dialog
235 const TranslatableString &dlogTitle, //!< Text for title bar
236 const TranslatableString &message, //!< The main message text
237 const ManualPageID &helpPage, //!< Identifies manual page (and maybe an anchor)
238 const ErrorDialogOptions &options = {})
239 {
240 if (auto p = Get())
241 p->DoShowErrorDialog(placement, dlogTitle, message, helpPage, options);
242 }
243
244 //! Show a modal message box with either Ok or Yes and No, and optionally Cancel
245 /*!
246 @return indicates which button was pressed
247 */
248 inline MessageBoxResult ShowMessageBox( const TranslatableString &message,
249 MessageBoxOptions options = {})
250 {
251 if (auto p = Get())
252 return p->DoMessageBox(message, std::move(options));
253 else
254 return MessageBoxResult::None;
255 }
256
257 //! Create and display a progress dialog
258 /*!
259 @param flags bitwise OR of values in ProgressDialogOptions
260 @param remainingLabelText if not empty substitutes for "Remaining Time:"
261 @return nullptr if Services not installed
262 */
263 inline std::unique_ptr<ProgressDialog> MakeProgress(
264 const TranslatableString & title,
265 const TranslatableString & message,
266 unsigned flags = (ProgressShowStop | ProgressShowCancel),
267 const TranslatableString & remainingLabelText = {})
268 {
269 if (auto p = Get())
270 return p->DoMakeProgress(title, message, flags, remainingLabelText);
271 else
272 return nullptr;
273 }
274
275 //! Create and display a progress dialog (return nullptr if Services not installed)
276 /*!
277 This function makes a "generic" progress dialog, for the case when time
278 to completion cannot be estimated, but some indication of progress is still
279 given
280 */
MakeGenericProgress(const WindowPlacement & placement,const TranslatableString & title,const TranslatableString & message)281 inline std::unique_ptr<GenericProgressDialog> MakeGenericProgress(
282 const WindowPlacement &placement,
283 const TranslatableString &title, const TranslatableString &message)
284 {
285 if (auto p = Get())
286 return p->DoMakeGenericProgress(placement, title, message);
287 else
288 return nullptr;
289 }
290
291 //! @}
292 }
293
294 #endif
295