| 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 | |
| 22 | namespace C4GUI |
| 23 | { |
| 24 | class DialogWindow; |
| 25 | |
| 26 | // a dialog |
| 27 | class Dialog : public Window |
| 28 | { |
| 29 | private: |
| 30 | enum Fade { eFadeNone = 0, eFadeOut, eFadeIn }; |
| 31 | |
| 32 | C4KeyBinding *pKeyAdvanceControl, *pKeyAdvanceControlB, *pKeyHotkey, *pKeyEnter, *pKeyEscape, *pKeyFocusDefControl; |
| 33 | |
| 34 | protected: |
| 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 | |
| 54 | public: |
| 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 | |
| 78 | private: |
| 79 | bool KeyHotkey(C4KeyCodeEx key); |
| 80 | bool KeyFocusDefault(); |
| 81 | |
| 82 | public: |
| 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) |
| 179 | class FullscreenDialog : public Dialog |
| 180 | { |
| 181 | protected: |
| 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 | |
| 188 | public: |
| 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 | |
| 193 | private: |
| 194 | void UpdateHelpButtonPos(); |
| 195 | |
| 196 | protected: |
| 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 |
| 220 | class MessageDialog : public Dialog |
| 221 | { |
| 222 | private: |
| 223 | bool fHasOK; |
| 224 | bool *piConfigDontShowAgainSetting; |
| 225 | const int32_t zOrdering; |
| 226 | |
| 227 | protected: |
| 228 | Label *lblText; |
| 229 | |
| 230 | public: |
| 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 | |
| 241 | protected: |
| 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 |
| 254 | class ConfirmationDialog : public MessageDialog |
| 255 | { |
| 256 | private: |
| 257 | BaseCallbackHandler *pCB; |
| 258 | |
| 259 | public: |
| 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 | |
| 263 | protected: |
| 264 | virtual void OnClosed(bool fOK) override; // callback when dlg got closed |
| 265 | }; |
| 266 | |
| 267 | // a simple progress dialog |
| 268 | class ProgressDialog : public Dialog |
| 269 | { |
| 270 | protected: |
| 271 | ProgressBar *pBar; // progress bar component |
| 272 | |
| 273 | virtual const char *GetID() override { return "ProgressDialog" ; } |
| 274 | |
| 275 | public: |
| 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 |
| 283 | class InputDialog : public Dialog |
| 284 | { |
| 285 | protected: |
| 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 | |
| 294 | public: |
| 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 |
| 306 | class InfoDialog : public Dialog |
| 307 | { |
| 308 | private: |
| 309 | int32_t iScroll; // current scroll pos; backup for text update |
| 310 | C4Sec1TimerCallback<InfoDialog> *pSec1Timer; // engine timer hook for text update |
| 311 | |
| 312 | protected: |
| 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 | |
| 327 | public: |
| 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 |
| 336 | class TimedDialog : public MessageDialog |
| 337 | { |
| 338 | private: |
| 339 | C4Sec1TimerCallback<TimedDialog> *sec1Timer; |
| 340 | uint32_t time; |
| 341 | |
| 342 | public: |
| 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 | |
| 347 | protected: |
| 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 | |