1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2023, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#pragma once
19
20#include "C4Gui.h"
21
22namespace C4GUI
23{
24class DialogWindow;
25
26// a dialog
27class Dialog : public Window
28{
29private:
30 enum Fade { eFadeNone = 0, eFadeOut, eFadeIn };
31
32 C4KeyBinding *pKeyAdvanceControl, *pKeyAdvanceControlB, *pKeyHotkey, *pKeyEnter, *pKeyEscape, *pKeyFocusDefControl;
33
34protected:
35 WoodenLabel *pTitle; // title bar text
36 CallbackButton<Dialog, C4GUI::IconButton> *pCloseBtn;
37 Control *pActiveCtrl; // control that has focus
38 bool fShow; // if set, the dlg is shown
39 bool fOK; // if set, the user pressed OK
40 int32_t iFade; // dlg fade (percent)
41 Fade eFade; // fading mode
42 bool fDelOnClose; // auto-delete when closing
43 StdStrBuf TitleString;
44 bool fViewportDlg; // set in ctor: if true, dlg is not independent, but drawn ad controlled within viewports
45 DialogWindow *pWindow; // window in console mode
46 CStdGLCtx *pCtx; // rendering context for OpenGL
47 FrameDecoration *pFrameDeco;
48
49 bool CreateConsoleWindow();
50 void DestroyConsoleWindow();
51
52 virtual void UpdateSize() override; // called when own size changed - update assigned pWindow
53
54public:
55 Dialog(int32_t iWdt, int32_t iHgt, const char *szTitle, bool fViewportDlg);
56 virtual ~Dialog();
57
58 virtual void RemoveElement(Element *pChild) override; // clear ptr
59
60 virtual void Draw(C4FacetEx &cgo) override; // render dialog (published)
61 virtual void DrawElement(C4FacetEx &cgo) override; // draw dlg bg
62 virtual bool IsComponentOutsideClientArea() override { return !!pTitle; } // pTitle lies outside client area
63
64 virtual const char *GetID() { return nullptr; }
65
66 // special handling for viewport dialogs
67 virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) override;
68 virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) override;
69
70 virtual bool IsFocused(Control *pCtrl) override { return pCtrl == pActiveCtrl; }
71 void SetFocus(Control *pCtrl, bool fByMouse);
72 Control *GetFocus() { return pActiveCtrl; }
73 virtual Dialog *GetDlg() override { return this; } // this is the dialog
74
75 virtual bool CharIn(const char *c); // input: character key pressed - should return false for none-character-inputs (forward to focused control)
76 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse. forwards to child controls
77
78private:
79 bool KeyHotkey(C4KeyCodeEx key);
80 bool KeyFocusDefault();
81
82public:
83 // default control to be set if unprocessed keyboard input has been detected
84 virtual class Control *GetDefaultControl() { return nullptr; }
85
86 // default dlg actions for enter/escape
87 virtual bool OnEnter() { UserClose(fOK: true); return true; }
88 bool KeyEnter() { return OnEnter(); }
89 virtual bool OnEscape() { UserClose(fOK: false); return true; }
90 bool KeyEscape() { return OnEscape(); }
91
92 void AdvanceFocus(bool fBackwards); // change focus to next component
93 bool KeyAdvanceFocus(bool fBackwards) { AdvanceFocus(fBackwards); return true; }
94
95 virtual int32_t GetMarginTop() override { return (pTitle ? pTitle->GetBounds().Hgt : 0) + (pFrameDeco ? pFrameDeco->iBorderTop : Window::GetMarginTop()); }
96 virtual int32_t GetMarginLeft() override { return pFrameDeco ? pFrameDeco->iBorderLeft : Window::GetMarginLeft(); }
97 virtual int32_t GetMarginRight() override { return pFrameDeco ? pFrameDeco->iBorderRight : Window::GetMarginRight(); }
98 virtual int32_t GetMarginBottom() override { return pFrameDeco ? pFrameDeco->iBorderBottom : Window::GetMarginBottom(); }
99
100 bool IsShown() { return fShow; } // returns whether dlg is on screen (may be invisible)
101 bool IsAborted() { return !fShow && !fOK; } // returns whether dialog has been aborted
102 bool IsActive(bool fForKeyboard); // return whether dlg has mouse control focus
103 bool IsFading() { return eFade != eFadeNone; }
104
105 virtual bool IsFullscreenDialog() { return false; }
106 virtual bool HasBackground() { return false; } // true if dlg draws screen background (fullscreen dialogs only)
107
108 // true for dialogs that should span the whole screen
109 // not just the mouse-viewport
110 virtual bool IsFreePlaceDialog() { return false; }
111
112 // true for dialogs that should be placed at the bottom of the screen (chat)
113 virtual bool IsBottomPlacementDialog() { return false; }
114
115 // true for dialogs that receive full keyboard and mouse input even in shared mode
116 virtual bool IsExclusiveDialog() { return false; }
117
118 // some dialogs, like menus or chat control, don't really need a mouse
119 // so do not enable it for those in shared mode if mouse control is disabled
120 virtual bool IsMouseControlled() { return true; }
121
122 // For dialogs associated to a viewport: Return viewport (for placement)
123 virtual C4Viewport *GetViewport() { return nullptr; }
124 bool IsViewportDialog() { return fViewportDlg; }
125
126 // for custom placement procedures; should call SetPos
127 virtual bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) { return false; }
128
129 // true for dialogs drawn externally
130 virtual bool IsExternalDrawDialog() override { return false; }
131
132 // z-ordering used for dialog placement
133 virtual int32_t GetZOrdering() { return C4GUI_Z_DEFAULT; }
134
135 bool Show(Screen *pOnScreen, bool fCB); // show dialog on screen - default to last created screen
136 void Close(bool fOK); // close dlg
137 bool FadeIn(Screen *pOnScreen); // fade dlg into screen
138 void FadeOut(bool fCloseWithOK); // fade out dlg
139 bool DoModal(); // execute message loop until dlg is closed (or GUI destructed) - returns whether dlg was OK
140 bool Execute(); // execute dialog - does message handling, gfx output and idle proc; return false if dlg got closed or GUI deleted
141 void SetDelOnClose(bool fToVal = true) { fDelOnClose = fToVal; } // dialog will delete itself when closed
142
143 void SetTitle(const char *szToTitle, bool fShowCloseButton = true); // change title text; creates or removes title bar if necessary
144
145 void SetFrameDeco(FrameDecoration *pNewDeco) // change border decoration
146 {
147 if (pFrameDeco) pFrameDeco->Deref();
148 if (pFrameDeco = pNewDeco) pNewDeco->Ref();
149 UpdateOwnPos(); // margin may have changed; might need to reposition stuff
150 }
151
152 void ClearFrameDeco() // clear border decoration; no own pos update!
153 {
154 if (pFrameDeco) pFrameDeco->Deref(); pFrameDeco = nullptr;
155 }
156
157 FrameDecoration *GetFrameDecoration() const { return pFrameDeco; }
158 void SetClientSize(int32_t iToWdt, int32_t iToHgt); // resize dialog so its client area has the specified size
159
160 void OnUserClose(C4GUI::Control *btn) // user presses close btn: Usually close dlg with abort
161 {
162 UserClose(fOK: false);
163 }
164
165 virtual void UserClose(bool fOK) { Close(fOK); }
166 virtual void OnClosed(bool fOK); // callback when dlg got closed
167 virtual void OnShown() {} // callback when shown - should not delete the dialog
168 virtual void OnIdle() {} // idle proc in DoModal
169
170 virtual ContextHandler *GetContextHandler() override // always use own context handler only (no fall-through to screen)
171 {
172 return pContextHandler;
173 }
174
175 friend class Screen;
176};
177
178// a dialog covering the whole screen (using UpperBoard-caption)
179class FullscreenDialog : public Dialog
180{
181protected:
182 Label *pFullscreenTitle, *pSubTitle; // subtitle to be put in upper-right corner
183 int32_t iDlgMarginX, iDlgMarginY; // dialog margin set by screen size
184 IconButton *pBtnHelp;
185
186 virtual const char *GetID() override { return nullptr; } // no ID needed, because it's never created as a window
187
188public:
189 FullscreenDialog(const char *szTitle, const char *szSubtitle);
190
191 void SetTitle(const char *szToTitle); // change title text; creates or removes title bar if necessary
192
193private:
194 void UpdateHelpButtonPos();
195
196protected:
197 virtual void DrawElement(C4FacetEx &cgo) override; // draw dlg bg
198
199 // fullscreen dialogs are not closed on Enter
200 virtual bool OnEnter() override { return false; }
201
202 virtual bool IsComponentOutsideClientArea() override { return true; }
203
204 virtual bool HasUpperBoard() { return false; } // standard fullscreen dialog: UpperBoard no longer present
205
206 virtual bool IsFullscreenDialog() override { return true; }
207 virtual bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) override { return true; } // fullscreen dlg already placed
208
209 virtual int32_t GetMarginTop() override;
210 virtual int32_t GetMarginLeft() override { return iDlgMarginX; }
211 virtual int32_t GetMarginRight() override { return iDlgMarginX; }
212 virtual int32_t GetMarginBottom() override { return iDlgMarginY; }
213 virtual void UpdateOwnPos() override; // called when element bounds were changed externally
214
215 // helper func: draw facet to screen background
216 void DrawBackground(C4FacetEx &cgo, C4Facet &rFromFct);
217};
218
219// a simple message dialog
220class MessageDialog : public Dialog
221{
222private:
223 bool fHasOK;
224 bool *piConfigDontShowAgainSetting;
225 const int32_t zOrdering;
226
227protected:
228 Label *lblText;
229
230public:
231 enum Buttons
232 {
233 btnOK = 1, btnAbort = 2, btnYes = 4, btnNo = 8, btnRetry = 16,
234 btnOKAbort = btnOK | btnAbort, btnYesNo = btnYes | btnNo, btnRetryAbort = btnRetry | btnAbort,
235 };
236
237 enum DlgSize { dsRegular = C4GUI_MessageDlgWdt, dsMedium = C4GUI_MessageDlgWdtMedium, dsSmall = C4GUI_MessageDlgWdtSmall };
238
239 MessageDialog(const char *szMessage, const char *szCaption, uint32_t dwButtons, Icons icoIcon, DlgSize eSize = dsRegular, bool *piConfigDontShowAgainSetting = nullptr, bool fDefaultNo = false, int32_t zOrdering = C4GUI_Z_INPUT);
240
241protected:
242 virtual bool OnEnter() override { if (!fHasOK) return false; Close(fOK: true); return true; }
243
244 void OnDontShowAgainCheck(C4GUI::Element *pCheckBox)
245 {
246 if (piConfigDontShowAgainSetting) *piConfigDontShowAgainSetting = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked();
247 }
248
249 virtual const char *GetID() override { return "MessageDialog"; }
250 virtual int32_t GetZOrdering() override { return zOrdering; }
251};
252
253// a confirmation dialog, which performs a callback after confirmation
254class ConfirmationDialog : public MessageDialog
255{
256private:
257 BaseCallbackHandler *pCB;
258
259public:
260 ConfirmationDialog(const char *szMessage, const char *szCaption, BaseCallbackHandler *pCB, uint32_t dwButtons = MessageDialog::btnOKAbort, bool fSmall = false, Icons icoIcon = Ico_Confirm);
261 ~ConfirmationDialog() { if (pCB) pCB->DeRef(); }
262
263protected:
264 virtual void OnClosed(bool fOK) override; // callback when dlg got closed
265};
266
267// a simple progress dialog
268class ProgressDialog : public Dialog
269{
270protected:
271 ProgressBar *pBar; // progress bar component
272
273 virtual const char *GetID() override { return "ProgressDialog"; }
274
275public:
276 ProgressDialog(const char *szMessage, const char *szCaption, int32_t iMaxProgress, int32_t iInitialProgress, Icons icoIcon);
277
278 void SetProgress(int32_t iToProgress) { pBar->SetProgress(iToProgress); } // change progress
279 virtual bool OnEnter() override { return false; } // don't close on Enter!
280};
281
282// a dialog for a one-line text input; contains OK and cancel button
283class InputDialog : public Dialog
284{
285protected:
286 Edit *pEdit; // edit for text input
287 BaseInputCallback *pCB;
288 C4Rect rcEditBounds;
289 bool fChatLayout;
290 Label *pChatLbl;
291
292 virtual const char *GetID() override { return "InputDialog"; }
293
294public:
295 InputDialog(const char *szMessage, const char *szCaption, Icons icoIcon, BaseInputCallback *pCB, bool fChatLayout = false);
296 ~InputDialog() { delete pCB; }
297
298 virtual void OnClosed(bool fOK) override; // close CB
299 void SetMaxText(int32_t iMaxLen);
300 void SetInputText(const char *szToText);
301 const char *GetInputText();
302 void SetCustomEdit(Edit *pCustomEdit);
303};
304
305// a dialog showing some information text
306class InfoDialog : public Dialog
307{
308private:
309 int32_t iScroll; // current scroll pos; backup for text update
310 C4Sec1TimerCallback<InfoDialog> *pSec1Timer; // engine timer hook for text update
311
312protected:
313 TextWindow *pTextWin;
314
315 void CreateSubComponents(); // ctor func
316
317 // add text to the info window
318 void AddLine(const char *szText);
319
320 void BeginUpdateText(); // backup scrolling and clear text window
321 void EndUpdateText(); // restore scroll pos; set last update time
322
323 virtual void UpdateText() {} // function to be overwritten for timed dlgs: Update window text
324
325 virtual void OnSec1Timer(); // idle proc: update text if necessary
326
327public:
328 InfoDialog(const char *szCaption, int32_t iLineCount);
329 InfoDialog(const char *szCaption, int iLineCount, const StdStrBuf &sText); // init w/o timer
330 ~InfoDialog();
331
332 friend class C4Sec1TimerCallback<InfoDialog>;
333};
334
335// message dialog with a timer
336class TimedDialog : public MessageDialog
337{
338private:
339 C4Sec1TimerCallback<TimedDialog> *sec1Timer;
340 uint32_t time;
341
342public:
343 TimedDialog(uint32_t time, const char *message, const char *caption, uint32_t buttons, Icons icon, DlgSize size = dsRegular, bool *configDontShowAgainSetting = nullptr, bool defaultNo = false, int32_t zOrdering = C4GUI_Z_INPUT);
344 virtual ~TimedDialog();
345 void OnSec1Timer();
346
347protected:
348 virtual const char *GetID() override { return "TimedDialog"; }
349 virtual void UpdateText() {}
350 void SetText(const char *message);
351 uint32_t GetRemainingTime() const { return time; }
352};
353}
354