1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 // generic user interface 17 // defines user controls 18 19 // 2do: 20 // mouse wheel processing 21 // disabled buttons 22 23 #ifndef INC_C4Gui 24 #define INC_C4Gui 25 26 #include "graphics/C4FacetEx.h" 27 #include "graphics/C4FontLoader.h" 28 #include "gui/C4KeyboardInput.h" 29 #include "lib/C4Rect.h" 30 #include "lib/C4LogBuf.h" 31 #include "object/C4Id.h" 32 #include "platform/C4Window.h" 33 #include "platform/StdScheduler.h" 34 35 // consts (load those from a def file some time) 36 // font colors - alpha is font alpha, which is inversed opaque 37 #define C4GUI_CaptionFontClr 0xffffffff 38 #define C4GUI_Caption2FontClr 0xffffff00 39 #define C4GUI_InactCaptionFontClr 0xffafafaf 40 #define C4GUI_ButtonFontClr 0xffffff00 41 #define C4GUI_ButtonFontShadowClr 0xff000000 42 #define C4GUI_StatusFontClr 0xffffffff 43 #define C4GUI_MessageFontClr 0xffffffff 44 #define C4GUI_MessageFontAlpha 0xff000000 45 #define C4GUI_InactMessageFontClr 0xffafafaf 46 #define C4GUI_NotifyFontClr 0xffff0000 47 #define C4GUI_ComboFontClr 0xffffffff 48 #define C4GUI_CheckboxFontClr 0xffffffff 49 #define C4GUI_SmallCheckboxFontClr 0xffffffff 50 #define C4GUI_CheckboxDisabledFontClr 0xffafafaf 51 #define C4GUI_LogFontClr 0xffafafaf 52 #define C4GUI_LogFontClr2 0xffff1f1f 53 #define C4GUI_ErrorFontClr 0xffff1f1f 54 #define C4GUI_ProgressBarFontClr 0xffffffff 55 #define C4GUI_ContextFontClr 0xffffffff 56 #define C4GUI_GfxTabCaptActiveClr 0xff000000 57 #define C4GUI_GfxTabCaptInactiveClr 0xff000000 58 59 // other colors 60 #define C4GUI_ImportantBGColor 0x2f00007f 61 #define C4GUI_ProgressBarColor 0x4fffffff 62 #define C4GUI_ListBoxSelColor 0x4faf0000 63 #define C4GUI_ListBoxInactSelColor 0x4f7f7f7f 64 #define C4GUI_ContextSelColor 0x4faf0000 65 #define C4GUI_ContextBGColor 0xaf3f1a00 66 #define C4GUI_StandardBGColor 0x9f000000 67 #define C4GUI_ActiveTabBGColor C4GUI_StandardBGColor 68 #define C4GUI_ListBoxBarColor 0x7f772200 69 #define C4GUI_EditBGColor 0x7f000000 70 #define C4GUI_EditFontColor 0xffffffff 71 72 #define C4GUI_ToolTipBGColor 0xFFF1EA78 73 #define C4GUI_ToolTipFrameColor 0x7f000000 74 #define C4GUI_ToolTipColor 0xFF483222 75 76 // winner/loser color marking 77 #define C4GUI_WinningTextColor 0xffffdf00 78 #define C4GUI_WinningBackgroundColor 0xafaf7a00 79 #define C4GUI_LosingTextColor 0xffffffff 80 #define C4GUI_LosingBackgroundColor 0x7fafafaf 81 82 83 // border colors for 3D-frames 84 #define C4GUI_BorderAlpha 0x4f 85 #define C4GUI_BorderColor1 0x772200 86 #define C4GUI_BorderColor2 0x331100 87 #define C4GUI_BorderColor3 0xaa4400 88 #define C4GUI_BorderColorA1 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor1) 89 #define C4GUI_BorderColorA2 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor2) 90 #define C4GUI_BorderColorA3 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor3) 91 92 // GUI icon sizes 93 #define C4GUI_IconWdt 40 94 #define C4GUI_IconHgt 40 95 #define C4GUI_IconExWdt 64 96 #define C4GUI_IconExHgt 64 97 #define C4GUI_ControllerIconWdt 100 98 #define C4GUI_ControllerIconHgt 100 99 100 #define C4GUI_IconLabelSpacing 2 // space between an icon and its text 101 102 // scroll bar size 103 #define C4GUI_ScrollBarWdt 16 104 #define C4GUI_ScrollBarHgt 16 105 #define C4GUI_ScrollArrowHgt 16 106 #define C4GUI_ScrollArrowWdt 16 107 #define C4GUI_ScrollThumbHgt 16 // only for non-dynamic scroll thumbs 108 #define C4GUI_ScrollThumbWdt 16 // only for non-dynamic scroll thumbs 109 110 // button size 111 #define C4GUI_ButtonHgt 32 // height of buttons 112 #define C4GUI_BigButtonHgt 40 // height of bigger buttons (main menu) 113 #define C4GUI_ButtonAreaHgt 40 // height of button areas 114 #define C4GUI_DefButtonWdt 140 // width of default buttons 115 #define C4GUI_DefButton2Wdt 120 // width of default buttons if there are two of them 116 #define C4GUI_DefButton2HSpace 10 // horzontal space between two def dlg buttons 117 118 // default checkbox height 119 #define C4GUI_CheckBoxHgt 32 120 #define C4GUI_CheckBoxLabelSpacing 4 // pixels between checkbox box and label 121 122 // list box item spacing 123 #define C4GUI_DefaultListSpacing 1 // 1 px of free space between two list items 124 #define C4GUI_ListBoxBarIndent 10 125 126 // default dialog box sizes 127 #define C4GUI_MessageDlgWdt 500 // width of message dialog 128 #define C4GUI_MessageDlgWdtMedium 360 // width of message dialog w/o much text 129 #define C4GUI_MessageDlgWdtSmall 300 // width of message dialog w/o much text 130 #define C4GUI_ProgressDlgWdt 500 // width of progress dialog 131 #define C4GUI_InputDlgWdt 300 132 #define C4GUI_DefDlgIndent 10 // indent for default dlg items 133 #define C4GUI_DefDlgSmallIndent 4 // indent for dlg items that are grouped 134 #define C4GUI_MessageDlgVRoom 100 // height added to text height in message dialog 135 #define C4GUI_ProgressDlgVRoom 150 // height added to text height in progress dialog 136 #define C4GUI_InputDlgVRoom 150 137 #define C4GUI_ProgressDlgPBHgt 30 // height of progress bar in progress dlg 138 #define C4GUI_InfoDlgWdt 620 // width of info dialog 139 #define C4GUI_InfoDlgVRoom 100 // height added to text height in info dialog 140 #define C4GUI_MaxToolTipWdt 500 // maximum width for tooltip boxes 141 142 // time for tooltips to appear (msecs) -evaluated while drawing 143 #define C4GUI_ToolTipShowTime 500 // 0.5 seconds 144 145 // time for title bars to start scrolling to make longer text visible -evaluated while drawing 146 #define C4GUI_TitleAutoScrollTime 3000 // 3 seconds 147 148 // time interval for tab caption scrolling 149 #define C4GUI_TabCaptionScrollTime 500 // 0.5 seconds 150 151 // Z-ordering of dialogs 152 #define C4GUI_Z_CHAT +2 // chat input dialog more important than other input dialogs 153 #define C4GUI_Z_INPUT +1 // input dialogs on top of others 154 #define C4GUI_Z_DEFAULT 0 // normal placement on top of viewports 155 #define C4GUI_Z_PLAYERMENU -1 // inside viewport: player menu 156 #define C4GUI_Z_OBJECTMENU -2 // inside viewport: cursor menu 157 158 #define C4GUI_MinWoodBarHgt 23 159 160 #define C4GUI_FullscreenDlg_TitleHeight C4UpperBoardHeight // pixels reserved for top of fullscreen dialog title 161 #define C4GUI_FullscreenCaptionFontClr 0xffffff00 162 163 namespace C4GUI 164 { 165 166 // some class predefs 167 168 // C4Gui.cpp 169 class Element; class Screen; class CMouse; 170 class ComponentAligner; 171 172 // C4GuiLabels.cpp 173 class Label; class WoodenLabel; class MultilineLabel; 174 class HorizontalLine; class ProgressBar; 175 class Picture; class Icon; class PaintBox; 176 class TextWindow; 177 178 // C4GuiContainers.cpp 179 class Container; class Window; class GroupBox; class Control; 180 class ScrollBar; class ScrollWindow; 181 182 // C4GuiButton.cpp 183 class Button; template <class CallbackDlg, class Base> class CallbackButton; 184 class IconButton; 185 class CloseButton; class OKButton; class CancelButton; 186 class CloseIconButton; class OKIconButton; class CancelIconButton; 187 188 // C4GuiEdit.cpp 189 class Edit; 190 191 // C4GuiCheckBox.cpp 192 class CheckBox; 193 194 // C4GuiListBox.cpp 195 class ListBox; 196 197 // C4GuiTabular.cpp 198 class Tabular; 199 200 // C4GuiMenu.cpp 201 class ContextMenu; 202 class ContextButton; 203 204 // C4GUIComboBox.cpp 205 class ComboBox; 206 207 // C4GuiDialog.cpp 208 class Dialog; class MessageDialog; class ProgressDialog; 209 class InputDialog; class InfoDialog; 210 211 // inline 212 class MenuHandler; class ContextHandler; 213 214 215 // expand text like "Te&xt" to "Te<c ffff00>x</c>t". Color yellow for normal hotkey and red for tooltip. 216 bool ExpandHotkeyMarkup(StdStrBuf &sText, uint32_t &rcHotkey, bool for_tooltip = false); 217 218 // make color readable on black: max alpha to 0x1f, max color hues 219 DWORD MakeColorReadableOnBlack(DWORD &rdwClr); 220 221 // menu handler: generic context menu callback 222 class MenuHandler 223 { 224 public: 225 MenuHandler() = default; //ctor 226 virtual ~MenuHandler() = default; // dtor 227 228 virtual void OnOK(Element *pTarget) = 0; // menu item selected 229 }; 230 231 // context handler: opens context menu on right-click or menu key 232 class ContextHandler 233 { 234 private: 235 int32_t iRefs{0}; 236 public: 237 ContextHandler() = default; //ctor 238 virtual ~ContextHandler() = default; // dtor 239 240 virtual bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) = 0; // context queried - ret true if handled 241 virtual ContextMenu *OnSubcontext(Element *pOnElement) = 0; // subcontext queried 242 Ref()243 inline void Ref() { ++iRefs; } DeRef()244 inline void DeRef() { if (!--iRefs) delete this; } 245 }; 246 247 // generic callback handler 248 class BaseCallbackHandler 249 { 250 private: 251 int32_t iRefs{0}; 252 public: 253 BaseCallbackHandler() = default; 254 virtual ~BaseCallbackHandler() = default; 255 Ref()256 inline void Ref() { ++iRefs; } DeRef()257 inline void DeRef() { if (!--iRefs) delete this; } 258 259 virtual void DoCall(class Element *pElement) = 0; 260 }; 261 262 template <class CB> class CallbackHandler : public BaseCallbackHandler 263 { 264 public: 265 typedef void (CB::*Func)(class Element *pElement); 266 267 private: 268 CB *pCBClass; 269 Func CBFunc; 270 271 public: DoCall(class Element * pElement)272 void DoCall(class Element *pElement) override 273 { 274 ((pCBClass)->*CBFunc)(pElement); 275 } 276 CallbackHandler(CB * pTarget,Func rFunc)277 CallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} 278 }; 279 280 template <class CB> class CallbackHandlerNoPar : public BaseCallbackHandler 281 { 282 public: 283 typedef void (CB::*Func)(); 284 285 private: 286 CB *pCBClass; 287 Func CBFunc; 288 289 public: DoCall(class Element * pElement)290 void DoCall(class Element *pElement) override 291 { 292 ((pCBClass)->*CBFunc)(); 293 } 294 CallbackHandlerNoPar(CB * pTarget,Func rFunc)295 CallbackHandlerNoPar(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} 296 }; 297 298 template <class CB, class ParType> class CallbackHandlerExPar : public BaseCallbackHandler 299 { 300 public: 301 typedef void (CB::*Func)(ParType); 302 303 private: 304 CB *pCBClass; 305 ParType par; 306 Func CBFunc; 307 308 public: DoCall(class Element * pElement)309 void DoCall(class Element *pElement) override 310 { 311 ((pCBClass)->*CBFunc)(par); 312 } 313 CallbackHandlerExPar(CB * pTarget,Func rFunc,ParType par)314 CallbackHandlerExPar(CB *pTarget, Func rFunc, ParType par) : pCBClass(pTarget), par(par), CBFunc(rFunc) {} 315 }; 316 317 // callback with parameter coming from calling class 318 template <class ParType> class BaseParCallbackHandler : public BaseCallbackHandler 319 { 320 protected: DoCall(class Element * pElement)321 void DoCall(class Element *pElement) override {assert(false);} // no-par: Not to be called 322 public: 323 BaseParCallbackHandler() = default; 324 325 virtual void DoCall(ParType par) = 0; 326 }; 327 328 template <class CB, class ParType> class ParCallbackHandler : public BaseParCallbackHandler<ParType> 329 { 330 public: 331 typedef void (CB::*Func)(ParType par); 332 333 private: 334 CB *pCBClass; 335 Func CBFunc; 336 337 338 protected: 339 // not to be called, but avoid warning for hiding base class functions 340 using BaseParCallbackHandler<ParType>::DoCall; 341 342 public: DoCall(ParType par)343 void DoCall(ParType par) override { ((pCBClass)->*CBFunc)(par); } 344 ParCallbackHandler(CB * pTarget,Func rFunc)345 ParCallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} 346 }; 347 348 // three facets for left/top, middle and right/bottom of an auto-sized bar 349 struct DynBarFacet 350 { 351 C4Facet fctBegin, fctMiddle, fctEnd; 352 353 void SetHorizontal(C4Surface &rBySfc, int iHeight=0, int iBorderWidth=0); 354 void SetHorizontal(C4Facet &rByFct, int32_t iBorderWidth=0); ClearDynBarFacet355 void Clear() { fctBegin.Default(); fctMiddle.Default(); fctEnd.Default(); } 356 }; 357 358 // facets used to draw a scroll bar 359 struct ScrollBarFacets 360 { 361 DynBarFacet barScroll; 362 C4Facet fctScrollDTop, fctScrollPin, fctScrollDBottom; 363 364 void Set(const C4Facet &rByFct, int32_t iPinIndex=0); ClearScrollBarFacets365 void Clear() { barScroll.Clear(); fctScrollDTop.Default(); fctScrollPin.Default(); fctScrollDBottom.Default(); } 366 }; 367 368 // a generic gui-element 369 class Element 370 { 371 private: 372 StdStrBuf ToolTip; // MouseOver - status text 373 bool is_immediate_tooltip{false}; 374 375 protected: 376 Container *pParent{nullptr}; // owning container 377 Element *pPrev, *pNext; // previous and next element of same container 378 Window *pDragTarget{nullptr}; // target that is dragged around when the user drags this element 379 int32_t iDragX, iDragY; // drag start pos 380 bool fDragging{false}; // if set, mouse is down on component and dragging enabled 381 ContextHandler *pContextHandler{nullptr}; // context handler to be called upon context menu request 382 public: 383 bool fVisible{true}; // if false, component (and subcomponents) are not drawn 384 protected: 385 C4Rect rcBounds; // element bounds 386 Draw(C4TargetFacet & cgo)387 virtual void Draw(C4TargetFacet &cgo) { DrawElement(cgo); } // draw this class (this + any contents) DrawElement(C4TargetFacet & cgo)388 virtual void DrawElement(C4TargetFacet &cgo) { }; // draw element itself 389 390 virtual void RemoveElement(Element *pChild); // clear ptrs 391 392 virtual void UpdateSize(); // called when own size changed 393 virtual void UpdatePos(); // called when own position changed 394 395 void Draw3DFrame(C4TargetFacet &cgo, bool fUp=false, int32_t iIndent=1, BYTE byAlpha=C4GUI_BorderAlpha, bool fDrawTop=true, int32_t iTopOff=0, bool fDrawLeft=true, int32_t iLeftOff=0); // draw frame around element 396 void DrawBar(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds 397 void DrawVBar(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds 398 void DrawHBarByVGfx(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw horizontal gfx bar within element bounds, using gfx of vertical one 399 void DrawHVBar(C4TargetFacet &cgo, DynBarFacet &rFacets, C4DrawTransform &trf, int32_t iMiddleLength); 400 IsOwnPtrElement()401 virtual bool IsOwnPtrElement() { return false; } // if true is returned, item will not be deleted when container is cleared IsExternalDrawDialog()402 virtual bool IsExternalDrawDialog() { return false; } IsMenu()403 virtual bool IsMenu() { return false; } GetDialogWindow()404 virtual class DialogWindow* GetDialogWindow() { return nullptr; } // return DialogWindow if this element is a dialog 405 406 // for listbox-selection by character input CheckNameHotkey(const char *)407 virtual bool CheckNameHotkey(const char *) { return false; } 408 409 public: GetContainer()410 virtual Container *GetContainer() { return pParent; } // returns parent for elements; this for containers 411 412 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons MouseEnter(CMouse & rMouse)413 virtual void MouseEnter(CMouse &rMouse) {}; // called when mouse cursor enters element region MouseLeave(CMouse & rMouse)414 virtual void MouseLeave(CMouse &rMouse) {}; // called when mouse cursor leaves element region 415 416 virtual void StartDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by element in MouseInput: start dragging 417 virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: dragging process 418 virtual void StopDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: mouse released after dragging process 419 OnHotkey(uint32_t cHotkey)420 virtual bool OnHotkey(uint32_t cHotkey) { return false; } // return true when hotkey has been processed 421 422 public: 423 bool DoContext(); // open context menu if assigned 424 425 public: 426 Element(); // ctor 427 virtual ~Element(); // dtor 428 GetParent()429 Container *GetParent() { return pParent; } // get owning container 430 virtual class Dialog *GetDlg(); // return contained dialog 431 virtual Screen *GetScreen(); // return contained screen IsFocusElement()432 virtual Control *IsFocusElement() { return nullptr; }; // return control to gain focus in search-cycle 433 UpdateOwnPos()434 virtual void UpdateOwnPos() { }; // called when element bounds were changed externally 435 void ScreenPos2ClientPos(int32_t &riX, int32_t &riY); // transform screen coordinates to element coordinates 436 void ClientPos2ScreenPos(int32_t &riX, int32_t &riY); // transform element coordinates to screen coordinates 437 438 void SetToolTip(const char *szNewTooltip, bool is_immediate = false); // update used tooltip 439 const char *GetToolTip(); // return tooltip const char* (own or fallback to parent) GetOwnToolTip()440 const char *GetOwnToolTip() { return ToolTip.getData(); } // return tooltip const char*, without fallback to parent IsImmediateToolTip()441 bool IsImmediateToolTip() const { return is_immediate_tooltip; } 442 GetWidth()443 int32_t GetWidth() { return rcBounds.Wdt; } GetHeight()444 int32_t GetHeight() { return rcBounds.Hgt; } GetBounds()445 C4Rect &GetBounds() { return rcBounds; } SetBounds(const C4Rect & rcNewBound)446 void SetBounds(const C4Rect &rcNewBound) { rcBounds=rcNewBound; UpdatePos(); UpdateSize(); } GetClientRect()447 virtual C4Rect &GetClientRect() { return rcBounds; } GetContainedClientRect()448 C4Rect GetContainedClientRect() { C4Rect rc=GetClientRect(); rc.x=rc.y=0; return rc; } GetNext()449 Element *GetNext() const { return pNext; } GetPrev()450 Element *GetPrev() const { return pPrev; } GetFirstNestedElement(bool fBackwards)451 virtual Element *GetFirstNestedElement(bool fBackwards) { return this; } GetFirstContained()452 virtual Element *GetFirstContained() { return nullptr; } 453 bool IsInActiveDlg(bool fForKeyboard); IsParentOf(Element * pEl)454 virtual bool IsParentOf(Element *pEl) { return false; } // whether this is the parent container (directly or recursively) of the passed element 455 456 C4Rect GetToprightCornerRect(int32_t iWidth=16, int32_t iHeight=16, int32_t iHIndent=4, int32_t iVIndent=4, int32_t iIndexX=0); // get rectangle to be used for context buttons and stuff 457 458 bool IsVisible(); 459 virtual void SetVisibility(bool fToValue); 460 GetListItemTopSpacing()461 virtual int32_t GetListItemTopSpacing() { return C4GUI_DefaultListSpacing; } GetListItemTopSpacingBar()462 virtual bool GetListItemTopSpacingBar() { return false; } 463 SetDragTarget(Window * pToWindow)464 void SetDragTarget(Window *pToWindow) { pDragTarget = pToWindow; } SetContextHandler(ContextHandler * pNewHd)465 void SetContextHandler(ContextHandler *pNewHd) // takes over ptr 466 { 467 if (pContextHandler) pContextHandler->DeRef(); 468 if ((pContextHandler = pNewHd)) pNewHd->Ref(); 469 } 470 virtual ContextHandler *GetContextHandler(); // get context handler to be used (own or parent) 471 472 friend class Container; friend class TextWindow; friend class ListBox; 473 }; 474 475 // a simple text label on the screen 476 class Label : public Element 477 { 478 protected: 479 StdStrBuf sText; // label text 480 DWORD dwFgClr; // text color 481 int32_t x0, iAlign; // x-textstart-pos; horizontal alignment 482 CStdFont *pFont; 483 uint32_t cHotkey; // hotkey for this label 484 bool fAutosize; 485 bool fMarkup; 486 487 Control *pClickFocusControl; // control that gets focus if the label is clicked or hotkey is pressed 488 489 void DrawElement(C4TargetFacet &cgo) override; // output label 490 void UpdateOwnPos() override; 491 492 bool OnHotkey(uint32_t cHotkey) override; // focus control on correct hotkey 493 GetLeftIndent()494 virtual int32_t GetLeftIndent() { return 0; } 495 496 public: 497 Label(const char *szLblText, int32_t iX0, int32_t iTop, int32_t iAlign=ALeft, DWORD dwFClr=0xffffffff, CStdFont *pFont=nullptr, bool fMakeReadableOnBlack = true, bool fMarkup=true); // ctor 498 Label(const char *szLblText, const C4Rect &rcBounds, int32_t iAlign=ALeft, DWORD dwFClr=0xffffffff, CStdFont *pFont=nullptr, bool fMakeReadableOnBlack = true, bool fAutosize = true, bool fMarkup=true); // ctor 499 500 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse 501 502 void SetText(const char *szToText, bool fAllowHotkey=true); // update text GetText()503 const char *GetText() { return sText.getData(); } // retrieve current text SetClickFocusControl(Control * pToCtrl)504 void SetClickFocusControl(Control *pToCtrl) { pClickFocusControl=pToCtrl; } 505 void SetColor(DWORD dwToClr, bool fMakeReadableOnBlack=true) { dwFgClr = fMakeReadableOnBlack ? MakeColorReadableOnBlack(dwToClr) : dwToClr; } // update label color 506 void SetX0(int32_t iToX0); 507 void SetAutosize(bool fToVal) { fAutosize = fToVal; } 508 }; 509 510 // a label with some wood behind 511 // used for captions 512 class WoodenLabel : public Label 513 { 514 private: 515 uint32_t iAutoScrollDelay; // if set and text is longer than would fit, the label will automatically start moving if not changed and displayed for a while 516 517 // Time when the label text was changed last. nullptr if not initialized; set upon first drawing 518 C4TimeMilliseconds tLastChangeTime; 519 int32_t iScrollPos, iScrollDir; 520 int32_t iRightIndent; 521 protected: 522 void DrawElement(C4TargetFacet &cgo) override; // output label 523 524 C4Facet fctIcon; // icon shown at left-side of label; if set, text is aligned to left 525 526 int32_t GetLeftIndent() override { return fctIcon.Surface ? rcBounds.Hgt : 0; } 527 int32_t GetRightIndent() const { return iRightIndent; } 528 529 public: 530 WoodenLabel(const char *szLblText, const C4Rect &rcBounds, DWORD dwFClr=0xffffffff, CStdFont *pFont=nullptr, int32_t iAlign=ACenter, bool fMarkup=true) // ctor 531 : Label(szLblText, rcBounds, iAlign, dwFClr, pFont, true, true, fMarkup), iAutoScrollDelay(0), tLastChangeTime(C4TimeMilliseconds::Now()), iScrollPos(0), iScrollDir(0), iRightIndent(0) 532 { SetAutosize(false); this->rcBounds=rcBounds; }// ctor - re-sets bounds after SetText 533 534 static int32_t GetDefaultHeight(CStdFont *pUseFont=nullptr); 535 536 void SetIcon(const C4Facet &rfctIcon); 537 void SetAutoScrollTime(uint32_t tDelay) { iAutoScrollDelay=tDelay; ResetAutoScroll(); } 538 void ResetAutoScroll(); 539 540 void SetRightIndent(int32_t iNewIndent) { iRightIndent = iNewIndent; } 541 }; 542 543 // a multiline label with automated text clipping 544 // used for display of log buffers 545 class MultilineLabel : public Element 546 { 547 protected: 548 C4LogBuffer Lines; 549 bool fMarkup; 550 551 protected: 552 void DrawElement(C4TargetFacet &cgo) override; // output label 553 void UpdateHeight(); 554 void UpdateSize() override; // update label height 555 556 public: 557 MultilineLabel(const C4Rect &rcBounds, int32_t iMaxLines, int32_t iMaxBuf, const char *szIndentChars, bool fAutoGrow, bool fMarkup); // ctor 558 559 void AddLine(const char *szLine, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont); // add line of text 560 void Clear(bool fDoUpdate); // clear lines 561 562 friend class TextWindow; 563 }; 564 565 // a bar that show progress 566 class ProgressBar : public Element 567 { 568 protected: 569 int32_t iProgress, iMax; 570 571 void DrawElement(C4TargetFacet &cgo) override; // draw progress bar 572 573 public: 574 ProgressBar(C4Rect &rrcBounds, int32_t iMaxProgress=100) // progress bar ctor 575 : Element(), iProgress(0), iMax(iMaxProgress) 576 { rcBounds=rrcBounds; UpdatePos(); } 577 578 void SetProgress(int32_t iToProgress) { iProgress = iToProgress; } 579 }; 580 581 // Auxiliary design gfx: a horizontal line 582 class HorizontalLine : public Element 583 { 584 protected: 585 uint32_t dwClr, dwShadowClr; 586 587 void DrawElement(C4TargetFacet &cgo) override; // draw horizontal line 588 public: 589 HorizontalLine(C4Rect &rrcBounds, uint32_t dwClr=0x000000, uint32_t dwShadowClr=0xaf7f7f7f) 590 : Element(), dwClr(dwClr), dwShadowClr(dwShadowClr) 591 { SetBounds(rrcBounds); } 592 }; 593 594 // picture displaying a FacetEx 595 class Picture : public Element 596 { 597 protected: 598 C4FacetSurface Facet; // picture facet 599 bool fAspect; // preserve width/height-ratio when drawing 600 bool fCustomDrawClr; // custom drawing color for clrbyowner-surfaces 601 uint32_t dwDrawClr; 602 bool fAnimate; // if true, the picture is animated. Whoaa! 603 int32_t iPhaseTime, iAnimationPhase, iDelay; // current animation phase - undefined if not animated 604 605 void DrawElement(C4TargetFacet &cgo) override; // draw the image 606 607 public: 608 Picture(const C4Rect &rcBounds, bool fAspect); // ctor - does not load image 609 610 const C4FacetSurface &GetFacet() const { return Facet; } // get picture facet 611 C4FacetSurface &GetMFacet() { return Facet; } // get picture facet 612 void SetFacet(const C4Facet &fct) { Facet.Clear(); Facet.Set(fct); } 613 bool EnsureOwnSurface(); // create an own surface, if it's just a link 614 void SetDrawColor(uint32_t dwToClr) { dwDrawClr = dwToClr; fCustomDrawClr = true; } 615 void SetAnimated(bool fEnabled, int iDelay); // starts/stops cycling through all phases of the specified facet 616 }; 617 618 // picture displaying two facets 619 class OverlayPicture : public Picture 620 { 621 protected: 622 int iBorderSize; // border of overlay image if not zoomed 623 C4Facet OverlayImage; // image to be displayed on top of the actual picture 624 625 void DrawElement(C4TargetFacet &cgo) override; // draw the image 626 627 public: 628 OverlayPicture(const C4Rect &rcBounds, bool fAspect, const C4Facet &rOverlayImage, int iBorderSize); // ctor - does not load image 629 }; 630 631 // icon indices 632 enum 633 { 634 Ico_Extended = 0x100, // icon index offset for extended icons 635 Ico_Controller = 0x200, 636 }; 637 enum Icons 638 { 639 Ico_Empty = -2, // for context menus only 640 Ico_None = -1, 641 Ico_Clonk = 0, 642 Ico_Notify = 1, 643 Ico_Wait = 2, 644 Ico_NetWait = 3, 645 Ico_Host = 4, 646 Ico_Client = 5, 647 Ico_UnknownClient = 6, 648 Ico_UnknownPlayer = 7, 649 Ico_ObserverClient = 8, 650 Ico_Player = 9, 651 Ico_Resource = 10, 652 Ico_Error = 11, 653 Ico_SavegamePlayer = 12, 654 Ico_Save = 13, 655 Ico_Active = 14, 656 Ico_Options = 14, 657 Ico_Editor = 14, 658 Ico_Inactive = 15, 659 Ico_Kick = 16, 660 Ico_Loading = 17, 661 Ico_Confirm = 18, 662 Ico_Team = 19, 663 Ico_AddPlr = 20, 664 Ico_Record = 21, 665 Ico_Chart = 21, 666 Ico_Gfx = 22, 667 Ico_Sound = 23, 668 Ico_Keyboard = 24, 669 Ico_Gamepad = 25, 670 Ico_MouseOff = 26, 671 Ico_MouseOn = 27, 672 Ico_Help = 28, 673 Ico_Definition = 29, 674 Ico_GameRunning = 30, 675 Ico_Lobby = 31, 676 Ico_RuntimeJoin = 32, 677 Ico_Exit = 33, 678 Ico_Close = 34, 679 Ico_Rank1 = 35, 680 Ico_Rank2 = 36, 681 Ico_Rank3 = 37, 682 Ico_Rank4 = 38, 683 Ico_Rank5 = 39, 684 Ico_Rank6 = 40, 685 Ico_Rank7 = 41, 686 Ico_Rank8 = 42, 687 Ico_Rank9 = 43, 688 Ico_OfficialServer = 44, 689 Ico_Surrender = 45, 690 Ico_MeleeLeague = 46, 691 Ico_Ready = 47, 692 Ico_Star = 48, 693 Ico_Disconnect = 49, 694 Ico_View = 50, 695 // Ico_RegJoinOnly = 51, 696 Ico_Ignored = 52, 697 698 Ico_Ex_RecordOff = Ico_Extended + 0, 699 Ico_Ex_RecordOn = Ico_Extended + 1, 700 // Ico_Ex_FairCrew = Ico_Extended + 2, 701 Ico_Ex_NormalCrew = Ico_Extended + 3, 702 Ico_Ex_LeagueOff = Ico_Extended + 4, 703 Ico_Ex_LeagueOn = Ico_Extended + 5, 704 Ico_Ex_InternetOff = Ico_Extended + 6, 705 Ico_Ex_InternetOn = Ico_Extended + 7, 706 Ico_Ex_League = Ico_Extended + 8, 707 // Ico_Ex_FairCrewGray = Ico_Extended + 9, 708 Ico_Ex_NormalCrewGray= Ico_Extended + 10, 709 Ico_Ex_Locked = Ico_Extended + 11, 710 Ico_Ex_Unlocked = Ico_Extended + 12, 711 Ico_Ex_LockedFrontal = Ico_Extended + 13, 712 Ico_Ex_Update = Ico_Extended + 14, 713 Ico_Ex_Chat = Ico_Extended + 15, 714 Ico_Ex_GameList = Ico_Extended + 16, 715 Ico_Ex_Comment = Ico_Extended + 17, 716 717 Ico_Controller_A = Ico_Controller + 0, 718 Ico_Controller_B = Ico_Controller + 3, 719 Ico_Controller_X = Ico_Controller + 17, 720 Ico_Controller_Y = Ico_Controller + 18, 721 Ico_Controller_Back = Ico_Controller + 1, 722 Ico_Controller_Start = Ico_Controller + 16, 723 Ico_Controller_Dpad = Ico_Controller + 6, 724 Ico_Controller_DpadLeft = Ico_Controller + 5, 725 Ico_Controller_DpadRight = Ico_Controller + 7, 726 Ico_Controller_DpadDown = Ico_Controller + 4, 727 Ico_Controller_DpadUp = Ico_Controller + 8, 728 Ico_Controller_LeftShoulder = Ico_Controller + 9, 729 Ico_Controller_RightShoulder = Ico_Controller + 12, 730 Ico_Controller_LeftTrigger = Ico_Controller + 11, 731 Ico_Controller_RightTrigger = Ico_Controller + 14, 732 Ico_Controller_LeftStick = Ico_Controller + 10, 733 Ico_Controller_RightStick = Ico_Controller + 13, 734 }; 735 736 // cute, litte, useless thingy 737 class Icon : public Picture 738 { 739 public: 740 Icon(const C4Rect &rcBounds, Icons icoIconIndex); 741 742 void SetIcon(Icons icoNewIconIndex); 743 static C4Facet GetIconFacet(Icons icoIconIndex); 744 }; 745 746 // a collection of gui-elements 747 class Container : public Element 748 { 749 protected: 750 Element *pFirst, *pLast; // contained elements 751 752 void Draw(C4TargetFacet &cgo) override; // draw all elements 753 virtual void ElementSizeChanged(Element *pOfElement) { } // called when an element size is changed 754 virtual void ElementPosChanged(Element *pOfElement) { } // called when an element position is changed 755 756 virtual void AfterElementRemoval() 757 { if (pParent) pParent->AfterElementRemoval(); } // called by ScrollWindow to parent after an element has been removed 758 759 bool OnHotkey(uint32_t cHotkey) override; // check all contained elements for hotkey 760 761 public: 762 Container(); // ctor 763 ~Container() override; // dtor 764 765 void Clear(); // delete all child elements 766 void ClearChildren(); // delete all child elements 767 void RemoveElement(Element *pChild) override; // remove child element from container 768 void MakeLastElement(Element *pChild); // resort to the end of the list 769 void AddElement(Element *pChild); // add child element to container 770 void ReaddElement(Element *pChild); // resort child element to end of list 771 void InsertElement(Element *pChild, Element *pInsertBefore); // add child element to container, ordered directly before given, other element 772 Element *GetNextNestedElement(Element *pPrevElement, bool fBackwards); // get next element after given, applying recursion 773 Element *GetFirstContained() override { return pFirst; } 774 virtual Element *GetLastContained() { return pLast; } 775 Element *GetFirstNestedElement(bool fBackwards) override; 776 777 class Iterator 778 { 779 private: 780 Element *current; 781 public: 782 Iterator(Element *element = nullptr) : current(element) { } 783 784 Element * operator*() const { return current; } 785 Element * operator->() const { return current; } 786 void operator++() { current = current->GetNext(); }; 787 void operator++(int) { operator++(); } 788 789 bool operator==(const Iterator & other) const 790 { 791 return (current == other.current); 792 } 793 794 bool operator!=(const Iterator & other) const 795 { 796 return !(*this == other); 797 } 798 }; 799 800 class ReverseIterator 801 { 802 private: 803 Element *current; 804 public: 805 ReverseIterator(Element *element = nullptr) : current(element) { } 806 807 Element * operator*() const { return current; } 808 Element * operator->() const { return current; } 809 void operator++() { current = current->GetPrev(); }; 810 void operator++(int) { operator++(); } 811 812 bool operator==(const ReverseIterator & other) const 813 { 814 return (current == other.current); 815 } 816 817 bool operator!=(const ReverseIterator & other) const 818 { 819 return !(*this == other); 820 } 821 }; 822 823 // provide C++-style iterator interface 824 Iterator begin() { return Iterator(pFirst); } 825 Iterator end() { return Iterator(nullptr); } 826 ReverseIterator rbegin() { return ReverseIterator(pLast); } 827 ReverseIterator rend() { return ReverseIterator(nullptr); } 828 829 Element *GetFirst() { return pFirst; } 830 Element *GetLast() { return pLast; } 831 Container *GetContainer() override { return this; } // returns parent for elements; this for containers 832 Element *GetElementByIndex(int32_t i); // get indexed child element 833 int32_t GetElementCount(); 834 835 void SetVisibility(bool fToValue) override; 836 virtual bool IsFocused(Control *pCtrl) { return pParent ? pParent->IsFocused(pCtrl) : false; } 837 virtual bool IsSelectedChild(Element *pChild) { return pParent ? pParent->IsSelectedChild(pChild) : true; } // whether the child element is selected - only false for list-box-containers which can have unselected children 838 bool IsParentOf(Element *pEl) override; // whether this is the parent container (directly or recursively) of the passed element 839 840 virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) {} // apply child drawing offset 841 virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) {} // subtract child drawing offset 842 843 844 friend class Element; friend class ScrollWindow; 845 }; 846 847 // a rectangled control that contains other elements 848 class Window : public Container 849 { 850 protected: 851 C4Rect rcClientRect; // area for contained elements 852 853 void Draw(C4TargetFacet &cgo) override; // draw this window 854 855 public: 856 Window(); // ctor 857 858 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 859 860 void SetPos(int32_t iXPos, int32_t iYPos) 861 { rcBounds.x=iXPos; rcBounds.y=iYPos; UpdatePos(); } 862 863 void UpdateOwnPos() override; // update client rect 864 C4Rect &GetClientRect() override { return rcClientRect; } 865 866 void ApplyElementOffset(int32_t &riX, int32_t &riY) override 867 { riX -= rcClientRect.x; riY -= rcClientRect.y; } 868 void ApplyInvElementOffset(int32_t &riX, int32_t &riY) override 869 { riX += rcClientRect.x; riY += rcClientRect.y; } 870 virtual bool IsComponentOutsideClientArea() { return false; } // if set, drawing routine of subcomponents will clip to Bounds rather than to ClientRect 871 872 // get margins from bounds to client rect 873 virtual int32_t GetMarginTop() { return 0; } 874 virtual int32_t GetMarginLeft() { return 0; } 875 virtual int32_t GetMarginRight() { return 0; } 876 virtual int32_t GetMarginBottom() { return 0; } 877 }; 878 879 // a scroll bar 880 class ScrollBar : public Element 881 { 882 protected: 883 bool fScrolling; // if set, scrolling is currently enabled 884 bool fAutoHide; // if set, bar is made invisible if scrolling is not possible anyway 885 int32_t iScrollThumbSize; // height(/width) of scroll thumb 886 int32_t iScrollPos; // y(/x) offset of scroll thumb 887 bool fTopDown, fBottomDown; // whether scrolling buttons are pressed 888 bool fHorizontal; // if set, the scroll bar is horizontal instead of vertical 889 int32_t iCBMaxRange; // range for callback class 890 891 ScrollWindow *pScrollWindow; // associated scrolled window - may be 0 for callback scrollbars 892 BaseParCallbackHandler<int32_t> *pScrollCallback; // callback called when scroll pos changes 893 894 ScrollBarFacets *pCustomGfx; 895 896 void Update(); // update scroll bar according to window 897 void OnPosChanged(); // update window according to scroll bar, and/or do callbacks 898 899 void DrawElement(C4TargetFacet &cgo) override; // draw scroll bar 900 901 // suppress scrolling pin for very narrow menus 902 bool HasPin() 903 { 904 if (fHorizontal) return rcBounds.Wdt > (2*C4GUI_ScrollArrowWdt + C4GUI_ScrollThumbWdt); 905 else return rcBounds.Hgt > (2*C4GUI_ScrollArrowHgt + C4GUI_ScrollThumbHgt); 906 } 907 int32_t GetMaxScroll() 908 { 909 if (fHorizontal) return HasPin() ? GetBounds().Wdt - 2*C4GUI_ScrollArrowWdt - iScrollThumbSize : 100; 910 else return HasPin() ? GetBounds().Hgt - 2*C4GUI_ScrollArrowHgt - iScrollThumbSize : 100; 911 } 912 int32_t GetScrollByPos(int32_t iX, int32_t iY) 913 { 914 return Clamp<int32_t>((fHorizontal ? iX-C4GUI_ScrollArrowWdt : iY-C4GUI_ScrollArrowHgt)-iScrollThumbSize/2, 0, GetMaxScroll()); 915 } 916 bool IsScrolling() { return fScrolling; } 917 918 public: 919 ScrollBar(C4Rect &rcBounds, ScrollWindow *pWin); // ctor for scroll window 920 ScrollBar(C4Rect &rcBounds, bool fHorizontal, BaseParCallbackHandler<int32_t> *pCB, int32_t iCBMaxRange=256); // ctor for callback 921 ~ScrollBar() override; // dtor 922 923 // mouse handling 924 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 925 void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // dragging: allow dragging of thumb 926 void MouseLeave(CMouse &rMouse) override; // mouse leaves with button down: reset down state of buttons 927 928 // change style 929 void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoHide) 930 { pCustomGfx = pToGfx; this->fAutoHide=fAutoHide; } 931 // change scroll pos in a [0, iCBMaxRange-1] scale 932 void SetScrollPos(int32_t iToPos) { iScrollPos = iToPos * GetMaxScroll() / (iCBMaxRange-1); } 933 934 friend class ScrollWindow; 935 friend class ::C4ScriptGuiWindow; 936 }; 937 938 // a window that can be scrolled 939 class ScrollWindow : public Window 940 { 941 protected: 942 ScrollBar *pScrollBar; // vertical scroll bar associated with the window 943 int32_t iScrollY; // vertical scroll pos 944 int32_t iClientHeight; // client rect height 945 bool fHasBar; 946 int32_t iFrozen; // if >0, no scrolling updates are done (used during window refill) 947 948 // pass element updates through to parent window 949 void ElementSizeChanged(Element *pOfElement) override // called when an element size is changed 950 { 951 Window::ElementSizeChanged(pOfElement); 952 if (pParent) pParent->ElementSizeChanged(pOfElement); 953 } 954 void ElementPosChanged(Element *pOfElement) override // called when an element position is changed 955 { 956 Window::ElementPosChanged(pOfElement); 957 if (pParent) pParent->ElementPosChanged(pOfElement); 958 } 959 960 public: 961 ScrollWindow(Window *pParentWindow); // create scroll window in client area of window 962 ~ScrollWindow() override { if (pScrollBar) pScrollBar->pScrollWindow = nullptr; } 963 964 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; 965 966 bool IsComponentOutsideClientArea() override { return true; } // always clip drawing routine of subcomponents to Bounds 967 void Update(); // update client rect and scroll bar according to window 968 void UpdateOwnPos() override; 969 void Freeze() { ++iFrozen; } 970 void UnFreeze() { if (!--iFrozen) Update(); } 971 bool IsFrozen() const { return !!iFrozen; } 972 973 void SetClientHeight(int32_t iToHgt) // set new client height 974 { iClientHeight=iToHgt; Update(); } 975 976 // change style 977 void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoScroll) 978 { if (pScrollBar) pScrollBar->SetDecoration(pToGfx, fAutoScroll); } 979 980 void SetScroll(int32_t iToScroll); // sets new scrolling; does not update scroll bar 981 void ScrollToBottom(); // set scrolling to bottom range; updates scroll bar 982 void ScrollPages(int iPageCount); // scroll down by multiples of visible height; updates scroll bar 983 void ScrollBy(int iAmount); // scroll down by vertical pixel amount; updates scroll bar 984 void ScrollRangeInView(int32_t iY, int32_t iHgt); // sets scrolling so range is in view; updates scroll bar 985 bool IsRangeInView(int32_t iY, int32_t iHgt); // returns whether scrolling range is in view 986 987 int32_t GetScrollY() { return iScrollY; } 988 989 void SetScrollBarEnabled(bool fToVal, bool noAutomaticPositioning = false); 990 bool IsScrollBarEnabled() { return fHasBar; } 991 992 bool IsScrollingActive() { return fHasBar && pScrollBar && pScrollBar->IsScrolling(); } 993 bool IsScrollingNecessary() { return iClientHeight > rcBounds.Hgt; } 994 995 friend class ScrollBar; 996 }; 997 998 // a collection of components 999 class GroupBox : public Window 1000 { 1001 private: 1002 StdStrBuf sTitle; 1003 CStdFont *pFont; 1004 uint32_t dwFrameClr, dwTitleClr, dwBackClr; 1005 int32_t iMargin; 1006 1007 CStdFont *GetTitleFont() const; 1008 1009 public: 1010 GroupBox(C4Rect &rtBounds) : Window(), pFont(nullptr), dwFrameClr(0u), dwTitleClr(C4GUI_CaptionFontClr), dwBackClr(0xffffffff), iMargin(4) 1011 { 1012 // init client rect 1013 SetBounds(rtBounds); 1014 } // ctor 1015 1016 void SetFont(CStdFont *pToFont) { pFont = pToFont; } 1017 void SetColors(uint32_t dwFrameClr, uint32_t dwTitleClr, uint32_t dwBackClr=0xffffffff) { this->dwFrameClr = dwFrameClr; this->dwTitleClr = dwTitleClr; this->dwBackClr = dwBackClr; } 1018 void SetTitle(const char *szToTitle) { if (szToTitle && *szToTitle) sTitle.Copy(szToTitle); else sTitle.Clear(); UpdateOwnPos(); } 1019 void SetMargin(int32_t iNewMargin) { iMargin = iNewMargin; UpdateOwnPos(); } 1020 1021 bool HasTitle() const { return !!sTitle.getLength(); } 1022 1023 void DrawElement(C4TargetFacet &cgo) override; // draw frame 1024 1025 int32_t GetMarginTop() override { return HasTitle() ? iMargin + GetTitleFont()->GetLineHeight() : iMargin; } 1026 int32_t GetMarginLeft() override { return iMargin; } 1027 int32_t GetMarginRight() override { return iMargin; } 1028 int32_t GetMarginBottom() override { return iMargin; } 1029 }; 1030 1031 // a drawing area 1032 class PaintBox : public Window 1033 { 1034 protected: 1035 C4FacetSurface fctPaint; 1036 1037 void DrawElement(C4TargetFacet &cgo) override; // draw what has been painted 1038 public: 1039 PaintBox(C4Rect &rtBounds, int32_t iSfcWdt=-1, int32_t iSfcHgt=-1); // ctor 1040 ~PaintBox() override; // dtor 1041 1042 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse 1043 }; 1044 1045 // a control that may have focus 1046 class Control : public Window 1047 { 1048 private: 1049 class C4KeyBinding *pKeyContext; 1050 protected: 1051 virtual bool CharIn(const char *) { return false; } // input: character key pressed - should return false for none-character-inputs 1052 1053 void DisableFocus(); // called when control gets disabled: Make sure it loses focus 1054 virtual bool IsFocusOnClick() { return true; } // defaultly, controls get focused on left-down 1055 Control *IsFocusElement() override { return this; }; // this control can gain focus 1056 virtual void OnGetFocus(bool fByMouse) {}; // callback when control gains focus 1057 virtual void OnLooseFocus() {}; // callback when control looses focus 1058 1059 bool KeyContext() { return DoContext(); } 1060 1061 public: 1062 Control(const C4Rect &rtBounds); // ctor 1063 ~Control() override; 1064 1065 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse. left-click sets focus 1066 1067 bool HasFocus() { return pParent && pParent->IsFocused(this); } 1068 bool HasDrawFocus(); 1069 1070 friend class Dialog; friend class ListBox; 1071 }; 1072 1073 // generic callback-functions to the dialog 1074 template <class CallbackDlg> class DlgCallback 1075 { 1076 public: 1077 typedef void (CallbackDlg::*Func)(Control *pFromControl); 1078 typedef bool (CallbackDlg::*BoolFunc)(Control *pFromControl); 1079 typedef bool (CallbackDlg::*Bool2Func)(Control *pFromControl, bool fBool, bool fBool2); 1080 typedef ContextMenu *(CallbackDlg::*ContextFunc)(Element *pFromElement, int32_t iX, int32_t iY); 1081 typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement); 1082 }; 1083 1084 // multi-param callback-functions to the dialog 1085 template <class CallbackDlg, class TEx> class DlgCallbackEx 1086 { 1087 public: 1088 typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement, TEx Extra); 1089 }; 1090 1091 // a button. may be pressed. 1092 class Button : public Control 1093 { 1094 private: 1095 class C4KeyBinding *pKeyButton; 1096 DynBarFacet *pCustomGfx, *pCustomGfxDown; 1097 1098 protected: 1099 StdStrBuf sText; // button label 1100 CStdFont *pCustomFont; // custom font (if assigned) 1101 DWORD dwCustomFontClr; // text font color (valid only if pCustomFont) 1102 bool fDown; // if set, button is currently held down 1103 bool fMouseOver; // if set, the mouse hovers over the button 1104 uint32_t cHotkey; // hotkey for this button 1105 bool fEnabled; 1106 1107 bool IsFocusOnClick() override { return false; } // buttons don't get focus on click (for easier input, e.g. in chatbox) 1108 1109 void DrawElement(C4TargetFacet &cgo) override; // draw dlg bg 1110 1111 virtual void OnPress(); // called when button is pressed 1112 1113 bool KeyButtonDown(); 1114 bool KeyButtonUp(); 1115 void SetDown(); // mark down and play sound 1116 void SetUp(bool fPress); // mark up and play sound 1117 1118 bool OnHotkey(uint32_t cHotkey) override; // press btn on correct hotkey 1119 1120 public: 1121 Button(const char *szBtnText, const C4Rect &rtBounds); // ctor 1122 ~Button() override; // dtor 1123 1124 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 1125 void MouseEnter(CMouse &rMouse) override; // mouse re-enters with button down: set button down 1126 void MouseLeave(CMouse &rMouse) override; // mouse leaves with button down: reset down state 1127 1128 void SetText(const char *szToText); // update button text (and hotkey) 1129 void SetCustomGraphics(DynBarFacet *pCustomGfx, DynBarFacet *pCustomGfxDown) 1130 { this->pCustomGfx = pCustomGfx; this->pCustomGfxDown = pCustomGfxDown; } 1131 void SetEnabled(bool fToVal) { fEnabled=fToVal; if (!fEnabled) fDown=false; } 1132 void SetFont(CStdFont *pFont, DWORD dwCustomFontClr=C4GUI_CaptionFontClr) { this->pCustomFont = pFont; this->dwCustomFontClr=dwCustomFontClr; } 1133 }; 1134 1135 // button using icon image 1136 class IconButton : public Button 1137 { 1138 protected: 1139 C4Facet fctIcon; 1140 uint32_t dwClr; 1141 bool fHasClr; 1142 bool fHighlight; // if true, the button is highlighted permanently 1143 1144 void DrawElement(C4TargetFacet &cgo) override; // draw icon and highlight if necessary 1145 1146 public: 1147 IconButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey='\0', const char *tooltip_text=nullptr); // ctor 1148 void SetIcon(Icons eUseIcon); 1149 void SetFacet(const C4Facet &rCpy, uint32_t dwClr=0u) { fctIcon = rCpy; } 1150 void SetColor(uint32_t dwClr) { fHasClr=true; this->dwClr=dwClr; } 1151 void SetHighlight(bool fToVal) { fHighlight=fToVal; } 1152 }; 1153 1154 // button using arrow image 1155 class ArrowButton : public Button 1156 { 1157 public: 1158 enum ArrowFct { Left=0, Right=1, Down=2 }; 1159 protected: 1160 ArrowFct eDir; 1161 1162 void DrawElement(C4TargetFacet &cgo) override; // draw arrow; highlight and down if necessary 1163 1164 public: 1165 ArrowButton(ArrowFct eDir, const C4Rect &rtBounds, char cHotkey=0); // ctor 1166 1167 static int32_t GetDefaultWidth(); 1168 static int32_t GetDefaultHeight(); 1169 }; 1170 1171 // button using facets for highlight 1172 class FacetButton : public Button 1173 { 1174 protected: 1175 C4Facet fctBase, fctHighlight; 1176 uint32_t dwTextClrInact, dwTextClrAct; // text colors for inactive/active button 1177 FLOAT_RECT rcfDrawBounds; // drawing bounds 1178 1179 // title drawing parameters 1180 int32_t iTxtOffX, iTxtOffY; 1181 uint8_t byTxtAlign; // ALeft, ACenter or ARight 1182 CStdFont *pFont; float fFontZoom; 1183 1184 void DrawElement(C4TargetFacet &cgo) override; // draw base facet or highlight facet if necessary 1185 1186 public: 1187 FacetButton(const C4Facet &rBaseFct, const C4Facet &rHighlightFct, const FLOAT_RECT &rtfBounds, char cHotkey); // ctor 1188 void SetTextColors(uint32_t dwClrInact, uint32_t dwClrAct) 1189 { dwTextClrInact = dwClrInact; dwTextClrAct = dwClrAct; } 1190 void SetTextPos(int32_t iOffX, int32_t iOffY, uint8_t byAlign=ACenter) 1191 { iTxtOffX=iOffX; iTxtOffY=iOffY; byTxtAlign=byAlign; } 1192 void SetTextFont(CStdFont *pFont, float fFontZoom=1.0f) 1193 { this->pFont=pFont; this->fFontZoom=fFontZoom; } 1194 }; 1195 1196 // a button doing some callback... 1197 template <class CallbackDlg, class Base=Button> class CallbackButton : public Base 1198 { 1199 protected: 1200 CallbackDlg *pCB; 1201 1202 typename DlgCallback<CallbackDlg>::Func pCallbackFn; // callback function 1203 void OnPress() override 1204 { 1205 if (pCallbackFn) 1206 { 1207 CallbackDlg *pC=pCB; 1208 if (!pC) if (!(pC=reinterpret_cast<CallbackDlg *>(Base::GetDlg()))) return; 1209 (pC->*pCallbackFn)(this); 1210 } 1211 } 1212 1213 public: 1214 CallbackButton(ArrowButton::ArrowFct eDir, const C4Rect &rtBounds, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB=nullptr) // ctor 1215 : Base(eDir, rtBounds, 0), pCB(pCB), pCallbackFn(pFn) { } 1216 CallbackButton(const char *szBtnText, C4Rect &rtBounds, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB=nullptr) // ctor 1217 : Base(szBtnText, rtBounds), pCB(pCB), pCallbackFn(pFn) { } 1218 CallbackButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB=nullptr) // ctor 1219 : Base(eUseIcon, rtBounds, cHotkey), pCB(pCB), pCallbackFn(pFn) { } 1220 CallbackButton(Icons eUseIcon, const C4Rect &rtBounds, const char *tooltip_text, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB = nullptr) // ctor 1221 : Base(eUseIcon, rtBounds, '\0', tooltip_text), pCB(pCB), pCallbackFn(pFn) { } 1222 CallbackButton(int32_t iID, const C4Rect &rtBounds, char cHotkey, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB=nullptr) // ctor 1223 : Base(iID, rtBounds, cHotkey), pCB(pCB), pCallbackFn(pFn) { } 1224 }; 1225 1226 // a button doing some callback to any class 1227 template <class CallbackDlg, class Base=Button> class CallbackButtonEx : public Base 1228 { 1229 protected: 1230 typedef CallbackDlg * CallbackDlgPointer; 1231 CallbackDlgPointer pCBTarget; // callback target 1232 typename DlgCallback<CallbackDlg>::Func pCallbackFn; // callback function 1233 void OnPress() override 1234 { (pCBTarget->*pCallbackFn)(this); } 1235 1236 public: 1237 CallbackButtonEx(const char *szBtnText, const C4Rect &rtBounds, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn) // ctor 1238 : Base(szBtnText, rtBounds), pCBTarget(pCBTarget), pCallbackFn(pFn) { } 1239 CallbackButtonEx(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn) // ctor 1240 : Base(eUseIcon, rtBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) { } 1241 CallbackButtonEx(const C4Facet &fctBase, const C4Facet &fctHighlight, const FLOAT_RECT &rtfBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn) // ctor 1242 : Base(fctBase, fctHighlight, rtfBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) { } 1243 }; 1244 1245 // an edit control to type text in 1246 class Edit : public Control 1247 { 1248 public: 1249 Edit(const C4Rect &rtBounds, bool fFocusEdit=false); // ctor 1250 ~Edit() override; 1251 1252 enum InputResult // action to be taken when text is confirmed with enter 1253 { 1254 IR_None=0, // do nothing and continue pasting 1255 IR_CloseDlg, // stop any pastes and close parent dialog successfully 1256 IR_CloseEdit, // stop any pastes and remove this control 1257 IR_Abort // do nothing and stop any pastes 1258 }; 1259 1260 private: 1261 enum CursorOperation { COP_BACK, COP_DELETE, COP_LEFT, COP_RIGHT, COP_HOME, COP_END }; 1262 static const char *CursorRepresentation; 1263 1264 bool KeyCursorOp(const C4KeyCodeEx &key, const CursorOperation &op); 1265 bool KeyEnter(); 1266 bool KeyCopy() { Copy(); return true; } 1267 bool KeyPaste() { Paste(); return true; } 1268 bool KeyCut() { Cut(); return true; } 1269 bool KeySelectAll() { SelectAll(); return true; } 1270 1271 class C4KeyBinding *RegisterCursorOp(CursorOperation op, C4KeyCode key, const char *szName, C4CustomKey::Priority eKeyPrio); 1272 1273 class C4KeyBinding *pKeyCursorBack, *pKeyCursorDel, *pKeyCursorLeft, *pKeyCursorRight, *pKeyCursorHome, *pKeyCursorEnd, 1274 *pKeyEnter, *pKeyCopy, *pKeyPaste, *pKeyCut, *pKeySelAll; 1275 1276 protected: 1277 // context callbacks 1278 ContextMenu *OnContext(C4GUI::Element *pListItem, int32_t iX, int32_t iY); 1279 void OnCtxCopy(C4GUI::Element *pThis) { Copy(); }; 1280 void OnCtxPaste(C4GUI::Element *pThis) { Paste(); }; 1281 void OnCtxCut(C4GUI::Element *pThis) { Cut(); }; 1282 void OnCtxClear(C4GUI::Element *pThis) { DeleteSelection(); }; 1283 void OnCtxSelAll(C4GUI::Element *pThis) { SelectAll(); }; 1284 1285 private: 1286 void Deselect(); // clear selection range 1287 void ClearText(); // remove all the text 1288 1289 public: 1290 bool InsertText(const char *szText, bool fUser); // insert text at cursor pos (returns whether all text could be inserted) 1291 void DeleteSelection(); // deletes the selected text. Adjust cursor position if necessary 1292 bool SetText(const char *szText, bool fUser) { ClearText(); return InsertText(szText, fUser); } 1293 void SetPasswordMask(char cNewPasswordMask) { cPasswordMask = cNewPasswordMask; } // mask edit box contents using the given character 1294 1295 private: 1296 int32_t GetCharPos(int32_t iControlXPos); // get character index of pixel position; always resides within current text length 1297 void EnsureBufferSize(int32_t iMinBufferSize); // ensure buffer has desired size 1298 void ScrollCursorInView(); // ensure cursor pos is visible in edit control 1299 bool DoFinishInput(bool fPasting, bool fPastingMore); // do OnFinishInput callback and process result - returns whether pasting operation should be continued 1300 1301 bool Copy(); bool Cut(); bool Paste(); // clipboard operations 1302 1303 protected: 1304 CStdFont *pFont; // font for edit 1305 char *Text; // edit text 1306 uint32_t dwBGClr, dwFontClr, dwBorderColor; // drawing colors for edit box 1307 int32_t iBufferSize; // size of current buffer 1308 int32_t iCursorPos; // cursor position: char, before which the cursor is located 1309 int32_t iSelectionStart, iSelectionEnd; // selection range (start may be larger than end) 1310 int32_t iMaxTextLength; // maximum number of characters to be input here 1311 C4TimeMilliseconds tLastInputTime; // time of last input (for cursor flashing) 1312 int32_t iXScroll; // horizontal scrolling 1313 char cPasswordMask; // character to be used for masking the contents. 0 for none. 1314 1315 bool fLeftBtnDown; // flag whether left mouse button is down or not 1316 1317 bool CharIn(const char * c) override; // input: character key pressed - should return false for none-character-inputs 1318 void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // dragging: allow text selection outside the component 1319 bool IsFocusOnClick() override { return true; } // edit fields do get focus on click 1320 void OnGetFocus(bool fByMouse) override; // edit control gets focus 1321 void OnLooseFocus() override; // edit control looses focus 1322 1323 void DrawElement(C4TargetFacet &cgo) override; // draw edit control 1324 1325 // called when user presses enter in single-line edit control - closes the current dialog 1326 virtual InputResult OnFinishInput(bool fPasting, bool fPastingMore) { return IR_CloseDlg; } 1327 virtual void OnAbortInput() {} 1328 1329 // get margins from bounds to client rect 1330 int32_t GetMarginTop() override { return 2; } 1331 int32_t GetMarginLeft() override { return 4; } 1332 int32_t GetMarginRight() override { return 4; } 1333 int32_t GetMarginBottom() override { return 2; } 1334 1335 public: 1336 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 1337 1338 const char *GetText() { return Text; } 1339 void SelectAll(); // select all the text 1340 1341 static int32_t GetDefaultEditHeight(); 1342 static int32_t GetCustomEditHeight(CStdFont *pUseFont); 1343 1344 bool GetCurrentWord(char *szTargetBuf, int32_t iMaxTargetBufLen); // get word before cursor pos (for nick completion) 1345 1346 // layout 1347 void SetFont(CStdFont *pToFont) { pFont=pToFont; ScrollCursorInView(); } 1348 void SetColors(uint32_t dwNewBGClr, uint32_t dwNewFontClr, uint32_t dwNewBorderColor) 1349 { dwBGClr = dwNewBGClr; dwFontClr = dwNewFontClr; dwBorderColor = dwNewBorderColor; } 1350 1351 void SetMaxText(int32_t iTo) { iMaxTextLength = iTo; } 1352 }; 1353 1354 // an edit doing some callback 1355 template <class CallbackCtrl> class CallbackEdit : public Edit 1356 { 1357 private: 1358 CallbackCtrl *pCBCtrl; 1359 1360 protected: 1361 typedef InputResult (CallbackCtrl::*CBFunc)(Edit *, bool, bool); 1362 typedef void (CallbackCtrl::*CBAbortFunc)(); 1363 CBFunc pCBFunc; CBAbortFunc pCBAbortFunc; 1364 InputResult OnFinishInput(bool fPasting, bool fPastingMore) override 1365 { if (pCBFunc && pCBCtrl) return (pCBCtrl->*pCBFunc)(this, fPasting, fPastingMore); else return IR_CloseDlg; } 1366 void OnAbortInput() override 1367 { if (pCBAbortFunc && pCBCtrl) (pCBCtrl->*pCBAbortFunc)(); } 1368 1369 public: 1370 CallbackEdit(const C4Rect &rtBounds, CallbackCtrl * pCBCtrl, CBFunc pCBFunc, CBAbortFunc pCBAbortFunc=nullptr) // ctor 1371 : Edit(rtBounds), pCBCtrl(pCBCtrl), pCBFunc(pCBFunc), pCBAbortFunc(pCBAbortFunc) { } 1372 }; 1373 1374 // an edit control that renames a label - some less decoration; abort on Escape and focus loss 1375 class RenameEdit : public Edit 1376 { 1377 private: 1378 C4KeyBinding *pKeyAbort; // key bindings 1379 bool fFinishing; // set during deletion process 1380 Label *pForLabel; // label that is being renamed 1381 Control *pPrevFocusCtrl; // previous focus element to be restored after rename 1382 1383 public: 1384 enum RenameResult 1385 { 1386 RR_Invalid=0, // rename not accepted; continue editing 1387 RR_Accepted, // rename accepted; delete control 1388 RR_Deleted // control deleted - leave everything 1389 }; 1390 1391 public: 1392 RenameEdit(Label *pLabel); // ctor - construct for label; add element; set focus 1393 ~RenameEdit() override; 1394 1395 void Abort(); 1396 1397 private: 1398 void FinishRename(); // renaming aborted or finished - remove this element and restore label 1399 1400 protected: 1401 bool KeyAbort() { Abort(); return true; } 1402 InputResult OnFinishInput(bool fPasting, bool fPastingMore) override; // forward last input to OnOKRename 1403 void OnLooseFocus() override; // callback when control looses focus: OK input 1404 1405 virtual void OnCancelRename() {} // renaming was aborted 1406 virtual RenameResult OnOKRename(const char *szNewName) = 0; // rename performed - return whether name was accepted 1407 }; 1408 1409 template <class CallbackDlg, class ParType> class CallbackRenameEdit : public RenameEdit 1410 { 1411 public: 1412 protected: 1413 typedef void (CallbackDlg::*CBCancelFunc)(ParType); 1414 typedef RenameResult (CallbackDlg::*CBOKFunc)(ParType, const char *); 1415 1416 CBCancelFunc pCBCancelFunc; CBOKFunc pCBOKFunc; 1417 CallbackDlg *pDlg; ParType par; 1418 1419 void OnCancelRename() override { if (pDlg && pCBCancelFunc) (pDlg->*pCBCancelFunc)(par); } 1420 RenameResult OnOKRename(const char *szNewName) override { return (pDlg && pCBOKFunc) ? (pDlg->*pCBOKFunc)(par, szNewName) : RR_Accepted; } 1421 1422 public: 1423 CallbackRenameEdit(Label *pForLabel, CallbackDlg *pDlg, const ParType &par, CBOKFunc pCBOKFunc, CBCancelFunc pCBCancelFunc) // ctor 1424 : RenameEdit(pForLabel), pCBCancelFunc(pCBCancelFunc), pCBOKFunc(pCBOKFunc), pDlg(pDlg), par(par) { } 1425 }; 1426 1427 // editbox below descriptive label sharing one window for common tooltip 1428 class LabeledEdit : public C4GUI::Window 1429 { 1430 public: 1431 LabeledEdit(const C4Rect &rcBounds, const char *szName, bool fMultiline, const char *szPrefText=nullptr, CStdFont *pUseFont=nullptr, uint32_t dwTextClr = C4GUI_CaptionFontClr); 1432 private: 1433 C4GUI::Edit *pEdit; 1434 public: 1435 const char *GetText() const { return pEdit->GetText(); } 1436 C4GUI::Edit *GetEdit() const { return pEdit; } 1437 static bool GetControlSize(int *piWdt, int *piHgt, const char *szForText, CStdFont *pForFont, bool fMultiline); 1438 }; 1439 1440 // checkbox with a text label right of it 1441 class CheckBox : public Control 1442 { 1443 private: 1444 bool fChecked; 1445 class C4KeyBinding *pKeyCheck; 1446 StdStrBuf sCaption; 1447 bool fMouseOn; 1448 BaseCallbackHandler *pCBHandler; // callback handler called if check state changes 1449 bool fEnabled; 1450 CStdFont *pFont; 1451 uint32_t dwEnabledClr, dwDisabledClr; 1452 uint32_t cHotkey; 1453 1454 public: 1455 CheckBox(const C4Rect &rtBounds, const char *szCaption, bool fChecked); // ctor 1456 ~CheckBox() override; 1457 1458 private: 1459 bool KeyCheck() { ToggleCheck(true); return true; } 1460 1461 public: 1462 void ToggleCheck(bool fByUser); // check on/off; do callback 1463 1464 protected: 1465 void UpdateOwnPos() override; 1466 bool IsFocusOnClick() override { return false; } // just check/uncheck on click; do not gain keyboard focus as well 1467 Control *IsFocusElement() override { return fEnabled ? Control::IsFocusElement() : nullptr; }; // this control can gain focus if enabled 1468 void DrawElement(C4TargetFacet &cgo) override; // draw checkbox 1469 bool OnHotkey(uint32_t cHotkey) override; // return true when hotkey has been processed 1470 1471 public: 1472 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 1473 void MouseEnter(CMouse &rMouse) override; 1474 void MouseLeave(CMouse &rMouse) override; 1475 1476 void SetChecked(bool fToVal) { fChecked = fToVal; } // set w/o callback 1477 bool GetChecked() const { return fChecked; } 1478 void SetOnChecked(BaseCallbackHandler *pCB); 1479 void SetEnabled(bool fToVal) { if (!(fEnabled=fToVal)) DisableFocus(); } 1480 1481 const char *GetText() { return sCaption.getData(); } 1482 1483 void SetFont(CStdFont *pFont, uint32_t dwEnabledClr, uint32_t dwDisabledClr) 1484 { this->pFont=pFont; this->dwEnabledClr=dwEnabledClr; this->dwDisabledClr=dwDisabledClr; } 1485 1486 static bool GetStandardCheckBoxSize(int *piWdt, int *piHgt, const char *szForCaptionText, CStdFont *pUseFont); // get needed size to construct a checkbox 1487 }; 1488 1489 // a vertical list of elements 1490 class ListBox : public Control 1491 { 1492 private: 1493 class C4KeyBinding *pKeyContext, *pKeyUp, *pKeyDown, *pKeyPageUp, *pKeyPageDown, *pKeyHome, *pKeyEnd, *pKeyActivate, *pKeyLeft, *pKeyRight; 1494 1495 bool KeyContext(); 1496 bool KeyUp(); 1497 bool KeyDown(); 1498 bool KeyLeft(); 1499 bool KeyRight(); 1500 bool KeyPageUp(); 1501 bool KeyPageDown(); 1502 bool KeyHome(); 1503 bool KeyEnd(); 1504 bool KeyActivate(); 1505 1506 protected: 1507 int32_t iMultiColItemWidth; // if nonzero, the listbox is multicolumn and the column count depends on how many items fit in 1508 int32_t iColCount; // number of columns (usually 1) 1509 ScrollWindow *pClientWindow; // client scrolling window 1510 Element *pSelectedItem; // selected list item 1511 BaseCallbackHandler *pSelectionChangeHandler, *pSelectionDblClickHandler; 1512 bool fDrawBackground; // whether darker background is to be drawn 1513 bool fDrawBorder; // whether 3D frame around box shall be drawn or nay 1514 bool fSelectionDisabled; // if set, no entries can be selected 1515 1516 void DrawElement(C4TargetFacet &cgo) override; // draw listbox 1517 1518 bool IsFocused(Control *pCtrl) override 1519 { 1520 // subcontrol also counts as focused if the list has focus and the subcontrol is selected 1521 return Control::IsFocused(pCtrl) || (HasFocus() && pSelectedItem == pCtrl); 1522 } 1523 bool IsFocusOnClick() override { return true; } // list boxes do get focus on click 1524 Control *IsFocusElement() override { return this; }; // this control can gain focus 1525 void OnGetFocus(bool fByMouse) override; // callback when control gains focus - select a list item if none are selected 1526 bool CharIn(const char * c) override; // character input for direct list element selection 1527 1528 void AfterElementRemoval() override 1529 { Container::AfterElementRemoval(); UpdateElementPositions(); } // called by ScrollWindow to parent after an element has been removed 1530 1531 void UpdateColumnCount(); 1532 1533 public: 1534 ListBox(const C4Rect &rtBounds, int32_t iMultiColItemWidth=0); // ctor 1535 ~ListBox() override; // dtor 1536 1537 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 1538 1539 void RemoveElement(Element *pChild) override; // remove child component 1540 bool AddElement(Element *pChild, int32_t iIndent=0); // add element and adjust its pos 1541 bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0); // insert element and adjust its pos 1542 void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed 1543 void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed 1544 1545 int32_t GetItemWidth() { return iMultiColItemWidth ? iMultiColItemWidth : pClientWindow->GetClientRect().Wdt; } 1546 1547 void SelectionChanged(bool fByUser); // pSelectedItem changed: sound, tooltip, etc. 1548 void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler) // update selection change handler 1549 { 1550 if (pSelectionChangeHandler) pSelectionChangeHandler->DeRef(); 1551 if ((pSelectionChangeHandler = pToHandler)) pToHandler->Ref(); 1552 } 1553 void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler) // update selection doubleclick handler 1554 { 1555 if (pSelectionDblClickHandler) pSelectionDblClickHandler->DeRef(); 1556 if ((pSelectionDblClickHandler = pToHandler)) pSelectionDblClickHandler->Ref(); 1557 } 1558 1559 void ScrollToBottom() // set scrolling to bottom range 1560 { if (pClientWindow) pClientWindow->ScrollToBottom(); } 1561 void ScrollItemInView(Element *pItem); // set scrolling so a specific item is visible 1562 void FreezeScrolling() { pClientWindow->Freeze(); } 1563 void UnFreezeScrolling() { pClientWindow->UnFreeze(); } 1564 1565 // change style 1566 void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false) 1567 { fDrawBackground=fDrawBG; this->fDrawBorder=fDrawBorder; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll); } 1568 void SetSelectionDiabled(bool fToVal=true) { fSelectionDisabled = fToVal; } 1569 1570 // get head and tail list items 1571 Element *GetFirst() { return pClientWindow ? pClientWindow->GetFirst() : nullptr; } 1572 Element *GetLast() { return pClientWindow ? pClientWindow->GetLast() : nullptr; } 1573 1574 // get margins from bounds to client rect 1575 int32_t GetMarginTop() override { return 3; } 1576 int32_t GetMarginLeft() override { return 3; } 1577 int32_t GetMarginRight() override { return 3; } 1578 int32_t GetMarginBottom() override { return 3; } 1579 1580 Element *GetSelectedItem() { return pSelectedItem; } // get focused listbox item 1581 bool IsScrollingActive() { return pClientWindow && pClientWindow->IsScrollingActive(); } 1582 bool IsScrollingNecessary() { return pClientWindow && pClientWindow->IsScrollingNecessary(); } 1583 void SelectEntry(Element *pNewSel, bool fByUser); 1584 void SelectFirstEntry(bool fByUser) { SelectEntry(GetFirst(), fByUser); } 1585 void SelectNone(bool fByUser) { SelectEntry(nullptr, fByUser); } 1586 bool IsMultiColumn() const { return iColCount > 1; } 1587 int32_t ContractToElementHeight(); // make smaller if elements don't use up all of the available height. Return amount by which list box got contracted 1588 1589 void UpdateElementPositions(); // reposition list items so they are stacked vertically 1590 void UpdateElementPosition(Element *pOfElement, int32_t iIndent); // update pos for one specific element 1591 void UpdateSize() override { Control::UpdateSize(); if (pClientWindow) { pClientWindow->UpdateSize(); UpdateElementPositions(); } } 1592 1593 bool IsSelectedChild(Element *pChild) override { return pChild == pSelectedItem || (pSelectedItem && pSelectedItem->IsParentOf(pChild)); } 1594 1595 typedef int32_t (*SortFunction)(const Element *pEl1, const Element *pEl2, void *par); 1596 void SortElements(SortFunction SortFunc, void *par); // sort list items 1597 }; 1598 1599 // tabbing panel 1600 class Tabular : public Control 1601 { 1602 public: 1603 // sheet covering the client area of a tabular 1604 class Sheet : public Window 1605 { 1606 protected: 1607 StdStrBuf sTitle; // sheet label 1608 int32_t icoTitle; // sheet label icon 1609 uint32_t cHotkey; 1610 uint32_t dwCaptionClr; // caption color - default if 0 1611 bool fHasCloseButton, fCloseButtonHighlighted; 1612 bool fTitleMarkup; 1613 1614 Sheet(const char *szTitle, const C4Rect &rcBounds, int32_t icoTitle = Ico_None, bool fHasCloseButton=false, bool fTitleMarkup=true); // ctor; expands hotkey markup in title 1615 1616 void DrawCaption(C4TargetFacet &cgo, int32_t x, int32_t y, int32_t iMaxWdt, bool fLarge, bool fActive, bool fFocus, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont); 1617 void GetCaptionSize(int32_t *piWdt, int32_t *piHgt, bool fLarge, bool fActive, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont); 1618 virtual void OnShown(bool fByUser) { } // calklback from tabular after sheet has been made visible 1619 void SetCloseButtonHighlight(bool fToVal) { fCloseButtonHighlighted = fToVal; } 1620 bool IsPosOnCloseButton(int32_t x, int32_t y, int32_t iCaptWdt, int32_t iCaptHgt, bool fLarge); 1621 bool IsActiveSheet(); 1622 1623 public: 1624 const char *GetTitle() { return sTitle.getData(); } 1625 char GetHotkey() { return cHotkey; } 1626 void SetTitle(const char *szNewTitle); 1627 void SetCaptionColor(uint32_t dwNewClr=0) { dwCaptionClr=dwNewClr; } 1628 virtual void UserClose() { delete this; } // user pressed close button 1629 bool HasCloseButton() const { return fHasCloseButton; } 1630 1631 friend class Tabular; 1632 }; 1633 1634 enum TabPosition 1635 { 1636 tbNone=0, // no tabs 1637 tbTop, // tabs on top 1638 tbLeft // tabs to the left 1639 }; 1640 1641 private: 1642 Sheet *pActiveSheet; // currently selected sheet 1643 TabPosition eTabPos; // whither tabs shalt be shown or nay, en where art thy shown? 1644 int32_t iMaxTabWidth; // maximum tab length; used when tabs are positioned left and do not have gfx 1645 int32_t iSheetSpacing, iSheetOff; // distances of sheet captions 1646 int32_t iCaptionLengthTotal, iCaptionScrollPos; // scrolling in captions (top only) 1647 bool fScrollingLeft, fScrollingRight, fScrollingLeftDown, fScrollingRightDown; // scrolling in captions (top only) 1648 C4TimeMilliseconds tLastScrollTime; // set when fScrollingLeftDown or fScrollingRightDown are true: Time for next scrolling if mouse is held down 1649 int iSheetMargin; 1650 bool fDrawSelf; // if border and bg shall be drawn 1651 1652 C4Facet *pfctBack, *pfctClip, *pfctIcons; // set for tabulars that have custom gfx 1653 CStdFont *pSheetCaptionFont; // font to be used for caption drawing; nullptr if default GUI font is to be used 1654 1655 C4KeyBinding *pKeySelUp, *pKeySelDown, *pKeySelUp2, *pKeySelDown2, *pKeyCloseTab; // key bindings 1656 1657 void SelectionChanged(bool fByUser); // pActiveSheet changed: sound, tooltip, etc. 1658 void SheetsChanged(); // update iMaxTabWidth by new set of sheet labels 1659 void UpdateScrolling(); 1660 void DoCaptionScroll(int32_t iDir); 1661 1662 1663 private: 1664 bool HasGfx() { return pfctBack && pfctClip && pfctIcons; } // whether the control uses custom graphics 1665 1666 protected: 1667 bool KeySelUp(); // keyboard callback: Select previous sheet 1668 bool KeySelDown(); // keyboard callback: Select next sheet 1669 bool KeyCloseTab(); // keyboard callback: Close current sheet if possible 1670 1671 void DrawElement(C4TargetFacet &cgo) override; 1672 void MouseLeaveCaptionArea(); 1673 void MouseLeave(CMouse &rMouse) override; 1674 void OnGetFocus(bool fByMouse) override; 1675 1676 Control *IsFocusElement() override { return eTabPos ? this : nullptr; }; // this control can gain focus only if tabs are enabled only 1677 bool IsFocusOnClick() override { return false; } // but never get focus on single mouse click, because this would de-focus any contained controls! 1678 1679 int32_t GetTopSize() { return (eTabPos == tbTop) ? 20 : 0; } // vertical size of tab selection bar 1680 int32_t GetLeftSize() { return (eTabPos == tbLeft) ? (HasGfx() ? GetLeftClipSize(pfctClip) : 20+iMaxTabWidth) : 0; } // horizontal size of tab selection bar 1681 bool HasLargeCaptions() { return eTabPos == tbLeft; } 1682 1683 int32_t GetMarginTop() override { return iSheetMargin+GetTopSize() + (HasGfx() ? (rcBounds.Hgt-GetTopSize())*30/483 : 0); } 1684 int32_t GetMarginLeft() override { return iSheetMargin+GetLeftSize() + (HasGfx() ? (rcBounds.Wdt-GetLeftSize())*13/628 : 0); } 1685 int32_t GetMarginRight() override { return iSheetMargin + (HasGfx() ? (rcBounds.Wdt-GetLeftSize())*30/628 : 0); } 1686 int32_t GetMarginBottom() override { return iSheetMargin + (HasGfx() ? (rcBounds.Hgt-GetTopSize())*32/483 : 0); } 1687 1688 void UpdateSize() override; 1689 1690 bool IsSelectedChild(Element *pChild) override { return pChild == pActiveSheet || (pActiveSheet && pActiveSheet->IsParentOf(pChild)); } 1691 1692 public: 1693 Tabular(C4Rect &rtBounds, TabPosition eTabPos); // ctor 1694 ~Tabular() override; // dtor 1695 1696 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; 1697 1698 void RemoveElement(Element *pChild) override; // clear ptr 1699 Sheet *AddSheet(const char *szTitle, int32_t icoTitle = Ico_None); 1700 void AddCustomSheet(Sheet *pAddSheet); 1701 void ClearSheets(); // del all sheets 1702 void SelectSheet(int32_t iIndex, bool fByUser); 1703 void SelectSheet(Sheet *pSelSheet, bool fByUser); 1704 1705 1706 Sheet *GetSheet(int32_t iIndex) { return (Sheet *) GetElementByIndex(iIndex); } 1707 Sheet *GetActiveSheet() { return pActiveSheet; } 1708 int32_t GetActiveSheetIndex(); 1709 int32_t GetSheetCount() { return GetElementCount(); } 1710 1711 void SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect); 1712 static int32_t GetLeftClipSize(C4Facet *pfctForClip) { return pfctForClip->Wdt*95/120; } // left clip area size by gfx 1713 void SetSheetMargin(int32_t iMargin) { iSheetMargin = iMargin; UpdateOwnPos(); } 1714 void SetDrawDecoration(bool fToVal) { fDrawSelf = fToVal; } 1715 1716 friend class Sheet; 1717 }; 1718 1719 // scrollable text box 1720 class TextWindow : public Control 1721 { 1722 protected: 1723 ScrollWindow *pClientWindow; // client scrolling window 1724 Picture *pTitlePicture; // [optional]: Picture shown atop the text 1725 MultilineLabel *pLogBuffer; // buffer holding text data 1726 bool fDrawBackground, fDrawFrame; // whether dark background should be drawn (default true) 1727 size_t iPicPadding; 1728 1729 void DrawElement(C4TargetFacet &cgo) override; // draw text window 1730 1731 void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed 1732 void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed 1733 void UpdateSize() override; 1734 1735 Control *IsFocusElement() override { return nullptr; }; // no focus element for now, because there's nothing to do (2do: scroll?) 1736 1737 public: 1738 TextWindow(C4Rect &rtBounds, size_t iPicWdt=0, size_t iPicHgt=0, size_t iPicPadding=0, size_t iMaxLines=100, size_t iMaxTextLen=4096, const char *szIndentChars=" ", bool fAutoGrow=false, const C4Facet *pOverlayPic=nullptr, int iOverlayBorder=0, bool fMarkup=false); // ctor 1739 1740 void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr) // add text in a new line 1741 { if (pLogBuffer) pLogBuffer->AddLine(szText, pFont, dwClr, fDoUpdate, fMakeReadableOnBlack, pCaptionFont); } 1742 void ScrollToBottom() // set scrolling to bottom range 1743 { if (pClientWindow) pClientWindow->ScrollToBottom(); } 1744 void ClearText(bool fDoUpdate) 1745 { if (pLogBuffer) pLogBuffer->Clear(fDoUpdate); } 1746 int32_t GetScrollPos() 1747 { return pClientWindow ? pClientWindow->GetScrollY() : 0; } 1748 void SetScrollPos(int32_t iPos) 1749 { if (pClientWindow) pClientWindow->ScrollRangeInView(iPos, 0); } 1750 void UpdateHeight() 1751 { if (pLogBuffer) pLogBuffer->UpdateHeight(); } 1752 void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll) 1753 { fDrawBackground=fDrawBG; this->fDrawFrame=fDrawFrame; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll); } 1754 void SetPicture(const C4Facet &rNewPic); 1755 1756 // get margins from bounds to client rect 1757 int32_t GetMarginTop() override { return 8; } 1758 int32_t GetMarginLeft() override { return 10; } 1759 int32_t GetMarginRight() override { return 5; } 1760 int32_t GetMarginBottom() override { return 8; } 1761 }; 1762 1763 // context menu opened on right-click 1764 class ContextMenu : public Window 1765 { 1766 private: 1767 // key bindings 1768 class C4KeyBinding *pKeySelUp, *pKeySelDown, *pKeySubmenu, *pKeyBack, *pKeyAbort, *pKeyConfirm, *pKeyHotkey; 1769 1770 bool KeySelUp(); 1771 bool KeySelDown(); 1772 bool KeySubmenu(); 1773 bool KeyBack(); 1774 bool KeyAbort(); 1775 bool KeyConfirm(); 1776 bool KeyHotkey(const C4KeyCodeEx &key); 1777 1778 private: 1779 static int32_t iGlobalMenuIndex; 1780 int32_t iMenuIndex; 1781 1782 void UpdateElementPositions(); // stack all menu elements 1783 1784 // element adding made private: May only add entries 1785 bool AddElement(Element *pChild); 1786 bool InsertElement(Element *pChild, Element *pInsertBefore); 1787 1788 public: 1789 // one text entry (icon+text+eventually right-arrow) 1790 class Entry : public Element 1791 { 1792 private: 1793 int32_t GetIconIndent() { return (icoIcon==-1) ? 0 : rcBounds.Hgt+2; } 1794 1795 protected: 1796 StdStrBuf sText; // entry text 1797 uint32_t cHotkey; // entry hotkey 1798 Icons icoIcon; // icon to be drawn left of text 1799 MenuHandler *pMenuHandler; // callback when item is selected 1800 ContextHandler *pSubmenuHandler; // callback when submenu is opened 1801 1802 protected: 1803 void DrawElement(C4TargetFacet &cgo) override; // draw element 1804 1805 MenuHandler *GetAndZeroCallback() 1806 { MenuHandler *pMH = pMenuHandler; pMenuHandler=nullptr; return pMH; } 1807 1808 void MouseLeave(CMouse &rMouse) override 1809 { if (GetParent()) ((ContextMenu *) GetParent())->MouseLeaveEntry(rMouse, this); } 1810 1811 bool OnHotkey(uint32_t cKey) override { return cKey==cHotkey; } 1812 1813 bool IsMenu() override { return true; } 1814 1815 public: 1816 Entry(const char *szText, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr); // ctor 1817 ~Entry() override 1818 { 1819 if (pMenuHandler) delete pMenuHandler; 1820 if (pSubmenuHandler) delete pSubmenuHandler; 1821 } 1822 1823 const char *GetText() { return sText.getData(); } 1824 1825 friend class ContextMenu; 1826 }; 1827 1828 protected: 1829 Element *pTarget{nullptr}; // target element; close menu if this is lost 1830 Element *pSelectedItem{nullptr}; // currently highlighted menu item 1831 1832 ContextMenu *pSubmenu{nullptr}; // currently open submenu 1833 1834 // mouse movement or buttons forwarded from screen: 1835 // Check bounds and forward as MouseInput to context menu or submenus 1836 // return whether inside context menu bounds 1837 bool CtxMouseInput(CMouse &rMouse, int32_t iButton, int32_t iScreenX, int32_t iScreenY, DWORD dwKeyParam); 1838 void RemoveElement(Element *pChild) override; // clear ptr - abort menu if target is destroyed 1839 1840 virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs 1841 void MouseLeaveEntry(CMouse &rMouse, Entry *pOldEntry); // callback: mouse leaves - deselect menu item if no submenu is open (callback done by menu item) 1842 1843 void DrawElement(C4TargetFacet &cgo) override; // draw BG 1844 void Draw(C4TargetFacet &cgo) override; // draw inherited (element+contents) plus submenu 1845 1846 bool IsMenu() override { return true; } 1847 1848 Screen *GetScreen() override; // return screen by static var 1849 1850 int32_t GetMarginTop() override { return 5; } 1851 int32_t GetMarginLeft() override { return 5; } 1852 int32_t GetMarginRight() override { return 5; } 1853 int32_t GetMarginBottom() override { return 5; } 1854 1855 void SelectionChanged(bool fByUser); // other item selected: Close any submenu; do callbacks 1856 1857 void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed 1858 void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed 1859 1860 void CheckOpenSubmenu(); // open submenu if present for selected item 1861 bool IsSubmenu(); // return whether it's a submenu 1862 void DoOK(); // do OK for selected item 1863 1864 public: 1865 ContextMenu(); // ctor 1866 ~ContextMenu() override; // dtor 1867 1868 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse movement or buttons 1869 1870 void Open(Element *pTarget, int32_t iScreenX, int32_t iScreenY); 1871 void Abort(bool fByUser); 1872 1873 void AddItem(const char *szText, const char *szToolTip=nullptr, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr) 1874 { 1875 Element *pNew = new ContextMenu::Entry(szText, icoIcon, pMenuHandler, pSubmenuHandler); 1876 AddElement(pNew); pNew->SetToolTip(szToolTip); 1877 } 1878 1879 Entry *GetIndexedEntry(int32_t iIndex) { return static_cast<Entry *>(GetElementByIndex(iIndex)); } 1880 1881 void SelectItem(int32_t iIndex); 1882 1883 int32_t GetMenuIndex() { return iMenuIndex; } 1884 static int32_t GetLastMenuIndex() { return iGlobalMenuIndex; } 1885 1886 Dialog *GetTargetDialog() const { return pTarget ? pTarget->GetDlg() : nullptr; } 1887 1888 friend class Screen; friend class Entry; friend class Dialog; 1889 }; 1890 1891 // a button to open a context menu 1892 class ContextButton : public Control 1893 { 1894 private: 1895 C4KeyBinding *pKeyContext; 1896 int32_t iOpenMenu; // associated menu (used to flag button down) 1897 bool fMouseOver; 1898 1899 public: 1900 ContextButton(C4Rect &rtBounds); // ctor 1901 ContextButton(Element *pForEl, bool fAdd, int32_t iHIndent=4, int32_t iVIndent=4); // ctor creating at topright pos of element 1902 ~ContextButton() override; 1903 1904 private: 1905 bool DoContext(int32_t iX=-1, int32_t iY=-1); // open context menu 1906 bool KeyContext() { return DoContext(); } 1907 void RegisterContextKey(); 1908 1909 protected: 1910 void DrawElement(C4TargetFacet &cgo) override; // draw btn 1911 1912 bool IsFocusOnClick() override { return false; } // don't select control on click 1913 1914 public: 1915 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse. left-click opens menu 1916 void MouseEnter(CMouse &rMouse) override; 1917 void MouseLeave(CMouse &rMouse) override; 1918 }; 1919 1920 1921 // combo box filler 1922 // should be ComboBox::FillCB; but nested class will cause internal compiler faults 1923 class ComboBox_FillCB 1924 { 1925 private: 1926 ComboBox *pCombo; 1927 ContextMenu *pDrop; 1928 public: 1929 virtual ~ComboBox_FillCB() = default; 1930 void FillDropDown(ComboBox *pComboBox, ContextMenu *pDropdownList) 1931 { pCombo = pComboBox; pDrop = pDropdownList; FillDropDownCB(); } 1932 virtual void FillDropDownCB() = 0; 1933 virtual bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) = 0; 1934 1935 // to be used in fill-callback only (crash otherwise!) 1936 void AddEntry(const char *szText, int32_t id); 1937 bool FindEntry(const char *szText); 1938 void ClearEntries(); 1939 void SelectEntry(int32_t iEntry); // select entry by index 1940 }; 1941 1942 template <class CB> class ComboBox_FillCallback : public ComboBox_FillCB 1943 { 1944 public: 1945 typedef void (CB::*ComboFillFunc)(ComboBox_FillCB *pFiller); 1946 typedef bool (CB::*ComboSelFunc)(ComboBox *pForCombo, int32_t idNewSelection); 1947 1948 private: 1949 CB *pCBClass; 1950 ComboFillFunc FillFunc; 1951 ComboSelFunc SelFunc; 1952 protected: 1953 void FillDropDownCB() override 1954 { if (pCBClass && FillFunc) (pCBClass->*FillFunc)(this); } 1955 bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) override 1956 { if (pCBClass && SelFunc) return (pCBClass->*SelFunc)(pForCombo, idNewSelection); else return false; } 1957 1958 public: 1959 ComboBox_FillCallback(CB * pCBClass, ComboFillFunc FillFunc, ComboSelFunc SelFunc) : 1960 pCBClass(pCBClass), FillFunc(FillFunc), SelFunc(SelFunc) { } 1961 }; 1962 1963 1964 // dropdown box to select elements from a list 1965 class ComboBox : public Control 1966 { 1967 public: 1968 struct ComboMenuCBStruct // struct used as menu callback parameter for dropdown menu 1969 { 1970 StdCopyStrBuf sText; 1971 int32_t id{0}; 1972 1973 ComboMenuCBStruct() : sText() {} 1974 ComboMenuCBStruct(const char *szText, int32_t id) : sText(szText), id(id) {} 1975 }; 1976 private: 1977 class C4KeyBinding *pKeyOpenCombo, *pKeyCloseCombo; 1978 private: 1979 int32_t iOpenMenu; // associated menu (used to flag dropped down) 1980 ComboBox_FillCB *pFillCallback; // callback used to display the dropdown 1981 char Text[C4MaxTitle+1]; // currently selected item 1982 bool fReadOnly; // merely a label in ReadOnly-mode 1983 bool fSimple; // combo without border and stuff 1984 bool fMouseOver; // mouse hovering over this? 1985 CStdFont *pUseFont; // font used to draw this control 1986 uint32_t dwFontClr, dwBGClr, dwBorderClr; // colors used to draw this control 1987 C4Facet *pFctSideArrow; // arrow gfx used to start combo-dropdown 1988 1989 private: 1990 bool DoDropdown(); // open dropdown menu (context menu) 1991 bool KeyDropDown() { return DoDropdown(); } 1992 bool KeyAbortDropDown() { return AbortDropdown(true); } 1993 bool AbortDropdown(bool fByUser); // abort dropdown menu, if it's open 1994 1995 protected: 1996 void DrawElement(C4TargetFacet &cgo) override; // draw combo box 1997 1998 bool IsFocusOnClick() override { return false; } // don't select control on click 1999 Control *IsFocusElement() override { return fReadOnly ? nullptr : this; }; // this control can gain focus if not readonly 2000 2001 void OnCtxComboSelect(C4GUI::Element *pListItem, const ComboMenuCBStruct &rNewSel); 2002 2003 public: 2004 ComboBox(const C4Rect &rtBounds); 2005 ~ComboBox() override; 2006 2007 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse. left-click opens menu 2008 void MouseEnter(CMouse &rMouse) override; // called when mouse cursor enters element region 2009 void MouseLeave(CMouse &rMouse) override; // called when mouse cursor leaves element region 2010 2011 void SetComboCB(ComboBox_FillCB *pNewFillCallback); 2012 static int32_t GetDefaultHeight(); 2013 void SetText(const char *szToText); 2014 void SetReadOnly(bool fToVal) { if ((fReadOnly = fToVal)) AbortDropdown(false); } 2015 void SetSimple(bool fToVal) { fSimple = fToVal; } 2016 const StdStrBuf GetText() { return StdStrBuf(Text, false); } 2017 void SetFont(CStdFont *pToFont) { pUseFont=pToFont; } 2018 void SetColors(uint32_t dwFontClr, uint32_t dwBGClr, uint32_t dwBorderClr) 2019 { this->dwFontClr=dwFontClr; this->dwBGClr=dwBGClr; this->dwBorderClr=dwBorderClr; } 2020 void SetDecoration(C4Facet *pFctSideArrow) 2021 { this->pFctSideArrow = pFctSideArrow; } 2022 2023 friend class ComboBox_FillCB; 2024 }; 2025 2026 class Dialog; 2027 2028 // EM window class 2029 class DialogWindow : public C4Window 2030 { 2031 public: 2032 Dialog* pDialog{nullptr}; 2033 DialogWindow(): C4Window() {} 2034 using C4Window::Init; 2035 C4Window * Init(C4AbstractApp * pApp, const char * Title, const C4Rect &rcBounds, const char *szID); 2036 void Close() override; 2037 void PerformUpdate() override; 2038 }; 2039 2040 // information on how to draw dialog borders and face 2041 class FrameDecoration 2042 { 2043 private: 2044 int iRefCount{0}; 2045 2046 bool SetFacetByAction(C4Def *pOfDef, class C4TargetFacet &rfctTarget, const char *szFacetName); 2047 2048 public: 2049 C4Def *pSourceDef; 2050 C4ID idSourceDef; 2051 uint32_t dwBackClr; // background face color 2052 C4TargetFacet fctTop, fctTopRight, fctRight, fctBottomRight, fctBottom, fctBottomLeft, fctLeft, fctTopLeft; 2053 int iBorderTop, iBorderLeft, iBorderRight, iBorderBottom; 2054 bool fHasGfxOutsideClientArea; 2055 2056 FrameDecoration() { Clear(); } 2057 void Clear(); // zero data 2058 2059 // create from ActMap and graphics of a definition (does some script callbacks to get parameters) 2060 bool SetByDef(C4Def *pSrcDef); 2061 bool SetByDef(C4ID idSourceDef); // a wrapper for the above method 2062 bool UpdateGfx(); // update Surface, e.g. after def reload 2063 2064 void Ref() { ++iRefCount; } 2065 void Deref() { if (!--iRefCount) delete this; } 2066 2067 void Draw(C4TargetFacet &cgo, C4Rect &rcDrawArea); // draw deco for given rect (rect includes border) 2068 }; 2069 2070 // a dialog 2071 class Dialog: public Window 2072 { 2073 private: 2074 enum Fade { eFadeNone=0, eFadeOut, eFadeIn }; 2075 2076 C4KeyBinding *pKeyAdvanceControl, *pKeyAdvanceControlB, *pKeyHotkey, *pKeyEnter, *pKeyEscape, *pKeyFocusDefControl; 2077 protected: 2078 WoodenLabel *pTitle; // title bar text 2079 CallbackButton<Dialog, C4GUI::IconButton> *pCloseBtn; 2080 Control *pActiveCtrl; // control that has focus 2081 bool fShow; // if set, the dlg is shown 2082 bool fOK; // if set, the user pressed OK 2083 int32_t iFade; // dlg fade (percent) 2084 Fade eFade; // fading mode 2085 bool fDelOnClose; // auto-delete when closing 2086 StdStrBuf TitleString; 2087 bool fViewportDlg; // set in ctor: if true, dlg is not independant, but drawn ad controlled within viewports 2088 DialogWindow *pWindow; // window in console mode 2089 FrameDecoration *pFrameDeco; 2090 2091 bool CreateConsoleWindow(); 2092 void DestroyConsoleWindow(); 2093 2094 void UpdateSize() override; // called when own size changed - update assigned pWindow 2095 void UpdatePos() override; // Dialogs with their own windows can only be at 0/0 2096 2097 public: 2098 Dialog(int32_t iWdt, int32_t iHgt, const char *szTitle, bool fViewportDlg); // ctor 2099 ~Dialog() override; // dtor 2100 2101 void RemoveElement(Element *pChild) override; // clear ptr 2102 2103 void Draw(C4TargetFacet &cgo) override; // render dialog (published) 2104 void DrawElement(C4TargetFacet &cgo) override; // draw dlg bg 2105 bool IsComponentOutsideClientArea() override { return !!pTitle; } // pTitle lies outside client area 2106 2107 virtual const char *GetID() { return nullptr; } 2108 2109 // special handling for viewport dialogs 2110 void ApplyElementOffset(int32_t &riX, int32_t &riY) override; 2111 void ApplyInvElementOffset(int32_t &riX, int32_t &riY) override; 2112 2113 bool IsFocused(Control *pCtrl) override { return pCtrl == pActiveCtrl; } 2114 void SetFocus(Control *pCtrl, bool fByMouse); 2115 Control *GetFocus() { return pActiveCtrl; } 2116 Dialog *GetDlg() override { return this; } // this is the dialog 2117 DialogWindow* GetDialogWindow() override { return pWindow; } 2118 2119 virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs (forward to focused control) 2120 2121 private: 2122 bool KeyHotkey(const C4KeyCodeEx &key); 2123 bool KeyFocusDefault(); 2124 2125 public: 2126 void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override; // input: mouse. forwards to child controls 2127 2128 // default control to be set if unprocessed keyboard input has been detected 2129 virtual class Control *GetDefaultControl() { return nullptr; } 2130 2131 // default dlg actions for enter/escape 2132 virtual bool OnEnter() { UserClose(true); return true; } 2133 bool KeyEnter() { return OnEnter(); } 2134 virtual bool OnEscape() { UserClose(false); return true; } 2135 bool KeyEscape() { return OnEscape(); } 2136 2137 void AdvanceFocus(bool fBackwards); // change focus to next component 2138 bool KeyAdvanceFocus(bool fBackwards) { AdvanceFocus(fBackwards); return true; } 2139 2140 int32_t GetMarginTop() override { return (pTitle ? pTitle->GetBounds().Hgt : 0) + (pFrameDeco ? pFrameDeco->iBorderTop : Window::GetMarginTop()); } 2141 int32_t GetMarginLeft() override { return pFrameDeco ? pFrameDeco->iBorderLeft : Window::GetMarginLeft(); } 2142 int32_t GetMarginRight() override { return pFrameDeco ? pFrameDeco->iBorderRight: Window::GetMarginRight(); } 2143 int32_t GetMarginBottom() override { return pFrameDeco ? pFrameDeco->iBorderBottom: Window::GetMarginBottom(); } 2144 2145 static int32_t GetDefaultTitleHeight(); 2146 2147 bool IsShown() { return fShow; } // returns whether dlg is on screen (may be invisible) 2148 bool IsOK() { return fOK; } // returns whether user pressed OK 2149 bool IsAborted() { return !fShow && !fOK; } // returns whether dialog has been aborted 2150 bool IsActive(bool fForKeyboard); // return whether dlg has mouse control focus 2151 bool IsFading() { return eFade != eFadeNone; } 2152 2153 virtual bool IsFullscreenDialog() { return false; } 2154 virtual bool HasBackground() { return false; } // true if dlg draws screen background (fullscreen dialogs only) 2155 2156 // true for dialogs that should span the whole screen 2157 // not just the mouse-viewport 2158 virtual bool IsFreePlaceDialog() { return false; } 2159 2160 // true for dialogs that should be placed at the bottom of the screen (chat) 2161 virtual bool IsBottomPlacementDialog() { return false; } 2162 2163 // true for dialogs that receive full keyboard and mouse input even in shared mode 2164 virtual bool IsExclusiveDialog() { return false; } 2165 2166 // some dialogs, like menus or chat control, don't really need a mouse 2167 // so do not enable it for those in shared mode if mouse control is disabled 2168 virtual bool IsMouseControlled() { return true; } 2169 2170 // For dialogs associated to a viewport: Return viewport (for placement) 2171 virtual C4Viewport *GetViewport() { return nullptr; } 2172 bool IsViewportDialog() { return fViewportDlg; } 2173 2174 // for custom placement procedures; should call SetPos 2175 virtual bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) { return false; } 2176 2177 // true for dialogs drawn externally 2178 bool IsExternalDrawDialog() override { return false; } 2179 2180 // z-ordering used for dialog placement 2181 virtual int32_t GetZOrdering() { return C4GUI_Z_DEFAULT; } 2182 2183 bool Show(Screen *pOnScreen, bool fCB); // show dialog on screen - default to last created screen 2184 void Close(bool fOK); // close dlg 2185 bool FadeIn(Screen *pOnScreen); // fade dlg into screen 2186 void FadeOut(bool fCloseWithOK); // fade out dlg 2187 bool DoModal(); // execute message loop until dlg is closed (or GUI destructed) - returns whether dlg was OK 2188 bool Execute(); // execute dialog - does message handling, gfx output and idle proc; return false if dlg got closed or GUI deleted 2189 bool Execute2(); // execute dialog - does message handling, gfx output and idle proc; return false and deletes self if dlg got closed or GUI deleted 2190 void SetDelOnClose(bool fToVal=true) { fDelOnClose=fToVal; } // dialog will delete itself when closed 2191 2192 void SetTitle(const char *szToTitle, bool fShowCloseButton = true); // change title text; creates or removes title bar if necessary 2193 void SetFrameDeco(FrameDecoration *pNewDeco) // change border decoration 2194 { 2195 if (pFrameDeco) pFrameDeco->Deref(); 2196 if ((pFrameDeco = pNewDeco)) pNewDeco->Ref(); 2197 UpdateOwnPos(); // margin may have changed; might need to reposition stuff 2198 } 2199 void ClearFrameDeco() // clear border decoration; no own pos update! 2200 {if (pFrameDeco) pFrameDeco->Deref(); pFrameDeco = nullptr; } 2201 FrameDecoration *GetFrameDecoration() const { return pFrameDeco; } 2202 void SetClientSize(int32_t iToWdt, int32_t iToHgt); // resize dialog so its client area has the specified size 2203 2204 void OnUserClose(C4GUI::Control *btn) // user presses close btn: Usually close dlg with abort 2205 { UserClose(false); } 2206 virtual void UserClose(bool fOK) { Close(fOK); } 2207 virtual void OnClosed(bool fOK); // callback when dlg got closed 2208 virtual void OnShown() {} // callback when shown - should not delete the dialog 2209 virtual void OnIdle() {} // idle proc in DoModal 2210 2211 ContextHandler *GetContextHandler() override // always use own context handler only (no fall-through to screen) 2212 { return pContextHandler; } 2213 #ifdef _WIN32 2214 static bool RegisterWindowClass(HINSTANCE hInst); // registers WNDCLASS for console mode dialogs 2215 #endif 2216 friend class Screen; 2217 }; 2218 2219 // a dialog covering the whole screen (using UpperBoard-caption) 2220 class FullscreenDialog : public Dialog 2221 { 2222 protected: 2223 Label *pFullscreenTitle, *pSubTitle; // subtitle to be put in upper-right corner 2224 int32_t iDlgMarginX, iDlgMarginY; // dialog margin set by screen size 2225 2226 const char *GetID() override { return nullptr; } // no ID needed, because it's never created as a window 2227 2228 public: 2229 FullscreenDialog(const char *szTitle, const char *szSubtitle); // ctor 2230 2231 void SetTitle(const char *szToTitle); // change title text; creates or removes title bar if necessary 2232 2233 protected: 2234 void DrawElement(C4TargetFacet &cgo) override; // draw dlg bg 2235 2236 // fullscreen dialogs are not closed on Enter 2237 bool OnEnter() override { return false; } 2238 2239 bool IsComponentOutsideClientArea() override { return true; } 2240 2241 virtual bool HasUpperBoard() { return false; } // standard fullscreen dialog: UpperBoard no longer present 2242 2243 bool IsFullscreenDialog() override { return true; } 2244 bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) override { return true; } // fullscreen dlg already placed 2245 2246 int32_t GetMarginTop() override { return (HasUpperBoard() ? C4UpperBoardHeight : C4GUI_FullscreenDlg_TitleHeight) + iDlgMarginY; } 2247 int32_t GetMarginLeft() override { return iDlgMarginX; } 2248 int32_t GetMarginRight() override { return iDlgMarginX; } 2249 int32_t GetMarginBottom() override { return iDlgMarginY; } 2250 void UpdateOwnPos() override; // called when element bounds were changed externally 2251 2252 // helper func: draw facet to screen background 2253 void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct); 2254 }; 2255 2256 // a button closing the Dlg 2257 class CloseButton : public Button 2258 { 2259 protected: 2260 bool fCloseResult; 2261 2262 void OnPress() override 2263 { Dialog *pDlg; if ((pDlg = GetDlg())) pDlg->UserClose(fCloseResult); } 2264 2265 public: 2266 CloseButton(const char *szBtnText, const C4Rect &rtBounds, bool fResult) // ctor 2267 : Button(szBtnText, rtBounds), fCloseResult(fResult) { } 2268 }; 2269 class CloseIconButton : public IconButton 2270 { 2271 protected: 2272 bool fCloseResult; 2273 2274 void OnPress() override 2275 { Dialog *pDlg; if ((pDlg = GetDlg())) pDlg->UserClose(fCloseResult); } 2276 2277 public: 2278 CloseIconButton(const C4Rect &rtBounds, Icons eIcon, bool fResult) // ctor 2279 : IconButton(eIcon, rtBounds, 0), fCloseResult(fResult) { } 2280 }; 2281 2282 // OK button 2283 class OKButton : public CloseButton 2284 { 2285 public: OKButton(const C4Rect &rtBounds) // ctor 2286 : CloseButton(LoadResStr("IDS_DLG_OK"), rtBounds, true) {} }; 2287 class OKIconButton : public CloseIconButton 2288 { 2289 public: OKIconButton(const C4Rect &rtBounds, Icons eIcon) // ctor 2290 : CloseIconButton(rtBounds, eIcon, true) {} }; 2291 // cancel button 2292 class CancelButton : public CloseButton 2293 { 2294 public: CancelButton(const C4Rect &rtBounds) // ctor 2295 : CloseButton(LoadResStr("IDS_DLG_CANCEL"), rtBounds, false) {} }; 2296 class CancelIconButton : public CloseIconButton 2297 { 2298 public: CancelIconButton(const C4Rect &rtBounds, Icons eIcon) // ctor 2299 : CloseIconButton(rtBounds, eIcon, false) {} }; 2300 // regular close button 2301 class DlgCloseButton : public CloseButton 2302 { 2303 public: DlgCloseButton(const C4Rect &rtBounds) // ctor 2304 : CloseButton(LoadResStr("IDS_DLG_CLOSE"), rtBounds, true) {} }; 2305 // Yes button 2306 class YesButton : public CloseButton 2307 { 2308 public: YesButton(const C4Rect &rtBounds) // ctor 2309 : CloseButton(LoadResStr("IDS_DLG_YES"), rtBounds, true) {} }; 2310 // No button 2311 class NoButton : public CloseButton 2312 { 2313 public: NoButton(const C4Rect &rtBounds) // ctor 2314 : CloseButton(LoadResStr("IDS_DLG_NO"), rtBounds, false) {} }; 2315 // Retry button 2316 class RetryButton : public CloseButton 2317 { 2318 public: RetryButton(const C4Rect &rtBounds) // ctor 2319 : CloseButton(LoadResStr("IDS_BTN_RETRY"), rtBounds, true) {} }; 2320 // Reset button 2321 class ResetButton : public CloseButton 2322 { 2323 public: ResetButton(const C4Rect &rtBounds) // ctor 2324 : CloseButton(LoadResStr("IDS_BTN_RESET"), rtBounds, true) {} }; 2325 2326 // a simple message dialog 2327 class MessageDialog : public Dialog 2328 { 2329 private: 2330 bool fHasOK; 2331 int32_t *piConfigDontShowAgainSetting; 2332 class C4KeyBinding *pKeyCopy; 2333 std::string sCopyText; // text that goes into clipboard if user presses Ctrl+C on this window 2334 public: 2335 enum Buttons { btnOK=1, btnAbort=2, btnYes=4, btnNo=8, btnRetry=16, btnReset=32, 2336 btnOKAbort=btnOK|btnAbort, btnYesNo=btnYes|btnNo, btnRetryAbort=btnRetry|btnAbort 2337 }; 2338 enum DlgSize { dsRegular=C4GUI_MessageDlgWdt, dsMedium=C4GUI_MessageDlgWdtMedium, dsSmall=C4GUI_MessageDlgWdtSmall }; 2339 MessageDialog(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, DlgSize eSize=dsRegular, int32_t *piConfigDontShowAgainSetting=nullptr, bool fDefaultNo=false); 2340 ~MessageDialog() override; 2341 2342 protected: 2343 bool OnEnter() override { if (!fHasOK) return false; Close(true); return true; } 2344 void OnDontShowAgainCheck(C4GUI::Element *pCheckBox) 2345 { if (piConfigDontShowAgainSetting) *piConfigDontShowAgainSetting = static_cast<C4GUI::CheckBox *>(pCheckBox)->GetChecked(); } 2346 bool KeyCopy(); 2347 2348 const char *GetID() override { return "MessageDialog"; } 2349 int32_t GetZOrdering() override { return C4GUI_Z_INPUT; } 2350 }; 2351 2352 // a confirmation dialog, which performs a callback after confirmation 2353 class ConfirmationDialog : public MessageDialog 2354 { 2355 private: 2356 BaseCallbackHandler *pCB; 2357 2358 public: 2359 ConfirmationDialog(const char *szMessage, const char *szCaption, BaseCallbackHandler *pCB, DWORD dwButtons=MessageDialog::btnOKAbort, bool fSmall=false, Icons icoIcon=Ico_Confirm); 2360 ~ConfirmationDialog() override { if (pCB) pCB->DeRef(); } 2361 2362 protected: 2363 void OnClosed(bool fOK) override; // callback when dlg got closed 2364 }; 2365 2366 // a simple progress dialog 2367 class ProgressDialog : public Dialog 2368 { 2369 protected: 2370 ProgressBar *pBar; // progress bar component 2371 2372 const char *GetID() override { return "ProgressDialog"; } 2373 2374 public: 2375 ProgressDialog(const char *szMessage, const char *szCaption, int32_t iMaxProgress, int32_t iInitialProgress, Icons icoIcon); 2376 2377 void SetProgress(int32_t iToProgress) { pBar->SetProgress(iToProgress); } // change progress 2378 bool OnEnter() override { return false; } // don't close on Enter! 2379 }; 2380 2381 2382 class BaseInputCallback 2383 { 2384 public: 2385 virtual void OnOK(const StdStrBuf &sText) = 0; 2386 virtual ~BaseInputCallback() = default; 2387 }; 2388 2389 template <class T> class InputCallback : public BaseInputCallback 2390 { 2391 public: 2392 typedef void (T::*CBFunc)(const StdStrBuf &); 2393 private: 2394 T *pTarget; 2395 CBFunc pCBFunc; 2396 public: 2397 InputCallback(T *pTarget, CBFunc pCBFunc) : pTarget(pTarget), pCBFunc(pCBFunc) {} 2398 2399 void OnOK(const StdStrBuf &sText) override { (pTarget->*pCBFunc)(sText); } 2400 }; 2401 2402 // a dialog for a one-line text input; contains OK and cancel button 2403 class InputDialog : public Dialog 2404 { 2405 protected: 2406 Edit *pEdit; // edit for text input 2407 BaseInputCallback *pCB; 2408 C4Rect rcEditBounds; 2409 bool fChatLayout; 2410 Label *pChatLbl; 2411 2412 const char *GetID() override { return "InputDialog"; } 2413 2414 public: 2415 InputDialog(const char *szMessage, const char *szCaption, Icons icoIcon, BaseInputCallback *pCB, bool fChatLayout=false); 2416 ~InputDialog() override { if (pCB) delete pCB; } 2417 2418 void OnClosed(bool fOK) override { if (pCB && fOK) pCB->OnOK(StdStrBuf(pEdit->GetText())); Dialog::OnClosed(fOK); } // close CB 2419 void SetMaxText(int32_t iMaxLen) { pEdit->SetMaxText(iMaxLen); } 2420 void SetInputText(const char *szToText); 2421 const char *GetInputText() { return pEdit->GetText(); } 2422 void SetCustomEdit(Edit *pCustomEdit); 2423 }; 2424 2425 // a dialog showing some information text 2426 class InfoDialog : public Dialog, private C4ApplicationSec1Timer 2427 { 2428 private: 2429 int32_t iScroll; // current scroll pos; backup for text update 2430 2431 protected: 2432 TextWindow *pTextWin; 2433 2434 void CreateSubComponents(); // ctor func 2435 2436 // add text to the info window 2437 void AddLine(const char *szText); 2438 void AddLineFmt(const char *szFmtString, ...) GNUC_FORMAT_ATTRIBUTE_O; 2439 2440 void BeginUpdateText(); // backup scrolling and clear text window 2441 void EndUpdateText(); // restore scroll pos; set last update time 2442 2443 virtual void UpdateText() {}; // function to be overwritten for timed dlgs: Update window text 2444 2445 void OnSec1Timer() override; // idle proc: update text if necessary 2446 public: 2447 InfoDialog(const char *szCaption, int32_t iLineCount); // ctor 2448 InfoDialog(const char *szCaption, int iLineCount, const StdStrBuf &sText); // ctor - init w/o timer 2449 ~InfoDialog() override; 2450 }; 2451 2452 // a keyboard event that's only executed if the dialog is activated 2453 template <class TargetClass> class DlgKeyCB : public C4KeyCB<TargetClass> 2454 { 2455 private: 2456 typedef C4KeyCB<TargetClass> Base; 2457 typedef bool(TargetClass::*CallbackFunc)(); 2458 public: 2459 DlgKeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2460 : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} 2461 2462 bool CheckCondition() override { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } 2463 }; 2464 2465 template <class TargetClass> class DlgKeyCBPassKey : public C4KeyCBPassKey<TargetClass> 2466 { 2467 private: 2468 typedef C4KeyCBPassKey<TargetClass> Base; 2469 typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key); 2470 public: 2471 DlgKeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2472 : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} 2473 2474 bool CheckCondition() override { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } 2475 }; 2476 2477 template <class TargetClass, class ParameterType> class DlgKeyCBEx : public C4KeyCBEx<TargetClass, ParameterType> 2478 { 2479 private: 2480 typedef C4KeyCBEx<TargetClass, ParameterType> Base; 2481 typedef bool(TargetClass::*CallbackFunc)(ParameterType par); 2482 public: 2483 DlgKeyCBEx(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2484 : Base(rTarget, par, pFuncDown, pFuncUp, pFuncPressed) {} 2485 2486 bool CheckCondition() override { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } 2487 }; 2488 2489 // a keyboard event that's only executed if the control has focus 2490 template <class TargetClass> class ControlKeyCB : public C4KeyCB<TargetClass> 2491 { 2492 private: 2493 typedef C4KeyCB<TargetClass> Base; 2494 typedef bool(TargetClass::*CallbackFunc)(); 2495 public: 2496 ControlKeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2497 : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} 2498 2499 bool CheckCondition() override { return Base::rTarget.HasDrawFocus(); } 2500 }; 2501 2502 template <class TargetClass, class ParameterType> class ControlKeyCBExPassKey : public C4KeyCBExPassKey<TargetClass, ParameterType> 2503 { 2504 private: 2505 typedef C4KeyCBExPassKey<TargetClass, ParameterType> Base; 2506 typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, const ParameterType &par); 2507 public: 2508 ControlKeyCBExPassKey(TargetClass &rTarget, const ParameterType &rPar, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2509 : Base(rTarget, rPar, pFuncDown, pFuncUp, pFuncPressed) {} 2510 2511 bool CheckCondition() override { return Base::rTarget.HasDrawFocus(); } 2512 }; 2513 2514 // key event that checks whether a control is active, but forwards to the dialog 2515 template <class TargetClass> class ControlKeyDlgCB : public C4KeyCB<TargetClass> 2516 { 2517 private: 2518 class Control *pCtrl; 2519 typedef C4KeyCB<TargetClass> Base; 2520 typedef bool(TargetClass::*CallbackFunc)(); 2521 public: 2522 ControlKeyDlgCB(Control *pCtrl, TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) 2523 : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed), pCtrl(pCtrl) {} 2524 2525 bool CheckCondition() override { return pCtrl && pCtrl->IsInActiveDlg(true) && pCtrl->IsVisible(); } 2526 }; 2527 2528 // mouse cursor control 2529 class CMouse 2530 { 2531 public: 2532 int32_t x,y; // cursor position 2533 bool LDown, MDown, RDown; // mouse button states 2534 int32_t LDownX, LDownY; // position where left button was pressed last 2535 DWORD dwKeys; // shift, ctrl, etc. 2536 bool fActive; 2537 C4TimeMilliseconds tLastMovementTime; // C4TimeMilliseconds::Now() when the mouse pos changed last 2538 2539 // whether last input was done by mouse 2540 // set to true whenever mouse pos changes or buttons are pressed 2541 // reset by keyboard actions to avoid tooltips where the user isn't even doing anything 2542 bool fActiveInput; 2543 2544 enum TooltipShowState 2545 { 2546 TTST_None = 0, // show no tooltips 2547 TTST_Immediate = 1, // show only tooltips of elements that require immediate show (i.e. otherwise unlabeled buttons) 2548 TTST_All = 2, // show all tooltips (mouse hovered on same element for some time) 2549 }; 2550 2551 public: 2552 Element *pMouseOverElement, *pPrevMouseOverElement; // elements at mouse position (after and before processing of mouse event) 2553 Element *pDragElement; // element at pos where left mouse button was clicked last 2554 2555 public: 2556 CMouse(int32_t iX, int32_t iY); // ctor 2557 ~CMouse(); // dtor 2558 2559 void Draw(C4TargetFacet &cgo, TooltipShowState draw_tool_tips); // draw cursor 2560 2561 void Input(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // process mouse input 2562 bool IsLDown() { return LDown; } 2563 2564 void GetLastXY(int32_t &rX, int32_t &rY, DWORD &rdwKeys) { rX=x; rY=y; rdwKeys=dwKeys; } 2565 2566 void ResetElements() // reset MouseOver/etc.-controls 2567 { pMouseOverElement=pPrevMouseOverElement=pDragElement=nullptr; } 2568 void ReleaseElements(); // reset MouseOver/etc.-controls, doing the appropriate callbacks 2569 void OnElementGetsInvisible(Element *pChild); // clear ptr 2570 2571 void RemoveElement(Element *pChild); // clear ptr 2572 2573 void SetOwnedMouse(bool fToVal) { fActive=fToVal; } 2574 2575 void ResetToolTipTime() { tLastMovementTime = C4TimeMilliseconds::Now(); } 2576 bool IsMouseStill() { return C4TimeMilliseconds::Now()-tLastMovementTime >= C4GUI_ToolTipShowTime; } 2577 void ResetActiveInput() { fActiveInput = false; } 2578 bool IsActiveInput() { return fActiveInput; } 2579 2580 void ReleaseButtons() { LDown = RDown = MDown = false; } 2581 }; 2582 2583 // the dialog client area 2584 class Screen : public Window 2585 { 2586 public: 2587 CMouse Mouse; 2588 2589 protected: 2590 Dialog *pActiveDlg; // current, active dialog 2591 ContextMenu *pContext{nullptr}; // currently opened context menu (lowest submenu) 2592 bool fExclusive{true}; // default true. if false, input is shared with the game 2593 C4Rect PreferredDlgRect; // rectangle in which dialogs should be placed 2594 float fZoom{1.0f}; 2595 2596 static Screen *pScreen; // static singleton var 2597 2598 public: 2599 void RemoveElement(Element *pChild) override; // clear ptr 2600 const C4Rect &GetPreferredDlgRect() { return PreferredDlgRect; } 2601 2602 protected: 2603 using Window::Draw; 2604 virtual void Draw(C4TargetFacet &cgo, bool fDoBG); // draw screen contents 2605 2606 void ElementPosChanged(Element *pOfElement) override; // called when a dialog is moved 2607 2608 public: 2609 void ShowDialog(Dialog *pDlg, bool fFade); // present dialog to user 2610 void CloseDialog(Dialog *pDlg, bool fFade);// hide dialog from user 2611 void ActivateDialog(Dialog *pDlg); // set dlg as active 2612 void RecheckActiveDialog(); 2613 2614 Dialog *GetTopDialog(); // get topmost dlg 2615 public: 2616 Screen(); 2617 ~Screen() override; 2618 2619 void Init(int32_t tx, int32_t ty, int32_t twdt, int32_t thgt); 2620 void Clear(); 2621 2622 void Render(bool fDoBG); // render to pDraw 2623 void RenderMouse(C4TargetFacet &cgo); // draw mouse only 2624 2625 Screen *GetScreen() override { return this; }; // return contained screen 2626 static Screen *GetScreenS() { return pScreen; } // get global screen 2627 2628 float GetZoom() const { return fZoom; }; // get GUI zoom 2629 2630 bool IsActive() { return !!GetTopDialog(); } // return whether GUI is active 2631 2632 bool KeyAny(); // to be called on keystrokes; resets some tooltip-times 2633 virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs 2634 using Window::MouseInput; 2635 bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, Dialog *pDlg, class C4Viewport *pVP); // input: mouse movement or buttons; sends MouseEnter/Leave; return whether inside dialog 2636 void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP); // pVP specified for console mode viewports only 2637 void SetMouseInGUI(bool fInGUI, bool fByMouse); 2638 bool RecheckMouseInput(); // do mouse movement iusing last input flags 2639 2640 bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr); // show message 2641 bool ShowErrorMessage(const char *szMessage); // show message: Error caption and icon 2642 bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr); // show modal message dlg 2643 ProgressDialog *ShowProgressDlg(const char *szMessage, const char *szCaption, int32_t iMaxProgress=100, int32_t iInitialProgress=0, Icons icoIcon=Ico_Wait); // create and show a progress dialog 2644 bool ShowModalDlg(Dialog *pDlg, bool fDestruct=true); // show any dialog modal and destruct it afterwards 2645 bool ShowRemoveDlg(Dialog *pDlg); // show dialog, and flag it to delete itself when closed; return immediately 2646 2647 void CloseAllDialogs(bool fWithOK); // close all dialogs on the screen; top dlgs first 2648 void SetPreferredDlgRect(const C4Rect &rtNewPref) { PreferredDlgRect = rtNewPref; } 2649 #ifdef USE_WIN32_WINDOWS 2650 Dialog *GetDialog(HWND hWindow); // get console dialog 2651 #endif 2652 Dialog *GetDialog(C4Window * pWindow); // get console dialog 2653 void DoContext(ContextMenu *pNewCtx, Element *pAtElement, int32_t iX, int32_t iY); // open context menu (closes any other contextmenu) 2654 void AbortContext(bool fByUser) { if (pContext) pContext->Abort(fByUser); } // close context menu 2655 int32_t GetContextMenuIndex() { return pContext ? pContext->GetMenuIndex() : 0; } // get current context-menu (lowest level) 2656 int32_t GetLastContextMenuIndex() { return ContextMenu::GetLastMenuIndex(); } // get last opened context-menu (lowest level) 2657 bool HasContext() { return !!pContext; } 2658 2659 bool HasFullscreenDialog(bool fIncludeFading); // true if on fullscreen dialog is visible 2660 Dialog *GetFullscreenDialog(bool fIncludeFading); // return dlg pointer to first found fullscreen dialog 2661 2662 // callback from C4Game from message-handling of WM_TIMER; forwards to dialog-timer 2663 void OnSec1Timer(); 2664 2665 // exclusive 2666 void SetExclusive(bool fToState) { fExclusive = fToState; Mouse.SetOwnedMouse(fExclusive); UpdateMouseFocus(); } 2667 bool IsExclusive() { return fExclusive; } 2668 bool HasKeyboardFocus() 2669 { 2670 // always hook keyboard in exclusive mode; only on exclusive top dialogs in shared mode 2671 if (IsExclusive()) return true; 2672 Dialog *pDlg = GetTopDialog(); 2673 return pDlg && pDlg->IsExclusiveDialog(); 2674 } 2675 bool HasMouseFocus() // return whether mouse controls GUI only 2676 { return HasKeyboardFocus(); } 2677 int32_t GetMouseControlledDialogCount(); // count dlgs with that flag set 2678 void UpdateMouseFocus(); // when exclusive mode has changed: Make sure mouse clip is correct 2679 2680 // draw a tooltip in a box 2681 static void DrawToolTip(const char *szTip, C4TargetFacet &cgo, float guix, float guiy); 2682 2683 // gamepad 2684 void UpdateGamepadGUIControlEnabled(); // callback when gamepad gui option changed 2685 2686 friend class Dialog; friend class ContextMenu; 2687 }; 2688 2689 // menu handler bound to member functions of another class 2690 template <class CBClass> class CBMenuHandler : public MenuHandler 2691 { 2692 private: 2693 CBClass *pCBTarget; // callback target 2694 typename DlgCallback<CBClass>::ContextClickFunc pCallbackFn; // callback function 2695 2696 public: 2697 // ctor 2698 CBMenuHandler<CBClass>(CBClass *pCBTarget, typename DlgCallback<CBClass>::ContextClickFunc pCallbackFn, int32_t iaExtra=0) 2699 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) { } 2700 2701 void OnOK(Element *pTargetElement) override 2702 { 2703 if (!pCBTarget /* || !pCallbackFn */) return; 2704 // do callback 2705 (pCBTarget->*pCallbackFn)(pTargetElement); 2706 } 2707 }; 2708 2709 // menu handler bound to member functions of another class, providing extra storage for parameters 2710 template <class CBClass, class TEx> class CBMenuHandlerEx : public MenuHandler 2711 { 2712 private: 2713 CBClass *pCBTarget; // callback target 2714 typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn; // callback function 2715 TEx Extra; // extra data 2716 public: 2717 // ctor 2718 CBMenuHandlerEx<CBClass, TEx>(CBClass *pCBTarget, typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn, TEx aExtra) 2719 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra(std::move(aExtra)) { } 2720 CBMenuHandlerEx<CBClass, TEx>(CBClass *pCBTarget, typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn) 2721 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra() { } 2722 2723 const TEx &GetExtra() const { return Extra; } 2724 void SetExtra(const TEx &e) { Extra = e; } 2725 2726 void OnOK(Element *pTargetElement) override 2727 { 2728 if (!pCBTarget || !pCallbackFn) return; 2729 // do callback 2730 (pCBTarget->*pCallbackFn)(pTargetElement, Extra); 2731 } 2732 }; 2733 2734 // context handler bound to member functions of another class 2735 template <class CBClass> class CBContextHandler : public ContextHandler 2736 { 2737 private: 2738 CBClass *pCBTarget; // callback target 2739 typename DlgCallback<CBClass>::ContextFunc pCallbackFn; // callback function 2740 2741 public: 2742 // ctor 2743 CBContextHandler<CBClass>(CBClass *pCBTarget, typename DlgCallback<CBClass>::ContextFunc pCallbackFn) 2744 : ContextHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) { } 2745 2746 bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) override 2747 { 2748 DlgCallback<CBClass>(); 2749 if (!pCBTarget) return false; 2750 // if (!pCallbackFn) return false; 2751 Screen *pScreen = pOnElement->GetScreen(); 2752 if (!pScreen) return false; 2753 // let CB func create context menu 2754 ContextMenu *pNewMenu = (pCBTarget->*pCallbackFn)(pOnElement, iX, iY); 2755 if (!pNewMenu) return false; 2756 // open it on screen 2757 pScreen->DoContext(pNewMenu, pOnElement, iX, iY); 2758 // done, success 2759 return true; 2760 } 2761 2762 C4GUI::ContextMenu *OnSubcontext(Element *pOnElement) override 2763 { 2764 // query directly 2765 return (pCBTarget->*pCallbackFn)(pOnElement, 0, 0); 2766 } 2767 friend class Dialog; 2768 }; 2769 2770 // a helper used to align elements 2771 class ComponentAligner 2772 { 2773 protected: 2774 C4Rect rcClientArea; // free client area 2775 int32_t iMarginX, iMarginY; // horizontal and vertical margins of components 2776 2777 static C4Rect rcTemp; // temp rect 2778 2779 public: 2780 ComponentAligner(const C4Rect &rcArea, int32_t iMarginX, int32_t iMarginY, bool fZeroAreaXY=false) // ctor 2781 : rcClientArea(rcArea), iMarginX(iMarginX), iMarginY(iMarginY) 2782 { if (fZeroAreaXY) rcClientArea.x=rcClientArea.y=0; } 2783 2784 bool GetFromTop(int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the top 2785 bool GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the left 2786 bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the right 2787 bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the bottom 2788 void GetAll(C4Rect &rcOut); // get remaining client area 2789 bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get centered subregion within area 2790 2791 C4Rect &GetFromTop(int32_t iHgt, int32_t iWdt=-1) { GetFromTop(iHgt, iWdt, rcTemp); return rcTemp; } // get a portion from the top 2792 C4Rect &GetFromLeft(int32_t iWdt, int32_t iHgt=-1) { GetFromLeft(iWdt, iHgt, rcTemp); return rcTemp; } // get a portion from the left 2793 C4Rect &GetFromRight(int32_t iWdt, int32_t iHgt=-1) { GetFromRight(iWdt, iHgt, rcTemp); return rcTemp; } // get a portion from the right 2794 C4Rect &GetFromBottom(int32_t iHgt, int32_t iWdt=-1) { GetFromBottom(iHgt, iWdt, rcTemp); return rcTemp; } // get a portion from the bottom 2795 C4Rect &GetAll() { GetAll(rcTemp); return rcTemp; } // get remaining client are 2796 C4Rect &GetCentered(int32_t iWdt, int32_t iHgt) { GetCentered(iWdt, iHgt, rcTemp); return rcTemp; } // get centered subregion within area (w/o size checks) 2797 2798 C4Rect &GetGridCell(int32_t iSectX, int32_t iSectXMax, int32_t iSectY, int32_t iSectYMax, int32_t iSectSizeX=-1, int32_t iSectSizeY=-1, bool fCenterPos=false, int32_t iSectNumX=1, int32_t iSectNumY=1); 2799 2800 int32_t GetWidth() const { return rcClientArea.Wdt; } 2801 int32_t GetHeight() const { return rcClientArea.Hgt; } 2802 int32_t GetHMargin() const { return iMarginX; } 2803 int32_t GetVMargin() const { return iMarginY; } 2804 2805 int32_t GetInnerWidth() const { return rcClientArea.Wdt-2*iMarginX; } 2806 int32_t GetInnerHeight() const { return rcClientArea.Hgt-2*iMarginY; } 2807 2808 void ExpandTop(int32_t iByHgt) { rcClientArea.y-=iByHgt; rcClientArea.Hgt+=iByHgt; } // enlarge client rect 2809 void ExpandLeft(int32_t iByWdt) { rcClientArea.x-=iByWdt; rcClientArea.Wdt+=iByWdt; } // enlarge client rect 2810 void ExpandRight(int32_t iByWdt) { rcClientArea.Wdt+=iByWdt; } // enlarge client rect 2811 void ExpandBottom(int32_t iByHgt) { rcClientArea.Hgt+=iByHgt; } // enlarge client rect 2812 2813 void LogIt(const char *szName); 2814 }; 2815 2816 // shortcut for check whether GUI is active 2817 inline bool IsActive() { return Screen::GetScreenS()->IsActive(); } 2818 inline bool IsExclusive() { return Screen::GetScreenS()->IsExclusive(); } 2819 2820 // shortcut for GUI screen size 2821 inline int32_t GetScreenWdt() { return Screen::GetScreenS()->GetBounds().Wdt; } 2822 inline int32_t GetScreenHgt() { return Screen::GetScreenS()->GetBounds().Hgt; } 2823 2824 // sound effect in GUI: Only if enabled 2825 void GUISound(const char *szSound); 2826 2827 // Zoom 2828 inline float GetZoom() { return Screen::GetScreenS()->GetZoom(); } 2829 inline void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP) // pVP specified for console mode viewports only 2830 { Screen::GetScreenS()->MouseMove(iButton, iX, iY, dwKeyParam, pVP); } 2831 2832 extern Screen TheScreen; 2833 } // end of namespace 2834 2835 typedef C4GUI::Screen C4GUIScreen; 2836 extern C4GUIScreen *pGUI; 2837 #endif 2838