/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ // generic user interface // defines user controls // 2do: // mouse wheel processing // disabled buttons #ifndef INC_C4Gui #define INC_C4Gui #include "graphics/C4FontLoader.h" #include "lib/C4Rect.h" #include "graphics/C4FacetEx.h" #include "lib/C4LogBuf.h" #include "gui/C4KeyboardInput.h" #include "platform/StdScheduler.h" #include "object/C4Id.h" #include "platform/C4Window.h" // consts (load those from a def file some time) // font colors - alpha is font alpha, which is inversed opaque #define C4GUI_CaptionFontClr 0xffffffff #define C4GUI_Caption2FontClr 0xffffff00 #define C4GUI_InactCaptionFontClr 0xffafafaf #define C4GUI_ButtonFontClr 0xffffff00 #define C4GUI_ButtonFontShadowClr 0xff000000 #define C4GUI_StatusFontClr 0xffffffff #define C4GUI_MessageFontClr 0xffffffff #define C4GUI_MessageFontAlpha 0xff000000 #define C4GUI_InactMessageFontClr 0xffafafaf #define C4GUI_NotifyFontClr 0xffff0000 #define C4GUI_ComboFontClr 0xffffffff #define C4GUI_CheckboxFontClr 0xffffffff #define C4GUI_SmallCheckboxFontClr 0xffffffff #define C4GUI_CheckboxDisabledFontClr 0xffafafaf #define C4GUI_LogFontClr 0xffafafaf #define C4GUI_LogFontClr2 0xffff1f1f #define C4GUI_ErrorFontClr 0xffff1f1f #define C4GUI_ProgressBarFontClr 0xffffffff #define C4GUI_ContextFontClr 0xffffffff #define C4GUI_GfxTabCaptActiveClr 0xff000000 #define C4GUI_GfxTabCaptInactiveClr 0xff000000 // other colors #define C4GUI_ImportantBGColor 0x2f00007f #define C4GUI_ProgressBarColor 0x4fffffff #define C4GUI_ListBoxSelColor 0x4faf0000 #define C4GUI_ListBoxInactSelColor 0x4f7f7f7f #define C4GUI_ContextSelColor 0x4faf0000 #define C4GUI_ContextBGColor 0xaf3f1a00 #define C4GUI_StandardBGColor 0x9f000000 #define C4GUI_ActiveTabBGColor C4GUI_StandardBGColor #define C4GUI_ListBoxBarColor 0x7f772200 #define C4GUI_EditBGColor 0x7f000000 #define C4GUI_EditFontColor 0xffffffff #define C4GUI_ToolTipBGColor 0xFFF1EA78 #define C4GUI_ToolTipFrameColor 0x7f000000 #define C4GUI_ToolTipColor 0xFF483222 // winner/loser color marking #define C4GUI_WinningTextColor 0xffffdf00 #define C4GUI_WinningBackgroundColor 0xafaf7a00 #define C4GUI_LosingTextColor 0xffffffff #define C4GUI_LosingBackgroundColor 0x7fafafaf // border colors for 3D-frames #define C4GUI_BorderAlpha 0x4f #define C4GUI_BorderColor1 0x772200 #define C4GUI_BorderColor2 0x331100 #define C4GUI_BorderColor3 0xaa4400 #define C4GUI_BorderColorA1 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor1) #define C4GUI_BorderColorA2 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor2) #define C4GUI_BorderColorA3 (C4GUI_BorderAlpha<<24 | C4GUI_BorderColor3) // GUI icon sizes #define C4GUI_IconWdt 40 #define C4GUI_IconHgt 40 #define C4GUI_IconExWdt 64 #define C4GUI_IconExHgt 64 #define C4GUI_ControllerIconWdt 100 #define C4GUI_ControllerIconHgt 100 #define C4GUI_IconLabelSpacing 2 // space between an icon and its text // scroll bar size #define C4GUI_ScrollBarWdt 16 #define C4GUI_ScrollBarHgt 16 #define C4GUI_ScrollArrowHgt 16 #define C4GUI_ScrollArrowWdt 16 #define C4GUI_ScrollThumbHgt 16 // only for non-dynamic scroll thumbs #define C4GUI_ScrollThumbWdt 16 // only for non-dynamic scroll thumbs // button size #define C4GUI_ButtonHgt 32 // height of buttons #define C4GUI_BigButtonHgt 40 // height of bigger buttons (main menu) #define C4GUI_ButtonAreaHgt 40 // height of button areas #define C4GUI_DefButtonWdt 140 // width of default buttons #define C4GUI_DefButton2Wdt 120 // width of default buttons if there are two of them #define C4GUI_DefButton2HSpace 10 // horzontal space between two def dlg buttons // default checkbox height #define C4GUI_CheckBoxHgt 32 #define C4GUI_CheckBoxLabelSpacing 4 // pixels between checkbox box and label // list box item spacing #define C4GUI_DefaultListSpacing 1 // 1 px of free space between two list items #define C4GUI_ListBoxBarIndent 10 // default dialog box sizes #define C4GUI_MessageDlgWdt 500 // width of message dialog #define C4GUI_MessageDlgWdtMedium 360 // width of message dialog w/o much text #define C4GUI_MessageDlgWdtSmall 300 // width of message dialog w/o much text #define C4GUI_ProgressDlgWdt 500 // width of progress dialog #define C4GUI_InputDlgWdt 300 #define C4GUI_DefDlgIndent 10 // indent for default dlg items #define C4GUI_DefDlgSmallIndent 4 // indent for dlg items that are grouped #define C4GUI_MessageDlgVRoom 100 // height added to text height in message dialog #define C4GUI_ProgressDlgVRoom 150 // height added to text height in progress dialog #define C4GUI_InputDlgVRoom 150 #define C4GUI_ProgressDlgPBHgt 30 // height of progress bar in progress dlg #define C4GUI_InfoDlgWdt 620 // width of info dialog #define C4GUI_InfoDlgVRoom 100 // height added to text height in info dialog #define C4GUI_MaxToolTipWdt 500 // maximum width for tooltip boxes // time for tooltips to appear (msecs) -evaluated while drawing #define C4GUI_ToolTipShowTime 500 // 0.5 seconds // time for title bars to start scrolling to make longer text visible -evaluated while drawing #define C4GUI_TitleAutoScrollTime 3000 // 3 seconds // time interval for tab caption scrolling #define C4GUI_TabCaptionScrollTime 500 // 0.5 seconds // Z-ordering of dialogs #define C4GUI_Z_CHAT +2 // chat input dialog more important than other input dialogs #define C4GUI_Z_INPUT +1 // input dialogs on top of others #define C4GUI_Z_DEFAULT 0 // normal placement on top of viewports #define C4GUI_Z_PLAYERMENU -1 // inside viewport: player menu #define C4GUI_Z_OBJECTMENU -2 // inside viewport: cursor menu #define C4GUI_MinWoodBarHgt 23 #define C4GUI_FullscreenDlg_TitleHeight C4UpperBoardHeight // pixels reserved for top of fullscreen dialog title #define C4GUI_FullscreenCaptionFontClr 0xffffff00 namespace C4GUI { // some class predefs // C4Gui.cpp class Element; class Screen; class CMouse; class ComponentAligner; // C4GuiLabels.cpp class Label; class WoodenLabel; class MultilineLabel; class HorizontalLine; class ProgressBar; class Picture; class Icon; class PaintBox; class TextWindow; // C4GuiContainers.cpp class Container; class Window; class GroupBox; class Control; class ScrollBar; class ScrollWindow; // C4GuiButton.cpp class Button; template class CallbackButton; class IconButton; class CloseButton; class OKButton; class CancelButton; class CloseIconButton; class OKIconButton; class CancelIconButton; // C4GuiEdit.cpp class Edit; // C4GuiCheckBox.cpp class CheckBox; // C4GuiListBox.cpp class ListBox; // C4GuiTabular.cpp class Tabular; // C4GuiMenu.cpp class ContextMenu; class ContextButton; // C4GUIComboBox.cpp class ComboBox; // C4GuiDialog.cpp class Dialog; class MessageDialog; class ProgressDialog; class InputDialog; class InfoDialog; // inline class MenuHandler; class ContextHandler; // expand text like "Te&xt" to "Text". Color yellow for normal hotkey and red for tooltip. bool ExpandHotkeyMarkup(StdStrBuf &sText, uint32_t &rcHotkey, bool for_tooltip = false); // make color readable on black: max alpha to 0x1f, max color hues DWORD MakeColorReadableOnBlack(DWORD &rdwClr); // menu handler: generic context menu callback class MenuHandler { public: MenuHandler() {} //ctor virtual ~MenuHandler() { } // dtor virtual void OnOK(Element *pTarget) = 0; // menu item selected }; // context handler: opens context menu on right-click or menu key class ContextHandler { private: int32_t iRefs; public: ContextHandler() : iRefs(0) { } //ctor virtual ~ContextHandler() { } // dtor virtual bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) = 0; // context queried - ret true if handled virtual ContextMenu *OnSubcontext(Element *pOnElement) = 0; // subcontext queried inline void Ref() { ++iRefs; } inline void DeRef() { if (!--iRefs) delete this; } }; // generic callback handler class BaseCallbackHandler { private: int32_t iRefs; public: BaseCallbackHandler() : iRefs(0) {} virtual ~BaseCallbackHandler() {} inline void Ref() { ++iRefs; } inline void DeRef() { if (!--iRefs) delete this; } virtual void DoCall(class Element *pElement) = 0; }; template class CallbackHandler : public BaseCallbackHandler { public: typedef void (CB::*Func)(class Element *pElement); private: CB *pCBClass; Func CBFunc; public: virtual void DoCall(class Element *pElement) { ((pCBClass)->*CBFunc)(pElement); } CallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} }; template class CallbackHandlerNoPar : public BaseCallbackHandler { public: typedef void (CB::*Func)(); private: CB *pCBClass; Func CBFunc; public: virtual void DoCall(class Element *pElement) { ((pCBClass)->*CBFunc)(); } CallbackHandlerNoPar(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} }; template class CallbackHandlerExPar : public BaseCallbackHandler { public: typedef void (CB::*Func)(ParType); private: CB *pCBClass; ParType par; Func CBFunc; public: virtual void DoCall(class Element *pElement) { ((pCBClass)->*CBFunc)(par); } CallbackHandlerExPar(CB *pTarget, Func rFunc, ParType par) : pCBClass(pTarget), par(par), CBFunc(rFunc) {} }; // callback with parameter coming from calling class template class BaseParCallbackHandler : public BaseCallbackHandler { protected: virtual void DoCall(class Element *pElement) {assert(false);} // no-par: Not to be called public: BaseParCallbackHandler() {} virtual void DoCall(ParType par) = 0; }; template class ParCallbackHandler : public BaseParCallbackHandler { public: typedef void (CB::*Func)(ParType par); private: CB *pCBClass; Func CBFunc; protected: // not to be called, but avoid warning for hiding base class functions using BaseParCallbackHandler::DoCall; public: virtual void DoCall(ParType par) { ((pCBClass)->*CBFunc)(par); } ParCallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {} }; // three facets for left/top, middle and right/bottom of an auto-sized bar struct DynBarFacet { C4Facet fctBegin, fctMiddle, fctEnd; void SetHorizontal(C4Surface &rBySfc, int iHeight=0, int iBorderWidth=0); void SetHorizontal(C4Facet &rByFct, int32_t iBorderWidth=0); void Clear() { fctBegin.Default(); fctMiddle.Default(); fctEnd.Default(); } }; // facets used to draw a scroll bar struct ScrollBarFacets { DynBarFacet barScroll; C4Facet fctScrollDTop, fctScrollPin, fctScrollDBottom; void Set(const C4Facet &rByFct, int32_t iPinIndex=0); void Clear() { barScroll.Clear(); fctScrollDTop.Default(); fctScrollPin.Default(); fctScrollDBottom.Default(); } }; // a generic gui-element class Element { private: StdStrBuf ToolTip; // MouseOver - status text bool is_immediate_tooltip; protected: Container *pParent; // owning container Element *pPrev, *pNext; // previous and next element of same container Window *pDragTarget; // target that is dragged around when the user drags this element int32_t iDragX, iDragY; // drag start pos bool fDragging; // if set, mouse is down on component and dragging enabled ContextHandler *pContextHandler; // context handler to be called upon context menu request public: bool fVisible; // if false, component (and subcomponents) are not drawn protected: C4Rect rcBounds; // element bounds virtual void Draw(C4TargetFacet &cgo) { DrawElement(cgo); } // draw this class (this + any contents) virtual void DrawElement(C4TargetFacet &cgo) { }; // draw element itself virtual void RemoveElement(Element *pChild); // clear ptrs virtual void UpdateSize(); // called when own size changed virtual void UpdatePos(); // called when own position changed 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 void DrawBar(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds void DrawVBar(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds void DrawHBarByVGfx(C4TargetFacet &cgo, DynBarFacet &rFacets); // draw horizontal gfx bar within element bounds, using gfx of vertical one void DrawHVBar(C4TargetFacet &cgo, DynBarFacet &rFacets, C4DrawTransform &trf, int32_t iMiddleLength); virtual bool IsOwnPtrElement() { return false; } // if true is returned, item will not be deleted when container is cleared virtual bool IsExternalDrawDialog() { return false; } virtual bool IsMenu() { return false; } virtual class DialogWindow* GetDialogWindow() { return nullptr; } // return DialogWindow if this element is a dialog // for listbox-selection by character input virtual bool CheckNameHotkey(const char *) { return false; } public: virtual Container *GetContainer() { return pParent; } // returns parent for elements; this for containers virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons virtual void MouseEnter(CMouse &rMouse) {}; // called when mouse cursor enters element region virtual void MouseLeave(CMouse &rMouse) {}; // called when mouse cursor leaves element region virtual void StartDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by element in MouseInput: start dragging virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: dragging process virtual void StopDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // called by mouse: mouse released after dragging process virtual bool OnHotkey(uint32_t cHotkey) { return false; } // return true when hotkey has been processed public: bool DoContext(); // open context menu if assigned public: Element(); // ctor virtual ~Element(); // dtor Container *GetParent() { return pParent; } // get owning container virtual class Dialog *GetDlg(); // return contained dialog virtual Screen *GetScreen(); // return contained screen virtual Control *IsFocusElement() { return nullptr; }; // return control to gain focus in search-cycle virtual void UpdateOwnPos() { }; // called when element bounds were changed externally void ScreenPos2ClientPos(int32_t &riX, int32_t &riY); // transform screen coordinates to element coordinates void ClientPos2ScreenPos(int32_t &riX, int32_t &riY); // transform element coordinates to screen coordinates void SetToolTip(const char *szNewTooltip, bool is_immediate = false); // update used tooltip const char *GetToolTip(); // return tooltip const char* (own or fallback to parent) const char *GetOwnToolTip() { return ToolTip.getData(); } // return tooltip const char*, without fallback to parent bool IsImmediateToolTip() const { return is_immediate_tooltip; } int32_t GetWidth() { return rcBounds.Wdt; } int32_t GetHeight() { return rcBounds.Hgt; } C4Rect &GetBounds() { return rcBounds; } void SetBounds(const C4Rect &rcNewBound) { rcBounds=rcNewBound; UpdatePos(); UpdateSize(); } virtual C4Rect &GetClientRect() { return rcBounds; } C4Rect GetContainedClientRect() { C4Rect rc=GetClientRect(); rc.x=rc.y=0; return rc; } Element *GetNext() const { return pNext; } Element *GetPrev() const { return pPrev; } virtual Element *GetFirstNestedElement(bool fBackwards) { return this; } virtual Element *GetFirstContained() { return nullptr; } bool IsInActiveDlg(bool fForKeyboard); virtual bool IsParentOf(Element *pEl) { return false; } // whether this is the parent container (directly or recursively) of the passed element 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 bool IsVisible(); virtual void SetVisibility(bool fToValue); virtual int32_t GetListItemTopSpacing() { return C4GUI_DefaultListSpacing; } virtual bool GetListItemTopSpacingBar() { return false; } void SetDragTarget(Window *pToWindow) { pDragTarget = pToWindow; } void SetContextHandler(ContextHandler *pNewHd) // takes over ptr { if (pContextHandler) pContextHandler->DeRef(); if ((pContextHandler = pNewHd)) pNewHd->Ref(); } virtual ContextHandler *GetContextHandler(); // get context handler to be used (own or parent) friend class Container; friend class TextWindow; friend class ListBox; }; // a simple text label on the screen class Label : public Element { protected: StdStrBuf sText; // label text DWORD dwFgClr; // text color int32_t x0, iAlign; // x-textstart-pos; horizontal alignment CStdFont *pFont; uint32_t cHotkey; // hotkey for this label bool fAutosize; bool fMarkup; Control *pClickFocusControl; // control that gets focus if the label is clicked or hotkey is pressed virtual void DrawElement(C4TargetFacet &cgo); // output label virtual void UpdateOwnPos(); virtual bool OnHotkey(uint32_t cHotkey); // focus control on correct hotkey virtual int32_t GetLeftIndent() { return 0; } public: 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 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 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse void SetText(const char *szToText, bool fAllowHotkey=true); // update text const char *GetText() { return sText.getData(); } // retrieve current text void SetClickFocusControl(Control *pToCtrl) { pClickFocusControl=pToCtrl; } void SetColor(DWORD dwToClr, bool fMakeReadableOnBlack=true) { dwFgClr = fMakeReadableOnBlack ? MakeColorReadableOnBlack(dwToClr) : dwToClr; } // update label color void SetX0(int32_t iToX0); void SetAutosize(bool fToVal) { fAutosize = fToVal; } }; // a label with some wood behind // used for captions class WoodenLabel : public Label { private: 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 // Time when the label text was changed last. nullptr if not initialized; set upon first drawing C4TimeMilliseconds tLastChangeTime; int32_t iScrollPos, iScrollDir; int32_t iRightIndent; protected: virtual void DrawElement(C4TargetFacet &cgo); // output label C4Facet fctIcon; // icon shown at left-side of label; if set, text is aligned to left virtual int32_t GetLeftIndent() { return fctIcon.Surface ? rcBounds.Hgt : 0; } int32_t GetRightIndent() const { return iRightIndent; } public: WoodenLabel(const char *szLblText, const C4Rect &rcBounds, DWORD dwFClr=0xffffffff, CStdFont *pFont=nullptr, int32_t iAlign=ACenter, bool fMarkup=true) // ctor : Label(szLblText, rcBounds, iAlign, dwFClr, pFont, true, true, fMarkup), iAutoScrollDelay(0), tLastChangeTime(C4TimeMilliseconds::Now()), iScrollPos(0), iScrollDir(0), iRightIndent(0) { SetAutosize(false); this->rcBounds=rcBounds; }// ctor - re-sets bounds after SetText static int32_t GetDefaultHeight(CStdFont *pUseFont=nullptr); void SetIcon(const C4Facet &rfctIcon); void SetAutoScrollTime(uint32_t tDelay) { iAutoScrollDelay=tDelay; ResetAutoScroll(); } void ResetAutoScroll(); void SetRightIndent(int32_t iNewIndent) { iRightIndent = iNewIndent; } }; // a multiline label with automated text clipping // used for display of log buffers class MultilineLabel : public Element { protected: C4LogBuffer Lines; bool fMarkup; protected: virtual void DrawElement(C4TargetFacet &cgo); // output label void UpdateHeight(); virtual void UpdateSize(); // update label height public: MultilineLabel(const C4Rect &rcBounds, int32_t iMaxLines, int32_t iMaxBuf, const char *szIndentChars, bool fAutoGrow, bool fMarkup); // ctor void AddLine(const char *szLine, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont); // add line of text void Clear(bool fDoUpdate); // clear lines friend class TextWindow; }; // a bar that show progress class ProgressBar : public Element { protected: int32_t iProgress, iMax; virtual void DrawElement(C4TargetFacet &cgo); // draw progress bar public: ProgressBar(C4Rect &rrcBounds, int32_t iMaxProgress=100) // progress bar ctor : Element(), iProgress(0), iMax(iMaxProgress) { rcBounds=rrcBounds; UpdatePos(); } void SetProgress(int32_t iToProgress) { iProgress = iToProgress; } }; // Auxiliary design gfx: a horizontal line class HorizontalLine : public Element { protected: uint32_t dwClr, dwShadowClr; virtual void DrawElement(C4TargetFacet &cgo); // draw horizontal line public: HorizontalLine(C4Rect &rrcBounds, uint32_t dwClr=0x000000, uint32_t dwShadowClr=0xaf7f7f7f) : Element(), dwClr(dwClr), dwShadowClr(dwShadowClr) { SetBounds(rrcBounds); } }; // picture displaying a FacetEx class Picture : public Element { protected: C4FacetSurface Facet; // picture facet bool fAspect; // preserve width/height-ratio when drawing bool fCustomDrawClr; // custom drawing color for clrbyowner-surfaces uint32_t dwDrawClr; bool fAnimate; // if true, the picture is animated. Whoaa! int32_t iPhaseTime, iAnimationPhase, iDelay; // current animation phase - undefined if not animated virtual void DrawElement(C4TargetFacet &cgo); // draw the image public: Picture(const C4Rect &rcBounds, bool fAspect); // ctor - does not load image const C4FacetSurface &GetFacet() const { return Facet; } // get picture facet C4FacetSurface &GetMFacet() { return Facet; } // get picture facet void SetFacet(const C4Facet &fct) { Facet.Clear(); Facet.Set(fct); } bool EnsureOwnSurface(); // create an own surface, if it's just a link void SetDrawColor(uint32_t dwToClr) { dwDrawClr = dwToClr; fCustomDrawClr = true; } void SetAnimated(bool fEnabled, int iDelay); // starts/stops cycling through all phases of the specified facet }; // picture displaying two facets class OverlayPicture : public Picture { protected: int iBorderSize; // border of overlay image if not zoomed C4Facet OverlayImage; // image to be displayed on top of the actual picture virtual void DrawElement(C4TargetFacet &cgo); // draw the image public: OverlayPicture(const C4Rect &rcBounds, bool fAspect, const C4Facet &rOverlayImage, int iBorderSize); // ctor - does not load image }; // icon indices enum { Ico_Extended = 0x100, // icon index offset for extended icons Ico_Controller = 0x200, }; enum Icons { Ico_Empty = -2, // for context menus only Ico_None = -1, Ico_Clonk = 0, Ico_Notify = 1, Ico_Wait = 2, Ico_NetWait = 3, Ico_Host = 4, Ico_Client = 5, Ico_UnknownClient = 6, Ico_UnknownPlayer = 7, Ico_ObserverClient = 8, Ico_Player = 9, Ico_Resource = 10, Ico_Error = 11, Ico_SavegamePlayer = 12, Ico_Save = 13, Ico_Active = 14, Ico_Options = 14, Ico_Editor = 14, Ico_Inactive = 15, Ico_Kick = 16, Ico_Loading = 17, Ico_Confirm = 18, Ico_Team = 19, Ico_AddPlr = 20, Ico_Record = 21, Ico_Chart = 21, Ico_Gfx = 22, Ico_Sound = 23, Ico_Keyboard = 24, Ico_Gamepad = 25, Ico_MouseOff = 26, Ico_MouseOn = 27, Ico_Help = 28, Ico_Definition = 29, Ico_GameRunning = 30, Ico_Lobby = 31, Ico_RuntimeJoin = 32, Ico_Exit = 33, Ico_Close = 34, Ico_Rank1 = 35, Ico_Rank2 = 36, Ico_Rank3 = 37, Ico_Rank4 = 38, Ico_Rank5 = 39, Ico_Rank6 = 40, Ico_Rank7 = 41, Ico_Rank8 = 42, Ico_Rank9 = 43, Ico_OfficialServer = 44, Ico_Surrender = 45, Ico_MeleeLeague = 46, Ico_Ready = 47, Ico_Star = 48, Ico_Disconnect = 49, Ico_View = 50, // Ico_RegJoinOnly = 51, Ico_Ignored = 52, Ico_Ex_RecordOff = Ico_Extended + 0, Ico_Ex_RecordOn = Ico_Extended + 1, // Ico_Ex_FairCrew = Ico_Extended + 2, Ico_Ex_NormalCrew = Ico_Extended + 3, Ico_Ex_LeagueOff = Ico_Extended + 4, Ico_Ex_LeagueOn = Ico_Extended + 5, Ico_Ex_InternetOff = Ico_Extended + 6, Ico_Ex_InternetOn = Ico_Extended + 7, Ico_Ex_League = Ico_Extended + 8, // Ico_Ex_FairCrewGray = Ico_Extended + 9, Ico_Ex_NormalCrewGray= Ico_Extended + 10, Ico_Ex_Locked = Ico_Extended + 11, Ico_Ex_Unlocked = Ico_Extended + 12, Ico_Ex_LockedFrontal = Ico_Extended + 13, Ico_Ex_Update = Ico_Extended + 14, Ico_Ex_Chat = Ico_Extended + 15, Ico_Ex_GameList = Ico_Extended + 16, Ico_Ex_Comment = Ico_Extended + 17, Ico_Controller_A = Ico_Controller + 0, Ico_Controller_B = Ico_Controller + 3, Ico_Controller_X = Ico_Controller + 17, Ico_Controller_Y = Ico_Controller + 18, Ico_Controller_Back = Ico_Controller + 1, Ico_Controller_Start = Ico_Controller + 16, Ico_Controller_Dpad = Ico_Controller + 6, Ico_Controller_DpadLeft = Ico_Controller + 5, Ico_Controller_DpadRight = Ico_Controller + 7, Ico_Controller_DpadDown = Ico_Controller + 4, Ico_Controller_DpadUp = Ico_Controller + 8, Ico_Controller_LeftShoulder = Ico_Controller + 9, Ico_Controller_RightShoulder = Ico_Controller + 12, Ico_Controller_LeftTrigger = Ico_Controller + 11, Ico_Controller_RightTrigger = Ico_Controller + 14, Ico_Controller_LeftStick = Ico_Controller + 10, Ico_Controller_RightStick = Ico_Controller + 13, }; // cute, litte, useless thingy class Icon : public Picture { public: Icon(const C4Rect &rcBounds, Icons icoIconIndex); void SetIcon(Icons icoNewIconIndex); static C4Facet GetIconFacet(Icons icoIconIndex); }; // a collection of gui-elements class Container : public Element { protected: Element *pFirst, *pLast; // contained elements virtual void Draw(C4TargetFacet &cgo); // draw all elements virtual void ElementSizeChanged(Element *pOfElement) { } // called when an element size is changed virtual void ElementPosChanged(Element *pOfElement) { } // called when an element position is changed virtual void AfterElementRemoval() { if (pParent) pParent->AfterElementRemoval(); } // called by ScrollWindow to parent after an element has been removed virtual bool OnHotkey(uint32_t cHotkey); // check all contained elements for hotkey public: Container(); // ctor ~Container(); // dtor void Clear(); // delete all child elements void ClearChildren(); // delete all child elements virtual void RemoveElement(Element *pChild); // remove child element from container void MakeLastElement(Element *pChild); // resort to the end of the list void AddElement(Element *pChild); // add child element to container void ReaddElement(Element *pChild); // resort child element to end of list void InsertElement(Element *pChild, Element *pInsertBefore); // add child element to container, ordered directly before given, other element Element *GetNextNestedElement(Element *pPrevElement, bool fBackwards); // get next element after given, applying recursion virtual Element *GetFirstContained() { return pFirst; } virtual Element *GetLastContained() { return pLast; } virtual Element *GetFirstNestedElement(bool fBackwards); class Iterator { private: Element *current; public: Iterator(Element *element = nullptr) : current(element) { } Element * operator*() const { return current; } Element * operator->() const { return current; } void operator++() { current = current->GetNext(); }; void operator++(int) { operator++(); } bool operator==(const Iterator & other) const { return (current == other.current); } bool operator!=(const Iterator & other) const { return !(*this == other); } }; class ReverseIterator { private: Element *current; public: ReverseIterator(Element *element = nullptr) : current(element) { } Element * operator*() const { return current; } Element * operator->() const { return current; } void operator++() { current = current->GetPrev(); }; void operator++(int) { operator++(); } bool operator==(const ReverseIterator & other) const { return (current == other.current); } bool operator!=(const ReverseIterator & other) const { return !(*this == other); } }; // provide C++-style iterator interface Iterator begin() { return Iterator(pFirst); } Iterator end() { return Iterator(nullptr); } ReverseIterator rbegin() { return ReverseIterator(pLast); } ReverseIterator rend() { return ReverseIterator(nullptr); } Element *GetFirst() { return pFirst; } Element *GetLast() { return pLast; } virtual Container *GetContainer() { return this; } // returns parent for elements; this for containers Element *GetElementByIndex(int32_t i); // get indexed child element int32_t GetElementCount(); virtual void SetVisibility(bool fToValue); virtual bool IsFocused(Control *pCtrl) { return pParent ? pParent->IsFocused(pCtrl) : false; } 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 virtual bool IsParentOf(Element *pEl); // whether this is the parent container (directly or recursively) of the passed element virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) {} // apply child drawing offset virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) {} // subtract child drawing offset friend class Element; friend class ScrollWindow; }; // a rectangled control that contains other elements class Window : public Container { protected: C4Rect rcClientRect; // area for contained elements virtual void Draw(C4TargetFacet &cgo); // draw this window public: Window(); // ctor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons void SetPos(int32_t iXPos, int32_t iYPos) { rcBounds.x=iXPos; rcBounds.y=iYPos; UpdatePos(); } virtual void UpdateOwnPos(); // update client rect virtual C4Rect &GetClientRect() { return rcClientRect; } virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) { riX -= rcClientRect.x; riY -= rcClientRect.y; } virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) { riX += rcClientRect.x; riY += rcClientRect.y; } virtual bool IsComponentOutsideClientArea() { return false; } // if set, drawing routine of subcomponents will clip to Bounds rather than to ClientRect // get margins from bounds to client rect virtual int32_t GetMarginTop() { return 0; } virtual int32_t GetMarginLeft() { return 0; } virtual int32_t GetMarginRight() { return 0; } virtual int32_t GetMarginBottom() { return 0; } }; // a scroll bar class ScrollBar : public Element { protected: bool fScrolling; // if set, scrolling is currently enabled bool fAutoHide; // if set, bar is made invisible if scrolling is not possible anyway int32_t iScrollThumbSize; // height(/width) of scroll thumb int32_t iScrollPos; // y(/x) offset of scroll thumb bool fTopDown, fBottomDown; // whether scrolling buttons are pressed bool fHorizontal; // if set, the scroll bar is horizontal instead of vertical int32_t iCBMaxRange; // range for callback class ScrollWindow *pScrollWindow; // associated scrolled window - may be 0 for callback scrollbars BaseParCallbackHandler *pScrollCallback; // callback called when scroll pos changes ScrollBarFacets *pCustomGfx; void Update(); // update scroll bar according to window void OnPosChanged(); // update window according to scroll bar, and/or do callbacks virtual void DrawElement(C4TargetFacet &cgo); // draw scroll bar // suppress scrolling pin for very narrow menus bool HasPin() { if (fHorizontal) return rcBounds.Wdt > (2*C4GUI_ScrollArrowWdt + C4GUI_ScrollThumbWdt); else return rcBounds.Hgt > (2*C4GUI_ScrollArrowHgt + C4GUI_ScrollThumbHgt); } int32_t GetMaxScroll() { if (fHorizontal) return HasPin() ? GetBounds().Wdt - 2*C4GUI_ScrollArrowWdt - iScrollThumbSize : 100; else return HasPin() ? GetBounds().Hgt - 2*C4GUI_ScrollArrowHgt - iScrollThumbSize : 100; } int32_t GetScrollByPos(int32_t iX, int32_t iY) { return Clamp((fHorizontal ? iX-C4GUI_ScrollArrowWdt : iY-C4GUI_ScrollArrowHgt)-iScrollThumbSize/2, 0, GetMaxScroll()); } bool IsScrolling() { return fScrolling; } public: ScrollBar(C4Rect &rcBounds, ScrollWindow *pWin); // ctor for scroll window ScrollBar(C4Rect &rcBounds, bool fHorizontal, BaseParCallbackHandler *pCB, int32_t iCBMaxRange=256); // ctor for callback ~ScrollBar(); // dtor // mouse handling virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // dragging: allow dragging of thumb virtual void MouseLeave(CMouse &rMouse); // mouse leaves with button down: reset down state of buttons // change style void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoHide) { pCustomGfx = pToGfx; this->fAutoHide=fAutoHide; } // change scroll pos in a [0, iCBMaxRange-1] scale void SetScrollPos(int32_t iToPos) { iScrollPos = iToPos * GetMaxScroll() / (iCBMaxRange-1); } friend class ScrollWindow; friend class ::C4ScriptGuiWindow; }; // a window that can be scrolled class ScrollWindow : public Window { protected: ScrollBar *pScrollBar; // vertical scroll bar associated with the window int32_t iScrollY; // vertical scroll pos int32_t iClientHeight; // client rect height bool fHasBar; int32_t iFrozen; // if >0, no scrolling updates are done (used during window refill) // pass element updates through to parent window virtual void ElementSizeChanged(Element *pOfElement) // called when an element size is changed { Window::ElementSizeChanged(pOfElement); if (pParent) pParent->ElementSizeChanged(pOfElement); } virtual void ElementPosChanged(Element *pOfElement) // called when an element position is changed { Window::ElementPosChanged(pOfElement); if (pParent) pParent->ElementPosChanged(pOfElement); } public: ScrollWindow(Window *pParentWindow); // create scroll window in client area of window ~ScrollWindow() { if (pScrollBar) pScrollBar->pScrollWindow = nullptr; } virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); virtual bool IsComponentOutsideClientArea() { return true; } // always clip drawing routine of subcomponents to Bounds void Update(); // update client rect and scroll bar according to window virtual void UpdateOwnPos(); void Freeze() { ++iFrozen; } void UnFreeze() { if (!--iFrozen) Update(); } bool IsFrozen() const { return !!iFrozen; } void SetClientHeight(int32_t iToHgt) // set new client height { iClientHeight=iToHgt; Update(); } // change style void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoScroll) { if (pScrollBar) pScrollBar->SetDecoration(pToGfx, fAutoScroll); } void SetScroll(int32_t iToScroll); // sets new scrolling; does not update scroll bar void ScrollToBottom(); // set scrolling to bottom range; updates scroll bar void ScrollPages(int iPageCount); // scroll down by multiples of visible height; updates scroll bar void ScrollBy(int iAmount); // scroll down by vertical pixel amount; updates scroll bar void ScrollRangeInView(int32_t iY, int32_t iHgt); // sets scrolling so range is in view; updates scroll bar bool IsRangeInView(int32_t iY, int32_t iHgt); // returns whether scrolling range is in view int32_t GetScrollY() { return iScrollY; } void SetScrollBarEnabled(bool fToVal, bool noAutomaticPositioning = false); bool IsScrollBarEnabled() { return fHasBar; } bool IsScrollingActive() { return fHasBar && pScrollBar && pScrollBar->IsScrolling(); } bool IsScrollingNecessary() { return iClientHeight > rcBounds.Hgt; } friend class ScrollBar; }; // a collection of components class GroupBox : public Window { private: StdStrBuf sTitle; CStdFont *pFont; uint32_t dwFrameClr, dwTitleClr, dwBackClr; int32_t iMargin; CStdFont *GetTitleFont() const; public: GroupBox(C4Rect &rtBounds) : Window(), pFont(nullptr), dwFrameClr(0u), dwTitleClr(C4GUI_CaptionFontClr), dwBackClr(0xffffffff), iMargin(4) { // init client rect SetBounds(rtBounds); } // ctor void SetFont(CStdFont *pToFont) { pFont = pToFont; } void SetColors(uint32_t dwFrameClr, uint32_t dwTitleClr, uint32_t dwBackClr=0xffffffff) { this->dwFrameClr = dwFrameClr; this->dwTitleClr = dwTitleClr; this->dwBackClr = dwBackClr; } void SetTitle(const char *szToTitle) { if (szToTitle && *szToTitle) sTitle.Copy(szToTitle); else sTitle.Clear(); UpdateOwnPos(); } void SetMargin(int32_t iNewMargin) { iMargin = iNewMargin; UpdateOwnPos(); } bool HasTitle() const { return !!sTitle.getLength(); } virtual void DrawElement(C4TargetFacet &cgo); // draw frame virtual int32_t GetMarginTop() { return HasTitle() ? iMargin + GetTitleFont()->GetLineHeight() : iMargin; } virtual int32_t GetMarginLeft() { return iMargin; } virtual int32_t GetMarginRight() { return iMargin; } virtual int32_t GetMarginBottom() { return iMargin; } }; // a drawing area class PaintBox : public Window { protected: C4FacetSurface fctPaint; virtual void DrawElement(C4TargetFacet &cgo); // draw what has been painted public: PaintBox(C4Rect &rtBounds, int32_t iSfcWdt=-1, int32_t iSfcHgt=-1); // ctor ~PaintBox(); // dtor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse }; // a control that may have focus class Control : public Window { private: class C4KeyBinding *pKeyContext; protected: virtual bool CharIn(const char *) { return false; } // input: character key pressed - should return false for none-character-inputs void DisableFocus(); // called when control gets disabled: Make sure it loses focus virtual bool IsFocusOnClick() { return true; } // defaultly, controls get focused on left-down virtual Control *IsFocusElement() { return this; }; // this control can gain focus virtual void OnGetFocus(bool fByMouse) {}; // callback when control gains focus virtual void OnLooseFocus() {}; // callback when control looses focus bool KeyContext() { return DoContext(); } public: Control(const C4Rect &rtBounds); // ctor ~Control(); virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. left-click sets focus bool HasFocus() { return pParent && pParent->IsFocused(this); } bool HasDrawFocus(); friend class Dialog; friend class ListBox; }; // generic callback-functions to the dialog template class DlgCallback { public: typedef void (CallbackDlg::*Func)(Control *pFromControl); typedef bool (CallbackDlg::*BoolFunc)(Control *pFromControl); typedef bool (CallbackDlg::*Bool2Func)(Control *pFromControl, bool fBool, bool fBool2); typedef ContextMenu *(CallbackDlg::*ContextFunc)(Element *pFromElement, int32_t iX, int32_t iY); typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement); }; // multi-param callback-functions to the dialog template class DlgCallbackEx { public: typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement, TEx Extra); }; // a button. may be pressed. class Button : public Control { private: class C4KeyBinding *pKeyButton; DynBarFacet *pCustomGfx, *pCustomGfxDown; protected: StdStrBuf sText; // button label CStdFont *pCustomFont; // custom font (if assigned) DWORD dwCustomFontClr; // text font color (valid only if pCustomFont) bool fDown; // if set, button is currently held down bool fMouseOver; // if set, the mouse hovers over the button uint32_t cHotkey; // hotkey for this button bool fEnabled; virtual bool IsFocusOnClick() { return false; } // buttons don't get focus on click (for easier input, e.g. in chatbox) virtual void DrawElement(C4TargetFacet &cgo); // draw dlg bg virtual void OnPress(); // called when button is pressed bool KeyButtonDown(); bool KeyButtonUp(); void SetDown(); // mark down and play sound void SetUp(bool fPress); // mark up and play sound virtual bool OnHotkey(uint32_t cHotkey); // press btn on correct hotkey public: Button(const char *szBtnText, const C4Rect &rtBounds); // ctor ~Button(); // dtor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons virtual void MouseEnter(CMouse &rMouse); // mouse re-enters with button down: set button down virtual void MouseLeave(CMouse &rMouse); // mouse leaves with button down: reset down state void SetText(const char *szToText); // update button text (and hotkey) void SetCustomGraphics(DynBarFacet *pCustomGfx, DynBarFacet *pCustomGfxDown) { this->pCustomGfx = pCustomGfx; this->pCustomGfxDown = pCustomGfxDown; } void SetEnabled(bool fToVal) { fEnabled=fToVal; if (!fEnabled) fDown=false; } void SetFont(CStdFont *pFont, DWORD dwCustomFontClr=C4GUI_CaptionFontClr) { this->pCustomFont = pFont; this->dwCustomFontClr=dwCustomFontClr; } }; // button using icon image class IconButton : public Button { protected: C4Facet fctIcon; uint32_t dwClr; bool fHasClr; bool fHighlight; // if true, the button is highlighted permanently virtual void DrawElement(C4TargetFacet &cgo); // draw icon and highlight if necessary public: IconButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey='\0', const char *tooltip_text=nullptr); // ctor void SetIcon(Icons eUseIcon); void SetFacet(const C4Facet &rCpy, uint32_t dwClr=0u) { fctIcon = rCpy; } void SetColor(uint32_t dwClr) { fHasClr=true; this->dwClr=dwClr; } void SetHighlight(bool fToVal) { fHighlight=fToVal; } }; // button using arrow image class ArrowButton : public Button { public: enum ArrowFct { Left=0, Right=1, Down=2 }; protected: ArrowFct eDir; virtual void DrawElement(C4TargetFacet &cgo); // draw arrow; highlight and down if necessary public: ArrowButton(ArrowFct eDir, const C4Rect &rtBounds, char cHotkey=0); // ctor static int32_t GetDefaultWidth(); static int32_t GetDefaultHeight(); }; // button using facets for highlight class FacetButton : public Button { protected: C4Facet fctBase, fctHighlight; uint32_t dwTextClrInact, dwTextClrAct; // text colors for inactive/active button FLOAT_RECT rcfDrawBounds; // drawing bounds // title drawing parameters int32_t iTxtOffX, iTxtOffY; uint8_t byTxtAlign; // ALeft, ACenter or ARight CStdFont *pFont; float fFontZoom; virtual void DrawElement(C4TargetFacet &cgo); // draw base facet or highlight facet if necessary public: FacetButton(const C4Facet &rBaseFct, const C4Facet &rHighlightFct, const FLOAT_RECT &rtfBounds, char cHotkey); // ctor void SetTextColors(uint32_t dwClrInact, uint32_t dwClrAct) { dwTextClrInact = dwClrInact; dwTextClrAct = dwClrAct; } void SetTextPos(int32_t iOffX, int32_t iOffY, uint8_t byAlign=ACenter) { iTxtOffX=iOffX; iTxtOffY=iOffY; byTxtAlign=byAlign; } void SetTextFont(CStdFont *pFont, float fFontZoom=1.0f) { this->pFont=pFont; this->fFontZoom=fFontZoom; } }; // a button doing some callback... template class CallbackButton : public Base { protected: CallbackDlg *pCB; typename DlgCallback::Func pCallbackFn; // callback function virtual void OnPress() { if (pCallbackFn) { CallbackDlg *pC=pCB; if (!pC) if (!(pC=reinterpret_cast(Base::GetDlg()))) return; (pC->*pCallbackFn)(this); } } public: CallbackButton(ArrowButton::ArrowFct eDir, const C4Rect &rtBounds, typename DlgCallback::Func pFn, CallbackDlg *pCB=nullptr) // ctor : Base(eDir, rtBounds, 0), pCB(pCB), pCallbackFn(pFn) { } CallbackButton(const char *szBtnText, C4Rect &rtBounds, typename DlgCallback::Func pFn, CallbackDlg *pCB=nullptr) // ctor : Base(szBtnText, rtBounds), pCB(pCB), pCallbackFn(pFn) { } CallbackButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, typename DlgCallback::Func pFn, CallbackDlg *pCB=nullptr) // ctor : Base(eUseIcon, rtBounds, cHotkey), pCB(pCB), pCallbackFn(pFn) { } CallbackButton(Icons eUseIcon, const C4Rect &rtBounds, const char *tooltip_text, typename DlgCallback::Func pFn, CallbackDlg *pCB = nullptr) // ctor : Base(eUseIcon, rtBounds, '\0', tooltip_text), pCB(pCB), pCallbackFn(pFn) { } CallbackButton(int32_t iID, const C4Rect &rtBounds, char cHotkey, typename DlgCallback::Func pFn, CallbackDlg *pCB=nullptr) // ctor : Base(iID, rtBounds, cHotkey), pCB(pCB), pCallbackFn(pFn) { } }; // a button doing some callback to any class template class CallbackButtonEx : public Base { protected: typedef CallbackDlg * CallbackDlgPointer; CallbackDlgPointer pCBTarget; // callback target typename DlgCallback::Func pCallbackFn; // callback function virtual void OnPress() { (pCBTarget->*pCallbackFn)(this); } public: CallbackButtonEx(const char *szBtnText, const C4Rect &rtBounds, CallbackDlgPointer pCBTarget, typename DlgCallback::Func pFn) // ctor : Base(szBtnText, rtBounds), pCBTarget(pCBTarget), pCallbackFn(pFn) { } CallbackButtonEx(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback::Func pFn) // ctor : Base(eUseIcon, rtBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) { } CallbackButtonEx(const C4Facet &fctBase, const C4Facet &fctHighlight, const FLOAT_RECT &rtfBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback::Func pFn) // ctor : Base(fctBase, fctHighlight, rtfBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) { } }; // an edit control to type text in class Edit : public Control { public: Edit(const C4Rect &rtBounds, bool fFocusEdit=false); // ctor ~Edit(); enum InputResult // action to be taken when text is confirmed with enter { IR_None=0, // do nothing and continue pasting IR_CloseDlg, // stop any pastes and close parent dialog successfully IR_CloseEdit, // stop any pastes and remove this control IR_Abort // do nothing and stop any pastes }; private: enum CursorOperation { COP_BACK, COP_DELETE, COP_LEFT, COP_RIGHT, COP_HOME, COP_END }; static const char *CursorRepresentation; bool KeyCursorOp(const C4KeyCodeEx &key, const CursorOperation &op); bool KeyEnter(); bool KeyCopy() { Copy(); return true; } bool KeyPaste() { Paste(); return true; } bool KeyCut() { Cut(); return true; } bool KeySelectAll() { SelectAll(); return true; } class C4KeyBinding *RegisterCursorOp(CursorOperation op, C4KeyCode key, const char *szName, C4CustomKey::Priority eKeyPrio); class C4KeyBinding *pKeyCursorBack, *pKeyCursorDel, *pKeyCursorLeft, *pKeyCursorRight, *pKeyCursorHome, *pKeyCursorEnd, *pKeyEnter, *pKeyCopy, *pKeyPaste, *pKeyCut, *pKeySelAll; protected: // context callbacks ContextMenu *OnContext(C4GUI::Element *pListItem, int32_t iX, int32_t iY); void OnCtxCopy(C4GUI::Element *pThis) { Copy(); }; void OnCtxPaste(C4GUI::Element *pThis) { Paste(); }; void OnCtxCut(C4GUI::Element *pThis) { Cut(); }; void OnCtxClear(C4GUI::Element *pThis) { DeleteSelection(); }; void OnCtxSelAll(C4GUI::Element *pThis) { SelectAll(); }; private: void Deselect(); // clear selection range void ClearText(); // remove all the text public: bool InsertText(const char *szText, bool fUser); // insert text at cursor pos (returns whether all text could be inserted) void DeleteSelection(); // deletes the selected text. Adjust cursor position if necessary bool SetText(const char *szText, bool fUser) { ClearText(); return InsertText(szText, fUser); } void SetPasswordMask(char cNewPasswordMask) { cPasswordMask = cNewPasswordMask; } // mask edit box contents using the given character private: int32_t GetCharPos(int32_t iControlXPos); // get character index of pixel position; always resides within current text length void EnsureBufferSize(int32_t iMinBufferSize); // ensure buffer has desired size void ScrollCursorInView(); // ensure cursor pos is visible in edit control bool DoFinishInput(bool fPasting, bool fPastingMore); // do OnFinishInput callback and process result - returns whether pasting operation should be continued bool Copy(); bool Cut(); bool Paste(); // clipboard operations protected: CStdFont *pFont; // font for edit char *Text; // edit text uint32_t dwBGClr, dwFontClr, dwBorderColor; // drawing colors for edit box int32_t iBufferSize; // size of current buffer int32_t iCursorPos; // cursor position: char, before which the cursor is located int32_t iSelectionStart, iSelectionEnd; // selection range (start may be larger than end) int32_t iMaxTextLength; // maximum number of characters to be input here C4TimeMilliseconds tLastInputTime; // time of last input (for cursor flashing) int32_t iXScroll; // horizontal scrolling char cPasswordMask; // character to be used for masking the contents. 0 for none. bool fLeftBtnDown; // flag whether left mouse button is down or not virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); // dragging: allow text selection outside the component virtual bool IsFocusOnClick() { return true; } // edit fields do get focus on click virtual void OnGetFocus(bool fByMouse); // edit control gets focus virtual void OnLooseFocus(); // edit control looses focus virtual void DrawElement(C4TargetFacet &cgo); // draw edit control // called when user presses enter in single-line edit control - closes the current dialog virtual InputResult OnFinishInput(bool fPasting, bool fPastingMore) { return IR_CloseDlg; } virtual void OnAbortInput() {} // get margins from bounds to client rect virtual int32_t GetMarginTop() { return 2; } virtual int32_t GetMarginLeft() { return 4; } virtual int32_t GetMarginRight() { return 4; } virtual int32_t GetMarginBottom() { return 2; } public: virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons const char *GetText() { return Text; } void SelectAll(); // select all the text static int32_t GetDefaultEditHeight(); static int32_t GetCustomEditHeight(CStdFont *pUseFont); bool GetCurrentWord(char *szTargetBuf, int32_t iMaxTargetBufLen); // get word before cursor pos (for nick completion) // layout void SetFont(CStdFont *pToFont) { pFont=pToFont; ScrollCursorInView(); } void SetColors(uint32_t dwNewBGClr, uint32_t dwNewFontClr, uint32_t dwNewBorderColor) { dwBGClr = dwNewBGClr; dwFontClr = dwNewFontClr; dwBorderColor = dwNewBorderColor; } void SetMaxText(int32_t iTo) { iMaxTextLength = iTo; } }; // an edit doing some callback template class CallbackEdit : public Edit { private: CallbackCtrl *pCBCtrl; protected: typedef InputResult (CallbackCtrl::*CBFunc)(Edit *, bool, bool); typedef void (CallbackCtrl::*CBAbortFunc)(); CBFunc pCBFunc; CBAbortFunc pCBAbortFunc; virtual InputResult OnFinishInput(bool fPasting, bool fPastingMore) { if (pCBFunc && pCBCtrl) return (pCBCtrl->*pCBFunc)(this, fPasting, fPastingMore); else return IR_CloseDlg; } virtual void OnAbortInput() { if (pCBAbortFunc && pCBCtrl) (pCBCtrl->*pCBAbortFunc)(); } public: CallbackEdit(const C4Rect &rtBounds, CallbackCtrl * pCBCtrl, CBFunc pCBFunc, CBAbortFunc pCBAbortFunc=nullptr) // ctor : Edit(rtBounds), pCBCtrl(pCBCtrl), pCBFunc(pCBFunc), pCBAbortFunc(pCBAbortFunc) { } }; // an edit control that renames a label - some less decoration; abort on Escape and focus loss class RenameEdit : public Edit { private: C4KeyBinding *pKeyAbort; // key bindings bool fFinishing; // set during deletion process Label *pForLabel; // label that is being renamed Control *pPrevFocusCtrl; // previous focus element to be restored after rename public: enum RenameResult { RR_Invalid=0, // rename not accepted; continue editing RR_Accepted, // rename accepted; delete control RR_Deleted // control deleted - leave everything }; public: RenameEdit(Label *pLabel); // ctor - construct for label; add element; set focus virtual ~RenameEdit(); void Abort(); private: void FinishRename(); // renaming aborted or finished - remove this element and restore label protected: bool KeyAbort() { Abort(); return true; } virtual InputResult OnFinishInput(bool fPasting, bool fPastingMore); // forward last input to OnOKRename virtual void OnLooseFocus(); // callback when control looses focus: OK input virtual void OnCancelRename() {} // renaming was aborted virtual RenameResult OnOKRename(const char *szNewName) = 0; // rename performed - return whether name was accepted }; template class CallbackRenameEdit : public RenameEdit { public: protected: typedef void (CallbackDlg::*CBCancelFunc)(ParType); typedef RenameResult (CallbackDlg::*CBOKFunc)(ParType, const char *); CBCancelFunc pCBCancelFunc; CBOKFunc pCBOKFunc; CallbackDlg *pDlg; ParType par; virtual void OnCancelRename() { if (pDlg && pCBCancelFunc) (pDlg->*pCBCancelFunc)(par); } virtual RenameResult OnOKRename(const char *szNewName) { return (pDlg && pCBOKFunc) ? (pDlg->*pCBOKFunc)(par, szNewName) : RR_Accepted; } public: CallbackRenameEdit(Label *pForLabel, CallbackDlg *pDlg, const ParType &par, CBOKFunc pCBOKFunc, CBCancelFunc pCBCancelFunc) // ctor : RenameEdit(pForLabel), pCBCancelFunc(pCBCancelFunc), pCBOKFunc(pCBOKFunc), pDlg(pDlg), par(par) { } }; // editbox below descriptive label sharing one window for common tooltip class LabeledEdit : public C4GUI::Window { public: LabeledEdit(const C4Rect &rcBounds, const char *szName, bool fMultiline, const char *szPrefText=nullptr, CStdFont *pUseFont=nullptr, uint32_t dwTextClr = C4GUI_CaptionFontClr); private: C4GUI::Edit *pEdit; public: const char *GetText() const { return pEdit->GetText(); } C4GUI::Edit *GetEdit() const { return pEdit; } static bool GetControlSize(int *piWdt, int *piHgt, const char *szForText, CStdFont *pForFont, bool fMultiline); }; // checkbox with a text label right of it class CheckBox : public Control { private: bool fChecked; class C4KeyBinding *pKeyCheck; StdStrBuf sCaption; bool fMouseOn; BaseCallbackHandler *pCBHandler; // callback handler called if check state changes bool fEnabled; CStdFont *pFont; uint32_t dwEnabledClr, dwDisabledClr; uint32_t cHotkey; public: CheckBox(const C4Rect &rtBounds, const char *szCaption, bool fChecked); // ctor ~CheckBox(); private: bool KeyCheck() { ToggleCheck(true); return true; } public: void ToggleCheck(bool fByUser); // check on/off; do callback protected: virtual void UpdateOwnPos(); virtual bool IsFocusOnClick() { return false; } // just check/uncheck on click; do not gain keyboard focus as well virtual Control *IsFocusElement() { return fEnabled ? Control::IsFocusElement() : nullptr; }; // this control can gain focus if enabled virtual void DrawElement(C4TargetFacet &cgo); // draw checkbox virtual bool OnHotkey(uint32_t cHotkey); // return true when hotkey has been processed public: virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons virtual void MouseEnter(CMouse &rMouse); virtual void MouseLeave(CMouse &rMouse); void SetChecked(bool fToVal) { fChecked = fToVal; } // set w/o callback bool GetChecked() const { return fChecked; } void SetOnChecked(BaseCallbackHandler *pCB); void SetEnabled(bool fToVal) { if (!(fEnabled=fToVal)) DisableFocus(); } const char *GetText() { return sCaption.getData(); } void SetFont(CStdFont *pFont, uint32_t dwEnabledClr, uint32_t dwDisabledClr) { this->pFont=pFont; this->dwEnabledClr=dwEnabledClr; this->dwDisabledClr=dwDisabledClr; } static bool GetStandardCheckBoxSize(int *piWdt, int *piHgt, const char *szForCaptionText, CStdFont *pUseFont); // get needed size to construct a checkbox }; // a vertical list of elements class ListBox : public Control { private: class C4KeyBinding *pKeyContext, *pKeyUp, *pKeyDown, *pKeyPageUp, *pKeyPageDown, *pKeyHome, *pKeyEnd, *pKeyActivate, *pKeyLeft, *pKeyRight; bool KeyContext(); bool KeyUp(); bool KeyDown(); bool KeyLeft(); bool KeyRight(); bool KeyPageUp(); bool KeyPageDown(); bool KeyHome(); bool KeyEnd(); bool KeyActivate(); protected: int32_t iMultiColItemWidth; // if nonzero, the listbox is multicolumn and the column count depends on how many items fit in int32_t iColCount; // number of columns (usually 1) ScrollWindow *pClientWindow; // client scrolling window Element *pSelectedItem; // selected list item BaseCallbackHandler *pSelectionChangeHandler, *pSelectionDblClickHandler; bool fDrawBackground; // whether darker background is to be drawn bool fDrawBorder; // whether 3D frame around box shall be drawn or nay bool fSelectionDisabled; // if set, no entries can be selected virtual void DrawElement(C4TargetFacet &cgo); // draw listbox virtual bool IsFocused(Control *pCtrl) { // subcontrol also counts as focused if the list has focus and the subcontrol is selected return Control::IsFocused(pCtrl) || (HasFocus() && pSelectedItem == pCtrl); } virtual bool IsFocusOnClick() { return true; } // list boxes do get focus on click virtual Control *IsFocusElement() { return this; }; // this control can gain focus virtual void OnGetFocus(bool fByMouse); // callback when control gains focus - select a list item if none are selected virtual bool CharIn(const char * c); // character input for direct list element selection virtual void AfterElementRemoval() { Container::AfterElementRemoval(); UpdateElementPositions(); } // called by ScrollWindow to parent after an element has been removed void UpdateColumnCount(); public: ListBox(const C4Rect &rtBounds, int32_t iMultiColItemWidth=0); // ctor virtual ~ListBox(); // dtor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons virtual void RemoveElement(Element *pChild); // remove child component bool AddElement(Element *pChild, int32_t iIndent=0); // add element and adjust its pos bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0); // insert element and adjust its pos virtual void ElementSizeChanged(Element *pOfElement); // called when an element size is changed virtual void ElementPosChanged(Element *pOfElement); // called when an element position is changed int32_t GetItemWidth() { return iMultiColItemWidth ? iMultiColItemWidth : pClientWindow->GetClientRect().Wdt; } void SelectionChanged(bool fByUser); // pSelectedItem changed: sound, tooltip, etc. void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler) // update selection change handler { if (pSelectionChangeHandler) pSelectionChangeHandler->DeRef(); if ((pSelectionChangeHandler = pToHandler)) pToHandler->Ref(); } void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler) // update selection doubleclick handler { if (pSelectionDblClickHandler) pSelectionDblClickHandler->DeRef(); if ((pSelectionDblClickHandler = pToHandler)) pSelectionDblClickHandler->Ref(); } void ScrollToBottom() // set scrolling to bottom range { if (pClientWindow) pClientWindow->ScrollToBottom(); } void ScrollItemInView(Element *pItem); // set scrolling so a specific item is visible void FreezeScrolling() { pClientWindow->Freeze(); } void UnFreezeScrolling() { pClientWindow->UnFreeze(); } // change style void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false) { fDrawBackground=fDrawBG; this->fDrawBorder=fDrawBorder; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll); } void SetSelectionDiabled(bool fToVal=true) { fSelectionDisabled = fToVal; } // get head and tail list items Element *GetFirst() { return pClientWindow ? pClientWindow->GetFirst() : nullptr; } Element *GetLast() { return pClientWindow ? pClientWindow->GetLast() : nullptr; } // get margins from bounds to client rect virtual int32_t GetMarginTop() { return 3; } virtual int32_t GetMarginLeft() { return 3; } virtual int32_t GetMarginRight() { return 3; } virtual int32_t GetMarginBottom() { return 3; } Element *GetSelectedItem() { return pSelectedItem; } // get focused listbox item bool IsScrollingActive() { return pClientWindow && pClientWindow->IsScrollingActive(); } bool IsScrollingNecessary() { return pClientWindow && pClientWindow->IsScrollingNecessary(); } void SelectEntry(Element *pNewSel, bool fByUser); void SelectFirstEntry(bool fByUser) { SelectEntry(GetFirst(), fByUser); } void SelectNone(bool fByUser) { SelectEntry(nullptr, fByUser); } bool IsMultiColumn() const { return iColCount > 1; } int32_t ContractToElementHeight(); // make smaller if elements don't use up all of the available height. Return amount by which list box got contracted void UpdateElementPositions(); // reposition list items so they are stacked vertically void UpdateElementPosition(Element *pOfElement, int32_t iIndent); // update pos for one specific element virtual void UpdateSize() { Control::UpdateSize(); if (pClientWindow) { pClientWindow->UpdateSize(); UpdateElementPositions(); } } virtual bool IsSelectedChild(Element *pChild) { return pChild == pSelectedItem || (pSelectedItem && pSelectedItem->IsParentOf(pChild)); } typedef int32_t (*SortFunction)(const Element *pEl1, const Element *pEl2, void *par); void SortElements(SortFunction SortFunc, void *par); // sort list items }; // tabbing panel class Tabular : public Control { public: // sheet covering the client area of a tabular class Sheet : public Window { protected: StdStrBuf sTitle; // sheet label int32_t icoTitle; // sheet label icon uint32_t cHotkey; uint32_t dwCaptionClr; // caption color - default if 0 bool fHasCloseButton, fCloseButtonHighlighted; bool fTitleMarkup; Sheet(const char *szTitle, const C4Rect &rcBounds, int32_t icoTitle = Ico_None, bool fHasCloseButton=false, bool fTitleMarkup=true); // ctor; expands hotkey markup in title 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); void GetCaptionSize(int32_t *piWdt, int32_t *piHgt, bool fLarge, bool fActive, C4Facet *pfctClip, C4Facet *pfctIcon, CStdFont *pUseFont); virtual void OnShown(bool fByUser) { } // calklback from tabular after sheet has been made visible void SetCloseButtonHighlight(bool fToVal) { fCloseButtonHighlighted = fToVal; } bool IsPosOnCloseButton(int32_t x, int32_t y, int32_t iCaptWdt, int32_t iCaptHgt, bool fLarge); bool IsActiveSheet(); public: const char *GetTitle() { return sTitle.getData(); } char GetHotkey() { return cHotkey; } void SetTitle(const char *szNewTitle); void SetCaptionColor(uint32_t dwNewClr=0) { dwCaptionClr=dwNewClr; } virtual void UserClose() { delete this; } // user pressed close button bool HasCloseButton() const { return fHasCloseButton; } friend class Tabular; }; enum TabPosition { tbNone=0, // no tabs tbTop, // tabs on top tbLeft // tabs to the left }; private: Sheet *pActiveSheet; // currently selected sheet TabPosition eTabPos; // whither tabs shalt be shown or nay, en where art thy shown? int32_t iMaxTabWidth; // maximum tab length; used when tabs are positioned left and do not have gfx int32_t iSheetSpacing, iSheetOff; // distances of sheet captions int32_t iCaptionLengthTotal, iCaptionScrollPos; // scrolling in captions (top only) bool fScrollingLeft, fScrollingRight, fScrollingLeftDown, fScrollingRightDown; // scrolling in captions (top only) C4TimeMilliseconds tLastScrollTime; // set when fScrollingLeftDown or fScrollingRightDown are true: Time for next scrolling if mouse is held down int iSheetMargin; bool fDrawSelf; // if border and bg shall be drawn C4Facet *pfctBack, *pfctClip, *pfctIcons; // set for tabulars that have custom gfx CStdFont *pSheetCaptionFont; // font to be used for caption drawing; nullptr if default GUI font is to be used C4KeyBinding *pKeySelUp, *pKeySelDown, *pKeySelUp2, *pKeySelDown2, *pKeyCloseTab; // key bindings void SelectionChanged(bool fByUser); // pActiveSheet changed: sound, tooltip, etc. void SheetsChanged(); // update iMaxTabWidth by new set of sheet labels void UpdateScrolling(); void DoCaptionScroll(int32_t iDir); private: bool HasGfx() { return pfctBack && pfctClip && pfctIcons; } // whether the control uses custom graphics protected: bool KeySelUp(); // keyboard callback: Select previous sheet bool KeySelDown(); // keyboard callback: Select next sheet bool KeyCloseTab(); // keyboard callback: Close current sheet if possible virtual void DrawElement(C4TargetFacet &cgo); void MouseLeaveCaptionArea(); virtual void MouseLeave(CMouse &rMouse); virtual void OnGetFocus(bool fByMouse); virtual Control *IsFocusElement() { return eTabPos ? this : nullptr; }; // this control can gain focus only if tabs are enabled only virtual bool IsFocusOnClick() { return false; } // but never get focus on single mouse click, because this would de-focus any contained controls! int32_t GetTopSize() { return (eTabPos == tbTop) ? 20 : 0; } // vertical size of tab selection bar int32_t GetLeftSize() { return (eTabPos == tbLeft) ? (HasGfx() ? GetLeftClipSize(pfctClip) : 20+iMaxTabWidth) : 0; } // horizontal size of tab selection bar bool HasLargeCaptions() { return eTabPos == tbLeft; } virtual int32_t GetMarginTop() { return iSheetMargin+GetTopSize() + (HasGfx() ? (rcBounds.Hgt-GetTopSize())*30/483 : 0); } virtual int32_t GetMarginLeft() { return iSheetMargin+GetLeftSize() + (HasGfx() ? (rcBounds.Wdt-GetLeftSize())*13/628 : 0); } virtual int32_t GetMarginRight() { return iSheetMargin + (HasGfx() ? (rcBounds.Wdt-GetLeftSize())*30/628 : 0); } virtual int32_t GetMarginBottom() { return iSheetMargin + (HasGfx() ? (rcBounds.Hgt-GetTopSize())*32/483 : 0); } virtual void UpdateSize(); virtual bool IsSelectedChild(Element *pChild) { return pChild == pActiveSheet || (pActiveSheet && pActiveSheet->IsParentOf(pChild)); } public: Tabular(C4Rect &rtBounds, TabPosition eTabPos); // ctor ~Tabular(); // dtor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); virtual void RemoveElement(Element *pChild); // clear ptr Sheet *AddSheet(const char *szTitle, int32_t icoTitle = Ico_None); void AddCustomSheet(Sheet *pAddSheet); void ClearSheets(); // del all sheets void SelectSheet(int32_t iIndex, bool fByUser); void SelectSheet(Sheet *pSelSheet, bool fByUser); Sheet *GetSheet(int32_t iIndex) { return (Sheet *) GetElementByIndex(iIndex); } Sheet *GetActiveSheet() { return pActiveSheet; } int32_t GetActiveSheetIndex(); int32_t GetSheetCount() { return GetElementCount(); } void SetGfx(C4Facet *pafctBack, C4Facet *pafctClip, C4Facet *pafctIcons, CStdFont *paSheetCaptionFont, bool fResizeByAspect); static int32_t GetLeftClipSize(C4Facet *pfctForClip) { return pfctForClip->Wdt*95/120; } // left clip area size by gfx void SetSheetMargin(int32_t iMargin) { iSheetMargin = iMargin; UpdateOwnPos(); } void SetDrawDecoration(bool fToVal) { fDrawSelf = fToVal; } friend class Sheet; }; // scrollable text box class TextWindow : public Control { protected: ScrollWindow *pClientWindow; // client scrolling window Picture *pTitlePicture; // [optional]: Picture shown atop the text MultilineLabel *pLogBuffer; // buffer holding text data bool fDrawBackground, fDrawFrame; // whether dark background should be drawn (default true) size_t iPicPadding; virtual void DrawElement(C4TargetFacet &cgo); // draw text window virtual void ElementSizeChanged(Element *pOfElement); // called when an element size is changed virtual void ElementPosChanged(Element *pOfElement); // called when an element position is changed virtual void UpdateSize(); virtual Control *IsFocusElement() { return nullptr; }; // no focus element for now, because there's nothing to do (2do: scroll?) public: 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 void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr) // add text in a new line { if (pLogBuffer) pLogBuffer->AddLine(szText, pFont, dwClr, fDoUpdate, fMakeReadableOnBlack, pCaptionFont); } void ScrollToBottom() // set scrolling to bottom range { if (pClientWindow) pClientWindow->ScrollToBottom(); } void ClearText(bool fDoUpdate) { if (pLogBuffer) pLogBuffer->Clear(fDoUpdate); } int32_t GetScrollPos() { return pClientWindow ? pClientWindow->GetScrollY() : 0; } void SetScrollPos(int32_t iPos) { if (pClientWindow) pClientWindow->ScrollRangeInView(iPos, 0); } void UpdateHeight() { if (pLogBuffer) pLogBuffer->UpdateHeight(); } void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll) { fDrawBackground=fDrawBG; this->fDrawFrame=fDrawFrame; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll); } void SetPicture(const C4Facet &rNewPic); // get margins from bounds to client rect virtual int32_t GetMarginTop() { return 8; } virtual int32_t GetMarginLeft() { return 10; } virtual int32_t GetMarginRight() { return 5; } virtual int32_t GetMarginBottom() { return 8; } }; // context menu opened on right-click class ContextMenu : public Window { private: // key bindings class C4KeyBinding *pKeySelUp, *pKeySelDown, *pKeySubmenu, *pKeyBack, *pKeyAbort, *pKeyConfirm, *pKeyHotkey; bool KeySelUp(); bool KeySelDown(); bool KeySubmenu(); bool KeyBack(); bool KeyAbort(); bool KeyConfirm(); bool KeyHotkey(const C4KeyCodeEx &key); private: static int32_t iGlobalMenuIndex; int32_t iMenuIndex; void UpdateElementPositions(); // stack all menu elements // element adding made private: May only add entries bool AddElement(Element *pChild); bool InsertElement(Element *pChild, Element *pInsertBefore); public: // one text entry (icon+text+eventually right-arrow) class Entry : public Element { private: int32_t GetIconIndent() { return (icoIcon==-1) ? 0 : rcBounds.Hgt+2; } protected: StdStrBuf sText; // entry text uint32_t cHotkey; // entry hotkey Icons icoIcon; // icon to be drawn left of text MenuHandler *pMenuHandler; // callback when item is selected ContextHandler *pSubmenuHandler; // callback when submenu is opened protected: virtual void DrawElement(C4TargetFacet &cgo); // draw element MenuHandler *GetAndZeroCallback() { MenuHandler *pMH = pMenuHandler; pMenuHandler=nullptr; return pMH; } virtual void MouseLeave(CMouse &rMouse) { if (GetParent()) ((ContextMenu *) GetParent())->MouseLeaveEntry(rMouse, this); } virtual bool OnHotkey(uint32_t cKey) { return cKey==cHotkey; } virtual bool IsMenu() { return true; } public: Entry(const char *szText, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr); // ctor ~Entry() { if (pMenuHandler) delete pMenuHandler; if (pSubmenuHandler) delete pSubmenuHandler; } const char *GetText() { return sText.getData(); } friend class ContextMenu; }; protected: Element *pTarget; // target element; close menu if this is lost Element *pSelectedItem; // currently highlighted menu item ContextMenu *pSubmenu; // currently open submenu // mouse movement or buttons forwarded from screen: // Check bounds and forward as MouseInput to context menu or submenus // return whether inside context menu bounds bool CtxMouseInput(CMouse &rMouse, int32_t iButton, int32_t iScreenX, int32_t iScreenY, DWORD dwKeyParam); virtual void RemoveElement(Element *pChild); // clear ptr - abort menu if target is destroyed virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs void MouseLeaveEntry(CMouse &rMouse, Entry *pOldEntry); // callback: mouse leaves - deselect menu item if no submenu is open (callback done by menu item) virtual void DrawElement(C4TargetFacet &cgo); // draw BG virtual void Draw(C4TargetFacet &cgo); // draw inherited (element+contents) plus submenu virtual bool IsMenu() { return true; } virtual Screen *GetScreen(); // return screen by static var virtual int32_t GetMarginTop() { return 5; } virtual int32_t GetMarginLeft() { return 5; } virtual int32_t GetMarginRight() { return 5; } virtual int32_t GetMarginBottom() { return 5; } void SelectionChanged(bool fByUser); // other item selected: Close any submenu; do callbacks virtual void ElementSizeChanged(Element *pOfElement); // called when an element size is changed virtual void ElementPosChanged(Element *pOfElement); // called when an element position is changed void CheckOpenSubmenu(); // open submenu if present for selected item bool IsSubmenu(); // return whether it's a submenu void DoOK(); // do OK for selected item public: ContextMenu(); // ctor ~ContextMenu(); // dtor virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse movement or buttons void Open(Element *pTarget, int32_t iScreenX, int32_t iScreenY); void Abort(bool fByUser); void AddItem(const char *szText, const char *szToolTip=nullptr, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr) { Element *pNew = new ContextMenu::Entry(szText, icoIcon, pMenuHandler, pSubmenuHandler); AddElement(pNew); pNew->SetToolTip(szToolTip); } Entry *GetIndexedEntry(int32_t iIndex) { return static_cast(GetElementByIndex(iIndex)); } void SelectItem(int32_t iIndex); int32_t GetMenuIndex() { return iMenuIndex; } static int32_t GetLastMenuIndex() { return iGlobalMenuIndex; } Dialog *GetTargetDialog() const { return pTarget ? pTarget->GetDlg() : nullptr; } friend class Screen; friend class Entry; friend class Dialog; }; // a button to open a context menu class ContextButton : public Control { private: C4KeyBinding *pKeyContext; int32_t iOpenMenu; // associated menu (used to flag button down) bool fMouseOver; public: ContextButton(C4Rect &rtBounds); // ctor ContextButton(Element *pForEl, bool fAdd, int32_t iHIndent=4, int32_t iVIndent=4); // ctor creating at topright pos of element ~ContextButton(); private: bool DoContext(int32_t iX=-1, int32_t iY=-1); // open context menu bool KeyContext() { return DoContext(); } void RegisterContextKey(); protected: virtual void DrawElement(C4TargetFacet &cgo); // draw btn virtual bool IsFocusOnClick() { return false; } // don't select control on click public: virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. left-click opens menu virtual void MouseEnter(CMouse &rMouse); virtual void MouseLeave(CMouse &rMouse); }; // combo box filler // should be ComboBox::FillCB; but nested class will cause internal compiler faults class ComboBox_FillCB { private: ComboBox *pCombo; ContextMenu *pDrop; public: virtual ~ComboBox_FillCB() {} void FillDropDown(ComboBox *pComboBox, ContextMenu *pDropdownList) { pCombo = pComboBox; pDrop = pDropdownList; FillDropDownCB(); } virtual void FillDropDownCB() = 0; virtual bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) = 0; // to be used in fill-callback only (crash otherwise!) void AddEntry(const char *szText, int32_t id); bool FindEntry(const char *szText); void ClearEntries(); void SelectEntry(int32_t iEntry); // select entry by index }; template class ComboBox_FillCallback : public ComboBox_FillCB { public: typedef void (CB::*ComboFillFunc)(ComboBox_FillCB *pFiller); typedef bool (CB::*ComboSelFunc)(ComboBox *pForCombo, int32_t idNewSelection); private: CB *pCBClass; ComboFillFunc FillFunc; ComboSelFunc SelFunc; protected: virtual void FillDropDownCB() { if (pCBClass && FillFunc) (pCBClass->*FillFunc)(this); } virtual bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) { if (pCBClass && SelFunc) return (pCBClass->*SelFunc)(pForCombo, idNewSelection); else return false; } public: ComboBox_FillCallback(CB * pCBClass, ComboFillFunc FillFunc, ComboSelFunc SelFunc) : pCBClass(pCBClass), FillFunc(FillFunc), SelFunc(SelFunc) { } }; // dropdown box to select elements from a list class ComboBox : public Control { public: struct ComboMenuCBStruct // struct used as menu callback parameter for dropdown menu { StdCopyStrBuf sText; int32_t id; ComboMenuCBStruct() : sText(), id(0) {} ComboMenuCBStruct(const char *szText, int32_t id) : sText(szText), id(id) {} }; private: class C4KeyBinding *pKeyOpenCombo, *pKeyCloseCombo; private: int32_t iOpenMenu; // associated menu (used to flag dropped down) ComboBox_FillCB *pFillCallback; // callback used to display the dropdown char Text[C4MaxTitle+1]; // currently selected item bool fReadOnly; // merely a label in ReadOnly-mode bool fSimple; // combo without border and stuff bool fMouseOver; // mouse hovering over this? CStdFont *pUseFont; // font used to draw this control uint32_t dwFontClr, dwBGClr, dwBorderClr; // colors used to draw this control C4Facet *pFctSideArrow; // arrow gfx used to start combo-dropdown private: bool DoDropdown(); // open dropdown menu (context menu) bool KeyDropDown() { return DoDropdown(); } bool KeyAbortDropDown() { return AbortDropdown(true); } bool AbortDropdown(bool fByUser); // abort dropdown menu, if it's open protected: virtual void DrawElement(C4TargetFacet &cgo); // draw combo box virtual bool IsFocusOnClick() { return false; } // don't select control on click virtual Control *IsFocusElement() { return fReadOnly ? nullptr : this; }; // this control can gain focus if not readonly void OnCtxComboSelect(C4GUI::Element *pListItem, const ComboMenuCBStruct &rNewSel); public: ComboBox(const C4Rect &rtBounds); ~ComboBox(); virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. left-click opens menu virtual void MouseEnter(CMouse &rMouse); // called when mouse cursor enters element region virtual void MouseLeave(CMouse &rMouse); // called when mouse cursor leaves element region void SetComboCB(ComboBox_FillCB *pNewFillCallback); static int32_t GetDefaultHeight(); void SetText(const char *szToText); void SetReadOnly(bool fToVal) { if ((fReadOnly = fToVal)) AbortDropdown(false); } void SetSimple(bool fToVal) { fSimple = fToVal; } const StdStrBuf GetText() { return StdStrBuf(Text, false); } void SetFont(CStdFont *pToFont) { pUseFont=pToFont; } void SetColors(uint32_t dwFontClr, uint32_t dwBGClr, uint32_t dwBorderClr) { this->dwFontClr=dwFontClr; this->dwBGClr=dwBGClr; this->dwBorderClr=dwBorderClr; } void SetDecoration(C4Facet *pFctSideArrow) { this->pFctSideArrow = pFctSideArrow; } friend class ComboBox_FillCB; }; class Dialog; // EM window class class DialogWindow : public C4Window { public: Dialog* pDialog; DialogWindow(): C4Window(), pDialog(nullptr) {} using C4Window::Init; C4Window * Init(C4AbstractApp * pApp, const char * Title, const C4Rect &rcBounds, const char *szID); virtual void Close(); virtual void PerformUpdate(); }; // information on how to draw dialog borders and face class FrameDecoration { private: int iRefCount; bool SetFacetByAction(C4Def *pOfDef, class C4TargetFacet &rfctTarget, const char *szFacetName); public: C4Def *pSourceDef; C4ID idSourceDef; uint32_t dwBackClr; // background face color C4TargetFacet fctTop, fctTopRight, fctRight, fctBottomRight, fctBottom, fctBottomLeft, fctLeft, fctTopLeft; int iBorderTop, iBorderLeft, iBorderRight, iBorderBottom; bool fHasGfxOutsideClientArea; FrameDecoration() : iRefCount(0) { Clear(); } void Clear(); // zero data // create from ActMap and graphics of a definition (does some script callbacks to get parameters) bool SetByDef(C4Def *pSrcDef); bool SetByDef(C4ID idSourceDef); // a wrapper for the above method bool UpdateGfx(); // update Surface, e.g. after def reload void Ref() { ++iRefCount; } void Deref() { if (!--iRefCount) delete this; } void Draw(C4TargetFacet &cgo, C4Rect &rcDrawArea); // draw deco for given rect (rect includes border) }; // a dialog class Dialog: public Window { private: enum Fade { eFadeNone=0, eFadeOut, eFadeIn }; C4KeyBinding *pKeyAdvanceControl, *pKeyAdvanceControlB, *pKeyHotkey, *pKeyEnter, *pKeyEscape, *pKeyFocusDefControl; protected: WoodenLabel *pTitle; // title bar text CallbackButton *pCloseBtn; Control *pActiveCtrl; // control that has focus bool fShow; // if set, the dlg is shown bool fOK; // if set, the user pressed OK int32_t iFade; // dlg fade (percent) Fade eFade; // fading mode bool fDelOnClose; // auto-delete when closing StdStrBuf TitleString; bool fViewportDlg; // set in ctor: if true, dlg is not independant, but drawn ad controlled within viewports DialogWindow *pWindow; // window in console mode FrameDecoration *pFrameDeco; bool CreateConsoleWindow(); void DestroyConsoleWindow(); virtual void UpdateSize(); // called when own size changed - update assigned pWindow virtual void UpdatePos(); // Dialogs with their own windows can only be at 0/0 public: Dialog(int32_t iWdt, int32_t iHgt, const char *szTitle, bool fViewportDlg); // ctor virtual ~Dialog(); // dtor virtual void RemoveElement(Element *pChild); // clear ptr virtual void Draw(C4TargetFacet &cgo); // render dialog (published) virtual void DrawElement(C4TargetFacet &cgo); // draw dlg bg virtual bool IsComponentOutsideClientArea() { return !!pTitle; } // pTitle lies outside client area virtual const char *GetID() { return nullptr; } // special handling for viewport dialogs virtual void ApplyElementOffset(int32_t &riX, int32_t &riY); virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY); virtual bool IsFocused(Control *pCtrl) { return pCtrl == pActiveCtrl; } void SetFocus(Control *pCtrl, bool fByMouse); Control *GetFocus() { return pActiveCtrl; } virtual Dialog *GetDlg() { return this; } // this is the dialog virtual DialogWindow* GetDialogWindow() { return pWindow; } virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs (forward to focused control) private: bool KeyHotkey(const C4KeyCodeEx &key); bool KeyFocusDefault(); public: virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // input: mouse. forwards to child controls // default control to be set if unprocessed keyboard input has been detected virtual class Control *GetDefaultControl() { return nullptr; } // default dlg actions for enter/escape virtual bool OnEnter() { UserClose(true); return true; } bool KeyEnter() { return OnEnter(); } virtual bool OnEscape() { UserClose(false); return true; } bool KeyEscape() { return OnEscape(); } void AdvanceFocus(bool fBackwards); // change focus to next component bool KeyAdvanceFocus(bool fBackwards) { AdvanceFocus(fBackwards); return true; } virtual int32_t GetMarginTop() { return (pTitle ? pTitle->GetBounds().Hgt : 0) + (pFrameDeco ? pFrameDeco->iBorderTop : Window::GetMarginTop()); } virtual int32_t GetMarginLeft() { return pFrameDeco ? pFrameDeco->iBorderLeft : Window::GetMarginLeft(); } virtual int32_t GetMarginRight() { return pFrameDeco ? pFrameDeco->iBorderRight: Window::GetMarginRight(); } virtual int32_t GetMarginBottom() { return pFrameDeco ? pFrameDeco->iBorderBottom: Window::GetMarginBottom(); } static int32_t GetDefaultTitleHeight(); bool IsShown() { return fShow; } // returns whether dlg is on screen (may be invisible) bool IsOK() { return fOK; } // returns whether user pressed OK bool IsAborted() { return !fShow && !fOK; } // returns whether dialog has been aborted bool IsActive(bool fForKeyboard); // return whether dlg has mouse control focus bool IsFading() { return eFade != eFadeNone; } virtual bool IsFullscreenDialog() { return false; } virtual bool HasBackground() { return false; } // true if dlg draws screen background (fullscreen dialogs only) // true for dialogs that should span the whole screen // not just the mouse-viewport virtual bool IsFreePlaceDialog() { return false; } // true for dialogs that should be placed at the bottom of the screen (chat) virtual bool IsBottomPlacementDialog() { return false; } // true for dialogs that receive full keyboard and mouse input even in shared mode virtual bool IsExclusiveDialog() { return false; } // some dialogs, like menus or chat control, don't really need a mouse // so do not enable it for those in shared mode if mouse control is disabled virtual bool IsMouseControlled() { return true; } // For dialogs associated to a viewport: Return viewport (for placement) virtual C4Viewport *GetViewport() { return nullptr; } bool IsViewportDialog() { return fViewportDlg; } // for custom placement procedures; should call SetPos virtual bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) { return false; } // true for dialogs drawn externally virtual bool IsExternalDrawDialog() { return false; } // z-ordering used for dialog placement virtual int32_t GetZOrdering() { return C4GUI_Z_DEFAULT; } bool Show(Screen *pOnScreen, bool fCB); // show dialog on screen - default to last created screen void Close(bool fOK); // close dlg bool FadeIn(Screen *pOnScreen); // fade dlg into screen void FadeOut(bool fCloseWithOK); // fade out dlg bool DoModal(); // execute message loop until dlg is closed (or GUI destructed) - returns whether dlg was OK bool Execute(); // execute dialog - does message handling, gfx output and idle proc; return false if dlg got closed or GUI deleted bool Execute2(); // execute dialog - does message handling, gfx output and idle proc; return false and deletes self if dlg got closed or GUI deleted void SetDelOnClose(bool fToVal=true) { fDelOnClose=fToVal; } // dialog will delete itself when closed void SetTitle(const char *szToTitle, bool fShowCloseButton = true); // change title text; creates or removes title bar if necessary void SetFrameDeco(FrameDecoration *pNewDeco) // change border decoration { if (pFrameDeco) pFrameDeco->Deref(); if ((pFrameDeco = pNewDeco)) pNewDeco->Ref(); UpdateOwnPos(); // margin may have changed; might need to reposition stuff } void ClearFrameDeco() // clear border decoration; no own pos update! {if (pFrameDeco) pFrameDeco->Deref(); pFrameDeco = nullptr; } FrameDecoration *GetFrameDecoration() const { return pFrameDeco; } void SetClientSize(int32_t iToWdt, int32_t iToHgt); // resize dialog so its client area has the specified size void OnUserClose(C4GUI::Control *btn) // user presses close btn: Usually close dlg with abort { UserClose(false); } virtual void UserClose(bool fOK) { Close(fOK); } virtual void OnClosed(bool fOK); // callback when dlg got closed virtual void OnShown() {} // callback when shown - should not delete the dialog virtual void OnIdle() {} // idle proc in DoModal virtual ContextHandler *GetContextHandler() // always use own context handler only (no fall-through to screen) { return pContextHandler; } #ifdef _WIN32 static bool RegisterWindowClass(HINSTANCE hInst); // registers WNDCLASS for console mode dialogs #endif friend class Screen; }; // a dialog covering the whole screen (using UpperBoard-caption) class FullscreenDialog : public Dialog { protected: Label *pFullscreenTitle, *pSubTitle; // subtitle to be put in upper-right corner int32_t iDlgMarginX, iDlgMarginY; // dialog margin set by screen size virtual const char *GetID() { return 0; } // no ID needed, because it's never created as a window public: FullscreenDialog(const char *szTitle, const char *szSubtitle); // ctor void SetTitle(const char *szToTitle); // change title text; creates or removes title bar if necessary protected: virtual void DrawElement(C4TargetFacet &cgo); // draw dlg bg // fullscreen dialogs are not closed on Enter virtual bool OnEnter() { return false; } virtual bool IsComponentOutsideClientArea() { return true; } virtual bool HasUpperBoard() { return false; } // standard fullscreen dialog: UpperBoard no longer present virtual bool IsFullscreenDialog() { return true; } virtual bool DoPlacement(Screen *pOnScreen, const C4Rect &rPreferredDlgRect) { return true; } // fullscreen dlg already placed virtual int32_t GetMarginTop() { return (HasUpperBoard() ? C4UpperBoardHeight : C4GUI_FullscreenDlg_TitleHeight) + iDlgMarginY; } virtual int32_t GetMarginLeft() { return iDlgMarginX; } virtual int32_t GetMarginRight() { return iDlgMarginX; } virtual int32_t GetMarginBottom() { return iDlgMarginY; } virtual void UpdateOwnPos(); // called when element bounds were changed externally // helper func: draw facet to screen background void DrawBackground(C4TargetFacet &cgo, C4Facet &rFromFct); }; // a button closing the Dlg class CloseButton : public Button { protected: bool fCloseResult; virtual void OnPress() { Dialog *pDlg; if ((pDlg = GetDlg())) pDlg->UserClose(fCloseResult); } public: CloseButton(const char *szBtnText, const C4Rect &rtBounds, bool fResult) // ctor : Button(szBtnText, rtBounds), fCloseResult(fResult) { } }; class CloseIconButton : public IconButton { protected: bool fCloseResult; virtual void OnPress() { Dialog *pDlg; if ((pDlg = GetDlg())) pDlg->UserClose(fCloseResult); } public: CloseIconButton(const C4Rect &rtBounds, Icons eIcon, bool fResult) // ctor : IconButton(eIcon, rtBounds, 0), fCloseResult(fResult) { } }; // OK button class OKButton : public CloseButton { public: OKButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_DLG_OK"), rtBounds, true) {} }; class OKIconButton : public CloseIconButton { public: OKIconButton(const C4Rect &rtBounds, Icons eIcon) // ctor : CloseIconButton(rtBounds, eIcon, true) {} }; // cancel button class CancelButton : public CloseButton { public: CancelButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_DLG_CANCEL"), rtBounds, false) {} }; class CancelIconButton : public CloseIconButton { public: CancelIconButton(const C4Rect &rtBounds, Icons eIcon) // ctor : CloseIconButton(rtBounds, eIcon, false) {} }; // regular close button class DlgCloseButton : public CloseButton { public: DlgCloseButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_DLG_CLOSE"), rtBounds, true) {} }; // Yes button class YesButton : public CloseButton { public: YesButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_DLG_YES"), rtBounds, true) {} }; // No button class NoButton : public CloseButton { public: NoButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_DLG_NO"), rtBounds, false) {} }; // Retry button class RetryButton : public CloseButton { public: RetryButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_BTN_RETRY"), rtBounds, true) {} }; // Reset button class ResetButton : public CloseButton { public: ResetButton(const C4Rect &rtBounds) // ctor : CloseButton(LoadResStr("IDS_BTN_RESET"), rtBounds, true) {} }; // a simple message dialog class MessageDialog : public Dialog { private: bool fHasOK; int32_t *piConfigDontShowAgainSetting; class C4KeyBinding *pKeyCopy; StdCopyStrBuf sCopyText; // text that goes into clipboard if user presses Ctrl+C on this window public: enum Buttons { btnOK=1, btnAbort=2, btnYes=4, btnNo=8, btnRetry=16, btnReset=32, btnOKAbort=btnOK|btnAbort, btnYesNo=btnYes|btnNo, btnRetryAbort=btnRetry|btnAbort }; enum DlgSize { dsRegular=C4GUI_MessageDlgWdt, dsMedium=C4GUI_MessageDlgWdtMedium, dsSmall=C4GUI_MessageDlgWdtSmall }; MessageDialog(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, DlgSize eSize=dsRegular, int32_t *piConfigDontShowAgainSetting=nullptr, bool fDefaultNo=false); ~MessageDialog(); protected: virtual bool OnEnter() { if (!fHasOK) return false; Close(true); return true; } void OnDontShowAgainCheck(C4GUI::Element *pCheckBox) { if (piConfigDontShowAgainSetting) *piConfigDontShowAgainSetting = static_cast(pCheckBox)->GetChecked(); } bool KeyCopy(); virtual const char *GetID() { return "MessageDialog"; } virtual int32_t GetZOrdering() { return C4GUI_Z_INPUT; } }; // a confirmation dialog, which performs a callback after confirmation class ConfirmationDialog : public MessageDialog { private: BaseCallbackHandler *pCB; public: ConfirmationDialog(const char *szMessage, const char *szCaption, BaseCallbackHandler *pCB, DWORD dwButtons=MessageDialog::btnOKAbort, bool fSmall=false, Icons icoIcon=Ico_Confirm); ~ConfirmationDialog() { if (pCB) pCB->DeRef(); } protected: virtual void OnClosed(bool fOK); // callback when dlg got closed }; // a simple progress dialog class ProgressDialog : public Dialog { protected: ProgressBar *pBar; // progress bar component virtual const char *GetID() { return "ProgressDialog"; } public: ProgressDialog(const char *szMessage, const char *szCaption, int32_t iMaxProgress, int32_t iInitialProgress, Icons icoIcon); void SetProgress(int32_t iToProgress) { pBar->SetProgress(iToProgress); } // change progress virtual bool OnEnter() { return false; } // don't close on Enter! }; class BaseInputCallback { public: virtual void OnOK(const StdStrBuf &sText) = 0; virtual ~BaseInputCallback() {} }; template class InputCallback : public BaseInputCallback { public: typedef void (T::*CBFunc)(const StdStrBuf &); private: T *pTarget; CBFunc pCBFunc; public: InputCallback(T *pTarget, CBFunc pCBFunc) : pTarget(pTarget), pCBFunc(pCBFunc) {} virtual void OnOK(const StdStrBuf &sText) { (pTarget->*pCBFunc)(sText); } }; // a dialog for a one-line text input; contains OK and cancel button class InputDialog : public Dialog { protected: Edit *pEdit; // edit for text input BaseInputCallback *pCB; C4Rect rcEditBounds; bool fChatLayout; Label *pChatLbl; virtual const char *GetID() { return "InputDialog"; } public: InputDialog(const char *szMessage, const char *szCaption, Icons icoIcon, BaseInputCallback *pCB, bool fChatLayout=false); ~InputDialog() { if (pCB) delete pCB; } virtual void OnClosed(bool fOK) { if (pCB && fOK) pCB->OnOK(StdStrBuf(pEdit->GetText())); Dialog::OnClosed(fOK); } // close CB void SetMaxText(int32_t iMaxLen) { pEdit->SetMaxText(iMaxLen); } void SetInputText(const char *szToText); const char *GetInputText() { return pEdit->GetText(); } void SetCustomEdit(Edit *pCustomEdit); }; // a dialog showing some information text class InfoDialog : public Dialog, private C4ApplicationSec1Timer { private: int32_t iScroll; // current scroll pos; backup for text update protected: TextWindow *pTextWin; void CreateSubComponents(); // ctor func // add text to the info window void AddLine(const char *szText); void AddLineFmt(const char *szFmtString, ...) GNUC_FORMAT_ATTRIBUTE_O; void BeginUpdateText(); // backup scrolling and clear text window void EndUpdateText(); // restore scroll pos; set last update time virtual void UpdateText() {}; // function to be overwritten for timed dlgs: Update window text virtual void OnSec1Timer(); // idle proc: update text if necessary public: InfoDialog(const char *szCaption, int32_t iLineCount); // ctor InfoDialog(const char *szCaption, int iLineCount, const StdStrBuf &sText); // ctor - init w/o timer ~InfoDialog(); }; // a keyboard event that's only executed if the dialog is activated template class DlgKeyCB : public C4KeyCB { private: typedef C4KeyCB Base; typedef bool(TargetClass::*CallbackFunc)(); public: DlgKeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} virtual bool CheckCondition() { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } }; template class DlgKeyCBPassKey : public C4KeyCBPassKey { private: typedef C4KeyCBPassKey Base; typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key); public: DlgKeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} virtual bool CheckCondition() { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } }; template class DlgKeyCBEx : public C4KeyCBEx { private: typedef C4KeyCBEx Base; typedef bool(TargetClass::*CallbackFunc)(ParameterType par); public: DlgKeyCBEx(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, par, pFuncDown, pFuncUp, pFuncPressed) {} virtual bool CheckCondition() { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); } }; // a keyboard event that's only executed if the control has focus template class ControlKeyCB : public C4KeyCB { private: typedef C4KeyCB Base; typedef bool(TargetClass::*CallbackFunc)(); public: ControlKeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed) {} virtual bool CheckCondition() { return Base::rTarget.HasDrawFocus(); } }; template class ControlKeyCBExPassKey : public C4KeyCBExPassKey { private: typedef C4KeyCBExPassKey Base; typedef bool(TargetClass::*CallbackFunc)(const C4KeyCodeEx &key, const ParameterType &par); public: ControlKeyCBExPassKey(TargetClass &rTarget, const ParameterType &rPar, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, rPar, pFuncDown, pFuncUp, pFuncPressed) {} virtual bool CheckCondition() { return Base::rTarget.HasDrawFocus(); } }; // key event that checks whether a control is active, but forwards to the dialog template class ControlKeyDlgCB : public C4KeyCB { private: class Control *pCtrl; typedef C4KeyCB Base; typedef bool(TargetClass::*CallbackFunc)(); public: ControlKeyDlgCB(Control *pCtrl, TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=nullptr, CallbackFunc pFuncPressed=nullptr) : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed), pCtrl(pCtrl) {} virtual bool CheckCondition() { return pCtrl && pCtrl->IsInActiveDlg(true) && pCtrl->IsVisible(); } }; // mouse cursor control class CMouse { public: int32_t x,y; // cursor position bool LDown, MDown, RDown; // mouse button states int32_t LDownX, LDownY; // position where left button was pressed last DWORD dwKeys; // shift, ctrl, etc. bool fActive; C4TimeMilliseconds tLastMovementTime; // C4TimeMilliseconds::Now() when the mouse pos changed last // whether last input was done by mouse // set to true whenever mouse pos changes or buttons are pressed // reset by keyboard actions to avoid tooltips where the user isn't even doing anything bool fActiveInput; enum TooltipShowState { TTST_None = 0, // show no tooltips TTST_Immediate = 1, // show only tooltips of elements that require immediate show (i.e. otherwise unlabeled buttons) TTST_All = 2, // show all tooltips (mouse hovered on same element for some time) }; public: Element *pMouseOverElement, *pPrevMouseOverElement; // elements at mouse position (after and before processing of mouse event) Element *pDragElement; // element at pos where left mouse button was clicked last public: CMouse(int32_t iX, int32_t iY); // ctor ~CMouse(); // dtor void Draw(C4TargetFacet &cgo, TooltipShowState draw_tool_tips); // draw cursor void Input(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); // process mouse input bool IsLDown() { return LDown; } void GetLastXY(int32_t &rX, int32_t &rY, DWORD &rdwKeys) { rX=x; rY=y; rdwKeys=dwKeys; } void ResetElements() // reset MouseOver/etc.-controls { pMouseOverElement=pPrevMouseOverElement=pDragElement=nullptr; } void ReleaseElements(); // reset MouseOver/etc.-controls, doing the appropriate callbacks void OnElementGetsInvisible(Element *pChild); // clear ptr void RemoveElement(Element *pChild); // clear ptr void SetOwnedMouse(bool fToVal) { fActive=fToVal; } void ResetToolTipTime() { tLastMovementTime = C4TimeMilliseconds::Now(); } bool IsMouseStill() { return C4TimeMilliseconds::Now()-tLastMovementTime >= C4GUI_ToolTipShowTime; } void ResetActiveInput() { fActiveInput = false; } bool IsActiveInput() { return fActiveInput; } void ReleaseButtons() { LDown = RDown = MDown = false; } }; // the dialog client area class Screen : public Window { public: CMouse Mouse; protected: Dialog *pActiveDlg; // current, active dialog ContextMenu *pContext; // currently opened context menu (lowest submenu) bool fExclusive; // default true. if false, input is shared with the game C4Rect PreferredDlgRect; // rectangle in which dialogs should be placed float fZoom; static Screen *pScreen; // static singleton var public: virtual void RemoveElement(Element *pChild); // clear ptr const C4Rect &GetPreferredDlgRect() { return PreferredDlgRect; } protected: using Window::Draw; virtual void Draw(C4TargetFacet &cgo, bool fDoBG); // draw screen contents virtual void ElementPosChanged(Element *pOfElement); // called when a dialog is moved public: void ShowDialog(Dialog *pDlg, bool fFade); // present dialog to user void CloseDialog(Dialog *pDlg, bool fFade);// hide dialog from user void ActivateDialog(Dialog *pDlg); // set dlg as active void RecheckActiveDialog(); Dialog *GetTopDialog(); // get topmost dlg public: Screen(); ~Screen(); void Init(int32_t tx, int32_t ty, int32_t twdt, int32_t thgt); void Clear(); void Render(bool fDoBG); // render to pDraw void RenderMouse(C4TargetFacet &cgo); // draw mouse only virtual Screen *GetScreen() { return this; }; // return contained screen static Screen *GetScreenS() { return pScreen; } // get global screen float GetZoom() const { return fZoom; }; // get GUI zoom bool IsActive() { return !!GetTopDialog(); } // return whether GUI is active bool KeyAny(); // to be called on keystrokes; resets some tooltip-times virtual bool CharIn(const char * c); // input: character key pressed - should return false for none-character-inputs using Window::MouseInput; 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 void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP); // pVP specified for console mode viewports only void SetMouseInGUI(bool fInGUI, bool fByMouse); bool RecheckMouseInput(); // do mouse movement iusing last input flags bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr); // show message bool ShowErrorMessage(const char *szMessage); // show message: Error caption and icon bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr); // show modal message dlg 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 bool ShowModalDlg(Dialog *pDlg, bool fDestruct=true); // show any dialog modal and destruct it afterwards bool ShowRemoveDlg(Dialog *pDlg); // show dialog, and flag it to delete itself when closed; return immediately void CloseAllDialogs(bool fWithOK); // close all dialogs on the screen; top dlgs first void SetPreferredDlgRect(const C4Rect &rtNewPref) { PreferredDlgRect = rtNewPref; } #ifdef USE_WIN32_WINDOWS Dialog *GetDialog(HWND hWindow); // get console dialog #endif Dialog *GetDialog(C4Window * pWindow); // get console dialog void DoContext(ContextMenu *pNewCtx, Element *pAtElement, int32_t iX, int32_t iY); // open context menu (closes any other contextmenu) void AbortContext(bool fByUser) { if (pContext) pContext->Abort(fByUser); } // close context menu int32_t GetContextMenuIndex() { return pContext ? pContext->GetMenuIndex() : 0; } // get current context-menu (lowest level) int32_t GetLastContextMenuIndex() { return ContextMenu::GetLastMenuIndex(); } // get last opened context-menu (lowest level) bool HasContext() { return !!pContext; } bool HasFullscreenDialog(bool fIncludeFading); // true if on fullscreen dialog is visible Dialog *GetFullscreenDialog(bool fIncludeFading); // return dlg pointer to first found fullscreen dialog // callback from C4Game from message-handling of WM_TIMER; forwards to dialog-timer void OnSec1Timer(); // exclusive void SetExclusive(bool fToState) { fExclusive = fToState; Mouse.SetOwnedMouse(fExclusive); UpdateMouseFocus(); } bool IsExclusive() { return fExclusive; } bool HasKeyboardFocus() { // always hook keyboard in exclusive mode; only on exclusive top dialogs in shared mode if (IsExclusive()) return true; Dialog *pDlg = GetTopDialog(); return pDlg && pDlg->IsExclusiveDialog(); } bool HasMouseFocus() // return whether mouse controls GUI only { return HasKeyboardFocus(); } int32_t GetMouseControlledDialogCount(); // count dlgs with that flag set void UpdateMouseFocus(); // when exclusive mode has changed: Make sure mouse clip is correct // draw a tooltip in a box static void DrawToolTip(const char *szTip, C4TargetFacet &cgo, float guix, float guiy); // gamepad void UpdateGamepadGUIControlEnabled(); // callback when gamepad gui option changed friend class Dialog; friend class ContextMenu; }; // menu handler bound to member functions of another class template class CBMenuHandler : public MenuHandler { private: CBClass *pCBTarget; // callback target typename DlgCallback::ContextClickFunc pCallbackFn; // callback function public: // ctor CBMenuHandler(CBClass *pCBTarget, typename DlgCallback::ContextClickFunc pCallbackFn, int32_t iaExtra=0) : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) { } virtual void OnOK(Element *pTargetElement) { if (!pCBTarget /* || !pCallbackFn */) return; // do callback (pCBTarget->*pCallbackFn)(pTargetElement); } }; // menu handler bound to member functions of another class, providing extra storage for parameters template class CBMenuHandlerEx : public MenuHandler { private: CBClass *pCBTarget; // callback target typename DlgCallbackEx::ContextClickFunc pCallbackFn; // callback function TEx Extra; // extra data public: // ctor CBMenuHandlerEx(CBClass *pCBTarget, typename DlgCallbackEx::ContextClickFunc pCallbackFn, TEx aExtra) : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra(aExtra) { } CBMenuHandlerEx(CBClass *pCBTarget, typename DlgCallbackEx::ContextClickFunc pCallbackFn) : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra() { } const TEx &GetExtra() const { return Extra; } void SetExtra(const TEx &e) { Extra = e; } virtual void OnOK(Element *pTargetElement) { if (!pCBTarget || !pCallbackFn) return; // do callback (pCBTarget->*pCallbackFn)(pTargetElement, Extra); } }; // context handler bound to member functions of another class template class CBContextHandler : public ContextHandler { private: CBClass *pCBTarget; // callback target typename DlgCallback::ContextFunc pCallbackFn; // callback function public: // ctor CBContextHandler(CBClass *pCBTarget, typename DlgCallback::ContextFunc pCallbackFn) : ContextHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) { } virtual bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) { DlgCallback(); if (!pCBTarget) return false; // if (!pCallbackFn) return false; Screen *pScreen = pOnElement->GetScreen(); if (!pScreen) return false; // let CB func create context menu ContextMenu *pNewMenu = (pCBTarget->*pCallbackFn)(pOnElement, iX, iY); if (!pNewMenu) return false; // open it on screen pScreen->DoContext(pNewMenu, pOnElement, iX, iY); // done, success return true; } virtual C4GUI::ContextMenu *OnSubcontext(Element *pOnElement) { // query directly return (pCBTarget->*pCallbackFn)(pOnElement, 0, 0); } friend class Dialog; }; // a helper used to align elements class ComponentAligner { protected: C4Rect rcClientArea; // free client area int32_t iMarginX, iMarginY; // horizontal and vertical margins of components static C4Rect rcTemp; // temp rect public: ComponentAligner(const C4Rect &rcArea, int32_t iMarginX, int32_t iMarginY, bool fZeroAreaXY=false) // ctor : rcClientArea(rcArea), iMarginX(iMarginX), iMarginY(iMarginY) { if (fZeroAreaXY) rcClientArea.x=rcClientArea.y=0; } bool GetFromTop(int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the top bool GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the left bool GetFromRight(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the right bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the bottom void GetAll(C4Rect &rcOut); // get remaining client area bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get centered subregion within area C4Rect &GetFromTop(int32_t iHgt, int32_t iWdt=-1) { GetFromTop(iHgt, iWdt, rcTemp); return rcTemp; } // get a portion from the top C4Rect &GetFromLeft(int32_t iWdt, int32_t iHgt=-1) { GetFromLeft(iWdt, iHgt, rcTemp); return rcTemp; } // get a portion from the left C4Rect &GetFromRight(int32_t iWdt, int32_t iHgt=-1) { GetFromRight(iWdt, iHgt, rcTemp); return rcTemp; } // get a portion from the right C4Rect &GetFromBottom(int32_t iHgt, int32_t iWdt=-1) { GetFromBottom(iHgt, iWdt, rcTemp); return rcTemp; } // get a portion from the bottom C4Rect &GetAll() { GetAll(rcTemp); return rcTemp; } // get remaining client are C4Rect &GetCentered(int32_t iWdt, int32_t iHgt) { GetCentered(iWdt, iHgt, rcTemp); return rcTemp; } // get centered subregion within area (w/o size checks) 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); int32_t GetWidth() const { return rcClientArea.Wdt; } int32_t GetHeight() const { return rcClientArea.Hgt; } int32_t GetHMargin() const { return iMarginX; } int32_t GetVMargin() const { return iMarginY; } int32_t GetInnerWidth() const { return rcClientArea.Wdt-2*iMarginX; } int32_t GetInnerHeight() const { return rcClientArea.Hgt-2*iMarginY; } void ExpandTop(int32_t iByHgt) { rcClientArea.y-=iByHgt; rcClientArea.Hgt+=iByHgt; } // enlarge client rect void ExpandLeft(int32_t iByWdt) { rcClientArea.x-=iByWdt; rcClientArea.Wdt+=iByWdt; } // enlarge client rect void ExpandRight(int32_t iByWdt) { rcClientArea.Wdt+=iByWdt; } // enlarge client rect void ExpandBottom(int32_t iByHgt) { rcClientArea.Hgt+=iByHgt; } // enlarge client rect void LogIt(const char *szName); }; // shortcut for check whether GUI is active inline bool IsActive() { return Screen::GetScreenS()->IsActive(); } inline bool IsExclusive() { return Screen::GetScreenS()->IsExclusive(); } // shortcut for GUI screen size inline int32_t GetScreenWdt() { return Screen::GetScreenS()->GetBounds().Wdt; } inline int32_t GetScreenHgt() { return Screen::GetScreenS()->GetBounds().Hgt; } // sound effect in GUI: Only if enabled void GUISound(const char *szSound); // Zoom inline float GetZoom() { return Screen::GetScreenS()->GetZoom(); } inline void MouseMove(int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam, class C4Viewport *pVP) // pVP specified for console mode viewports only { Screen::GetScreenS()->MouseMove(iButton, iX, iY, dwKeyParam, pVP); } extern Screen TheScreen; } // end of namespace typedef C4GUI::Screen C4GUIScreen; extern C4GUIScreen *pGUI; #endif