1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2023, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// generic user interface
19// defines user controls
20
21// 2do:
22// mouse wheel processing
23// disabled buttons
24
25#pragma once
26
27#define ConsoleDlgClassName "C4GUIdlg"
28#define ConsoleDlgWindowStyle (WS_VISIBLE | WS_POPUP | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX)
29
30#include "C4FacetEx.h"
31#include "C4ForwardDeclarations.h"
32#include "C4GamePadCon.h"
33#include "C4Id.h"
34#include "C4KeyboardInput.h"
35#include "C4LogBuf.h"
36#include "C4Rect.h"
37
38#include "StdFont.h"
39#include "C4ResStrTable.h"
40#include "StdWindow.h"
41
42#include <cmath>
43#include <cstdint>
44
45class C4GroupSet;
46class C4Viewport;
47
48template<typename T>
49class C4Sec1TimerCallback;
50
51// consts (load those from a def file some time)
52// font colors - alpha is font alpha, which is inversed opaque
53#define C4GUI_CaptionFontClr 0xffffffff
54#define C4GUI_Caption2FontClr 0xffffff00
55#define C4GUI_InactCaptionFontClr 0xffafafaf
56#define C4GUI_ButtonFontClr 0xffffff00
57#define C4GUI_StatusFontClr 0xffffffff
58#define C4GUI_MessageFontClr 0xffffffff
59#define C4GUI_MessageFontAlpha 0xff000000
60#define C4GUI_InactMessageFontClr 0xffafafaf
61#define C4GUI_NotifyFontClr 0xffff0000
62#define C4GUI_ComboFontClr 0xffffffff
63#define C4GUI_CheckboxFontClr 0xffffffff
64#define C4GUI_CheckboxDisabledFontClr 0xffafafaf
65#define C4GUI_LogFontClr 0xffafafaf
66#define C4GUI_LogFontClr2 0xffff1f1f
67#define C4GUI_ErrorFontClr 0xffff1f1f
68#define C4GUI_ProgressBarFontClr 0xffffffff
69#define C4GUI_ContextFontClr 0xffffffff
70#define C4GUI_GfxTabCaptActiveClr 0xff000000
71#define C4GUI_GfxTabCaptInactiveClr 0xff000000
72#define C4GUI_HyperlinkFontClr 0xff8080ff
73
74// other colors
75#define C4GUI_ImportantBGColor 0xcf00007f
76#define C4GUI_ListBoxSelColor 0xafaf0000
77#define C4GUI_ListBoxInactSelColor 0xaf7f7f7f
78#define C4GUI_ContextSelColor 0xafaf0000
79#define C4GUI_ContextBGColor 0x4f3f1a00
80#define C4GUI_StandardBGColor 0x5f000000
81#define C4GUI_ActiveTabBGColor C4GUI_StandardBGColor
82#define C4GUI_ListBoxBarColor 0x7f772200
83#define C4GUI_EditBGColor 0x7f000000
84#define C4GUI_EditFontColor 0xffffffff
85
86#define C4GUI_ToolTipBGColor 0x00F1EA78
87#define C4GUI_ToolTipFrameColor 0x7f000000
88#define C4GUI_ToolTipColor 0xFF483222
89
90// winner/loser color marking
91#define C4GUI_WinningTextColor 0xffffdf00
92#define C4GUI_WinningBackgroundColor 0x4faf7a00
93#define C4GUI_LosingTextColor 0xffffffff
94#define C4GUI_LosingBackgroundColor 0x7fafafaf
95
96// border colors for 3D-frames
97#define C4GUI_BorderAlpha 0xaf
98#define C4GUI_BorderColor1 0x772200
99#define C4GUI_BorderColor2 0x331100
100#define C4GUI_BorderColor3 0xaa4400
101#define C4GUI_BorderColorA1 (C4GUI_BorderAlpha << 24 | C4GUI_BorderColor1)
102#define C4GUI_BorderColorA2 (C4GUI_BorderAlpha << 24 | C4GUI_BorderColor2)
103
104// GUI icon sizes
105#define C4GUI_IconWdt 40
106#define C4GUI_IconHgt 40
107#define C4GUI_IconExWdt 64
108#define C4GUI_IconExHgt 64
109
110// scroll bar size
111#define C4GUI_ScrollBarWdt 16
112#define C4GUI_ScrollBarHgt 16
113#define C4GUI_ScrollArrowHgt 16
114#define C4GUI_ScrollArrowWdt 16
115#define C4GUI_ScrollThumbHgt 16 // only for non-dynamic scroll thumbs
116#define C4GUI_ScrollThumbWdt 16 // only for non-dynamic scroll thumbs
117
118// button size
119#define C4GUI_ButtonHgt 32 // height of buttons
120#define C4GUI_BigButtonHgt 40 // height of bigger buttons (main menu)
121#define C4GUI_ButtonAreaHgt 40 // height of button areas
122#define C4GUI_DefButtonWdt 140 // width of default buttons
123#define C4GUI_DefButton2Wdt 120 // width of default buttons if there are two of them
124#define C4GUI_DefButton2HSpace 10 // horzontal space between two def dlg buttons
125
126#define C4GUI_CheckBoxLabelSpacing 4 // pixels between checkbox box and label
127
128// list box item spacing
129#define C4GUI_DefaultListSpacing 1 // 1 px of free space between two list items
130#define C4GUI_ListBoxBarIndent 10
131
132// default dialog box sizes
133#define C4GUI_MessageDlgWdt 500 // width of message dialog
134#define C4GUI_MessageDlgWdtMedium 360 // width of message dialog w/o much text
135#define C4GUI_MessageDlgWdtSmall 300 // width of message dialog w/o much text
136#define C4GUI_ProgressDlgWdt 500 // width of progress dialog
137#define C4GUI_InputDlgWdt 300
138#define C4GUI_DefDlgIndent 10 // indent for default dlg items
139#define C4GUI_DefDlgSmallIndent 4 // indent for dlg items that are grouped
140#define C4GUI_ProgressDlgVRoom 150 // height added to text height in progress dialog
141#define C4GUI_InputDlgVRoom 150
142#define C4GUI_ProgressDlgPBHgt 30 // height of progress bar in progress dlg
143#define C4GUI_InfoDlgWdt 620 // width of info dialog
144#define C4GUI_InfoDlgVRoom 100 // height added to text height in info dialog
145#define C4GUI_MaxToolTipWdt 500 // maximum width for tooltip boxes
146
147// time for tooltips to appear (msecs) -evaluated while drawing
148#define C4GUI_ToolTipShowTime 500 // 0.5 seconds
149
150// time for title bars to start scrolling to make longer text visible -evaluated while drawing
151#define C4GUI_TitleAutoScrollTime 3000 // 3 seconds
152
153// time interval for tab caption scrolling
154#define C4GUI_TabCaptionScrollTime 500 // 0.5 seconds
155
156// Z-ordering of dialogs
157#define C4GUI_Z_CHAT +2 // chat input dialog more important than other input dialogs
158#define C4GUI_Z_INPUT +1 // input dialogs on top of others
159#define C4GUI_Z_DEFAULT 0 // normal placement on top of viewports
160
161#define C4GUI_MinWoodBarHgt 23
162
163#define C4GUI_FullscreenDlg_TitleHeight C4UpperBoardHeight // pixels reserved for top of fullscreen dialog title
164#define C4GUI_FullscreenCaptionFontClr 0xffffff00
165
166namespace C4GUI
167{
168
169// some class predefs
170
171// C4Gui.cpp
172class Element; class Screen; class CMouse;
173class Resource; class ComponentAligner;
174
175// C4GuiLabels.cpp
176class Label; class WoodenLabel; class MultilineLabel;
177class HorizontalLine; class ProgressBar;
178class Picture; class Icon;
179class TextWindow;
180
181// C4GuiContainers.cpp
182class Container; class Window; class GroupBox; class Control;
183class ScrollBar; class ScrollWindow;
184
185// C4GuiButton.cpp
186class Button; template <class CallbackDlg, class Base> class CallbackButton;
187class IconButton;
188class CloseButton;
189class CloseIconButton;
190
191// C4GuiEdit.cpp
192class Edit;
193class LabeledEdit;
194class RenameEdit;
195
196enum InputResult // action to be taken when text is confirmed with enter
197{
198 IR_None = 0, // do nothing and continue pasting
199 IR_CloseDlg, // stop any pastes and close parent dialog successfully
200 IR_CloseEdit, // stop any pastes and remove this control
201 IR_Abort, // do nothing and stop any pastes
202};
203
204enum RenameResult
205{
206 RR_Invalid = 0, // rename not accepted; continue editing
207 RR_Accepted, // rename accepted; delete control
208 RR_Deleted, // control deleted - leave everything
209};
210
211// C4GuiCheckBox.cpp
212class CheckBox;
213
214// C4GuiListBox.cpp
215class ListBox;
216
217// C4GuiTabular.cpp
218class Tabular;
219
220// C4GuiMenu.cpp
221class ContextMenu;
222
223// C4GUIComboBox.cpp
224class ComboBox;
225
226// C4GuiDialog.cpp
227class Dialog; class MessageDialog; class ProgressDialog;
228class InputDialog; class InfoDialog; class TimedDialog;
229
230// inline
231class MenuHandler; class ContextHandler;
232
233// expand text like "Te&xt" to "Te<c ffff00>x</c>t"
234bool ExpandHotkeyMarkup(StdStrBuf &sText, char &rcHotkey);
235
236// make color readable on black: max alpha to 0x1f, max color hues
237uint32_t MakeColorReadableOnBlack(uint32_t &rdwClr);
238
239struct FLOAT_RECT
240{
241 float left, right, top, bottom;
242
243 // Surround floating point rectangle
244 operator C4Rect() const
245 {
246 return
247 {
248 static_cast<std::int32_t>(left),
249 static_cast<std::int32_t>(top),
250 static_cast<std::int32_t>(ceilf(x: right) - floorf(x: left)),
251 static_cast<std::int32_t>(ceilf(x: bottom) - floorf(x: top))
252 };
253 }
254};
255
256// menu handler: generic context menu callback
257class MenuHandler
258{
259public:
260 MenuHandler() {}
261 virtual ~MenuHandler() {}
262
263 virtual void OnOK(Element *pTarget) = 0; // menu item selected
264};
265
266// context handler: opens context menu on right-click or menu key
267class ContextHandler
268{
269private:
270 int32_t iRefs;
271
272public:
273 ContextHandler() : iRefs(0) {}
274 virtual ~ContextHandler() {}
275
276 virtual bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) = 0; // context queried - ret true if handled
277 virtual ContextMenu *OnSubcontext(Element *pOnElement) = 0; // subcontext queried
278
279 inline void Ref() { ++iRefs; }
280 inline void DeRef() { if (!--iRefs) delete this; }
281};
282
283// generic callback handler
284class BaseCallbackHandler
285{
286private:
287 int32_t iRefs;
288
289public:
290 BaseCallbackHandler() : iRefs(0) {}
291 virtual ~BaseCallbackHandler() {}
292
293 inline void Ref() { ++iRefs; }
294 inline void DeRef() { if (!--iRefs) delete this; }
295
296 virtual void DoCall(class Element *pElement) = 0;
297};
298
299template <class CB> class CallbackHandler : public BaseCallbackHandler
300{
301public:
302 typedef void (CB::*Func)(class Element *pElement);
303
304private:
305 CB *pCBClass;
306 Func CBFunc;
307
308public:
309 virtual void DoCall(class Element *pElement) override
310 {
311 ((pCBClass)->*CBFunc)(pElement);
312 }
313
314 CallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {}
315};
316
317template <class CB> class CallbackHandlerNoPar : public BaseCallbackHandler
318{
319public:
320 typedef void (CB::*Func)();
321
322private:
323 CB *pCBClass;
324 Func CBFunc;
325
326public:
327 virtual void DoCall(class Element *pElement) override
328 {
329 ((pCBClass)->*CBFunc)();
330 }
331
332 CallbackHandlerNoPar(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {}
333};
334
335template <class CB, class ParType> class CallbackHandlerExPar : public BaseCallbackHandler
336{
337public:
338 typedef void (CB::*Func)(ParType);
339
340private:
341 CB *pCBClass;
342 ParType par;
343 Func CBFunc;
344
345public:
346 virtual void DoCall(class Element *pElement) override
347 {
348 ((pCBClass)->*CBFunc)(par);
349 }
350
351 CallbackHandlerExPar(CB *pTarget, Func rFunc, ParType par) : pCBClass(pTarget), CBFunc(rFunc), par(par) {}
352};
353
354// callback with parameter coming from calling class
355template <class ParType> class BaseParCallbackHandler : public BaseCallbackHandler
356{
357private:
358 virtual void DoCall(class Element *pElement) override { assert(false); } // no-par: Not to be called
359
360public:
361 BaseParCallbackHandler() {}
362
363 virtual void DoCall(ParType par) = 0;
364};
365
366template <class CB, class ParType> class ParCallbackHandler : public BaseParCallbackHandler<ParType>
367{
368public:
369 typedef void (CB::*Func)(ParType par);
370
371private:
372 CB *pCBClass;
373 Func CBFunc;
374
375public:
376 virtual void DoCall(ParType par) override { ((pCBClass)->*CBFunc)(par); }
377
378 ParCallbackHandler(CB *pTarget, Func rFunc) : pCBClass(pTarget), CBFunc(rFunc) {}
379};
380
381// three facets for left/top, middle and right/bottom of an auto-sized bar
382struct DynBarFacet
383{
384 C4Facet fctBegin, fctMiddle, fctEnd;
385
386 void SetHorizontal(C4Surface &rBySfc, int iHeight = 0, int iBorderWidth = 0);
387 void SetHorizontal(C4Facet &rByFct, int32_t iBorderWidth = 0);
388 void Clear() { fctBegin.Default(); fctMiddle.Default(); fctEnd.Default(); }
389};
390
391// facets used to draw a scroll bar
392struct ScrollBarFacets
393{
394 DynBarFacet barScroll;
395 C4Facet fctScrollDTop, fctScrollPin, fctScrollDBottom;
396
397 void Set(const C4Facet &rByFct, int32_t iPinIndex = 0);
398 void Clear() { barScroll.Clear(); fctScrollDTop.Default(); fctScrollPin.Default(); fctScrollDBottom.Default(); }
399};
400
401// a generic gui-element
402class Element
403{
404private:
405 StdStrBuf ToolTip; // MouseOver - status text
406
407protected:
408 Container *pParent; // owning container
409 Element *pPrev, *pNext; // previous and next element of same container
410 Window *pDragTarget; // target that is dragged around when the user drags this element
411 int32_t iDragX, iDragY; // drag start pos
412 bool fDragging; // if set, mouse is down on component and dragging enabled
413 ContextHandler *pContextHandler; // context handler to be called upon context menu request
414
415public:
416 bool fVisible; // if false, component (and subcomponents) are not drawn
417
418protected:
419 C4Rect rcBounds; // element bounds
420
421 virtual void Draw(C4FacetEx &cgo) { DrawElement(cgo); } // draw this class (this + any contents)
422 virtual void DrawElement(C4FacetEx &cgo) {} // draw element itself
423
424 virtual void RemoveElement(Element *pChild); // clear ptrs
425
426 virtual void UpdateSize(); // called when own size changed
427 virtual void UpdatePos(); // called when own position changed
428
429 void Draw3DFrame(C4FacetEx &cgo, bool fUp = false, int32_t iIndent = 1, uint8_t byAlpha = C4GUI_BorderAlpha, bool fDrawTop = true, int32_t iTopOff = 0, bool fDrawLeft = true, int32_t iLeftOff = 0); // draw frame around element
430 void DrawBar(C4FacetEx &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds
431 void DrawVBar(C4FacetEx &cgo, DynBarFacet &rFacets); // draw gfx bar within element bounds
432 void DrawHBarByVGfx(C4FacetEx &cgo, DynBarFacet &rFacets); // draw horizontal gfx bar within element bounds, using gfx of vertical one
433
434 virtual bool IsOwnPtrElement() { return false; } // if true is returned, item will not be deleted when container is cleared
435 virtual bool IsExternalDrawDialog() { return false; }
436 virtual bool IsMenu() { return false; }
437
438 // for listbox-selection by character input
439 virtual bool CheckNameHotkey(const char *c) { return false; }
440
441public:
442 virtual Container *GetContainer() { return pParent; } // returns parent for elements; this for containers
443
444 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam); // input: mouse movement or buttons
445 virtual void MouseEnter(CMouse &rMouse) {} // called when mouse cursor enters element region
446 virtual void MouseLeave(CMouse &rMouse) {} // called when mouse cursor leaves element region
447
448 virtual void StartDragging(CMouse &rMouse, int32_t iX, int32_t iY, uint32_t dwKeyParam); // called by element in MouseInput: start dragging
449 virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, uint32_t dwKeyParam); // called by mouse: dragging process
450 virtual void StopDragging(CMouse &rMouse, int32_t iX, int32_t iY, uint32_t dwKeyParam); // called by mouse: mouse released after dragging process
451
452 virtual bool OnHotkey(char cHotkey) { return false; } // return true when hotkey has been processed
453
454public:
455 bool DoContext(); // open context menu if assigned
456
457public:
458 Element();
459 virtual ~Element();
460
461 Container *GetParent() { return pParent; } // get owning container
462 virtual class Dialog *GetDlg(); // return contained dialog
463 virtual Screen *GetScreen(); // return contained screen
464 virtual Control *IsFocusElement() { return nullptr; } // return control to gain focus in search-cycle
465
466 virtual void UpdateOwnPos() {} // called when element bounds were changed externally
467 void ScreenPos2ClientPos(int32_t &riX, int32_t &riY); // transform screen coordinates to element coordinates
468 void ClientPos2ScreenPos(int32_t &riX, int32_t &riY); // transform element coordinates to screen coordinates
469
470 void SetToolTip(const char *szNewTooltip); // update used tooltip
471 const char *GetToolTip(); // return tooltip const char* (own or fallback to parent)
472
473 int32_t GetWidth() { return rcBounds.Wdt; }
474 int32_t GetHeight() { return rcBounds.Hgt; }
475 C4Rect &GetBounds() { return rcBounds; }
476 void SetBounds(const C4Rect &rcNewBound) { rcBounds = rcNewBound; UpdatePos(); UpdateSize(); }
477 virtual C4Rect &GetClientRect() { return rcBounds; }
478 C4Rect GetContainedClientRect() { C4Rect rc = GetClientRect(); rc.x = rc.y = 0; return rc; }
479 Element *GetNext() const { return pNext; }
480 Element *GetPrev() const { return pPrev; }
481 virtual Element *GetFirstNestedElement(bool fBackwards) { return this; }
482 virtual Element *GetFirstContained() { return nullptr; }
483 bool IsInActiveDlg(bool fForKeyboard);
484 virtual bool IsParentOf(Element *pEl) { return false; } // whether this is the parent container (directly or recursively) of the passed element
485
486 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
487
488 bool IsVisible();
489 virtual void SetVisibility(bool fToValue);
490
491 virtual int32_t GetListItemTopSpacing() { return C4GUI_DefaultListSpacing; }
492 virtual bool GetListItemTopSpacingBar() { return false; }
493
494 void SetDragTarget(Window *pToWindow) { pDragTarget = pToWindow; }
495
496 void SetContextHandler(ContextHandler *pNewHd) // takes over ptr
497 {
498 if (pContextHandler) pContextHandler->DeRef();
499 if ((pContextHandler = pNewHd)) pNewHd->Ref();
500 }
501
502 virtual ContextHandler *GetContextHandler(); // get context handler to be used (own or parent)
503
504 friend class Container; friend class TextWindow; friend class ListBox;
505};
506
507// a simple text label on the screen
508class Label : public Element
509{
510protected:
511 StdStrBuf sText; // label text
512 StdStrBuf sHyperlink; // URL to be visited when clicked
513 uint32_t dwFgClr; // text color
514 int32_t x0, iAlign; // x-textstart-pos; horizontal alignment
515 CStdFont *pFont;
516 char cHotkey; // hotkey for this label
517 bool fAutosize;
518 bool fMarkup;
519
520 Control *pClickFocusControl; // control that gets focus if the label is clicked or hotkey is pressed
521
522 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse
523
524 virtual void DrawElement(C4FacetEx &cgo) override; // output label
525 virtual void UpdateOwnPos() override;
526
527 virtual bool OnHotkey(char cHotkey) override; // focus control on correct hotkey
528
529 virtual int32_t GetLeftIndent() { return 0; }
530
531public:
532 Label(std::string_view lblText, int32_t iX0, int32_t iTop, int32_t iAlign = ALeft, uint32_t dwFClr = 0xffffffff, CStdFont *pFont = nullptr, bool fMakeReadableOnBlack = true, bool fMarkup = true);
533 Label(std::string_view lblText, const C4Rect &rcBounds, int32_t iAlign = ALeft, uint32_t dwFClr = 0xffffffff, CStdFont *pFont = nullptr, bool fMakeReadableOnBlack = true, bool fAutosize = true, bool fMarkup = true);
534
535 void SetText(const char *szText, bool fAllowHotkey = true); // update text
536 void SetText(std::string_view toText, bool fAllowHotkey = true); // update text
537 const char *GetText() { return sText.getData(); } // retrieve current text
538 void SetClickFocusControl(Control *pToCtrl) { pClickFocusControl = pToCtrl; }
539 void SetColor(uint32_t dwToClr, bool fMakeReadableOnBlack = true) { dwFgClr = fMakeReadableOnBlack ? MakeColorReadableOnBlack(rdwClr&: dwToClr) : dwToClr; } // update label color
540 void SetX0(int32_t iToX0);
541 void SetAutosize(bool fToVal) { fAutosize = fToVal; }
542 void SetHyperlink(const char *szURL); // make blue and underlined and open this URL when clicked
543};
544
545// a label with some wood behind
546// used for captions
547class WoodenLabel : public Label
548{
549private:
550 time_t tAutoScrollDelay; // if set and text is longer than would fit, the label will automatically start moving if not changed and displayed for a while
551 time_t tLastChangeTime; // time when the label text was changed last. 0 if not initialized; set upon first drawing
552 int32_t iScrollPos, iScrollDir;
553 int32_t iRightIndent;
554
555protected:
556 virtual void DrawElement(C4FacetEx &cgo) override; // output label
557
558 C4Facet fctIcon; // icon shown at left-side of label; if set, text is aligned to left
559
560 virtual int32_t GetLeftIndent() override { return fctIcon.Surface ? rcBounds.Hgt : 0; }
561 int32_t GetRightIndent() const { return iRightIndent; }
562
563public:
564 WoodenLabel(const char *szLblText, const C4Rect &rcBounds, uint32_t dwFClr = 0xffffffff, CStdFont *pFont = nullptr, int32_t iAlign = ACenter, bool fMarkup = true)
565 : Label(szLblText, rcBounds, iAlign, dwFClr, pFont, true, true, fMarkup), tAutoScrollDelay(0), tLastChangeTime(0), iScrollPos(0), iScrollDir(0), iRightIndent(0)
566 {
567 SetAutosize(false); this->rcBounds = rcBounds;
568 } // re-sets bounds after SetText
569
570 static int32_t GetDefaultHeight(CStdFont *pUseFont = nullptr);
571
572 void SetIcon(const C4Facet &rfctIcon);
573 void SetAutoScrollTime(time_t tDelay) { tAutoScrollDelay = tDelay; ResetAutoScroll(); }
574 void ResetAutoScroll() { tLastChangeTime = 0; iScrollPos = iScrollDir = 0; }
575 void SetRightIndent(int32_t iNewIndent) { iRightIndent = iNewIndent; }
576};
577
578// a multiline label with automated text clipping
579// used for display of log buffers
580class MultilineLabel : public Element
581{
582protected:
583 C4LogBuffer Lines;
584 bool fMarkup;
585
586protected:
587 virtual void DrawElement(C4FacetEx &cgo) override; // output label
588 void UpdateHeight();
589 virtual void UpdateSize() override; // update label height
590
591public:
592 MultilineLabel(const C4Rect &rcBounds, int32_t iMaxLines, int32_t iMaxBuf, const char *szIndentChars, bool fAutoGrow, bool fMarkup);
593
594 void AddLine(const char *szLine, CStdFont *pFont, uint32_t dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont); // add line of text
595 void Clear(bool fDoUpdate); // clear lines
596
597 friend class TextWindow;
598};
599
600// a bar that show progress
601class ProgressBar : public Element
602{
603protected:
604 int32_t iProgress, iMax;
605
606 virtual void DrawElement(C4FacetEx &cgo) override; // draw progress bar
607
608public:
609 ProgressBar(C4Rect &rrcBounds, int32_t iMaxProgress = 100)
610 : Element(), iProgress(0), iMax(iMaxProgress)
611 {
612 rcBounds = rrcBounds; UpdatePos();
613 }
614
615 void SetProgress(int32_t iToProgress) { iProgress = iToProgress; }
616};
617
618// Auxiliary design gfx: a horizontal line
619class HorizontalLine : public Element
620{
621protected:
622 uint32_t dwClr, dwShadowClr;
623
624 virtual void DrawElement(C4FacetEx &cgo) override; // draw horizontal line
625
626public:
627 HorizontalLine(C4Rect &rrcBounds, uint32_t dwClr = 0x000000, uint32_t dwShadowClr = 0xaf7f7f7f)
628 : Element(), dwClr(dwClr), dwShadowClr(dwShadowClr)
629 {
630 SetBounds(rrcBounds);
631 }
632};
633
634// picture displaying a FacetEx
635class Picture : public Element
636{
637protected:
638 C4FacetExSurface Facet; // picture facet
639 bool fAspect; // preserve width/height-ratio when drawing
640 bool fCustomDrawClr; // custom drawing color for clrbyowner-surfaces
641 uint32_t dwDrawClr;
642 bool fAnimate; // if true, the picture is animated. Whoaa!
643 int32_t iPhaseTime, iAnimationPhase, iDelay; // current animation phase - undefined if not animated
644
645 virtual void DrawElement(C4FacetEx &cgo) override; // draw the image
646
647public:
648 Picture(const C4Rect &rcBounds, bool fAspect); // does not load image
649
650 const C4FacetExSurface &GetFacet() const { return Facet; } // get picture facet
651 C4FacetExSurface &GetMFacet() { return Facet; } // get picture facet
652 void SetFacet(const C4Facet &fct) { Facet.Clear(); Facet.Set(fct); }
653 bool EnsureOwnSurface(); // create an own surface, if it's just a link
654 void SetDrawColor(uint32_t dwToClr) { dwDrawClr = dwToClr; fCustomDrawClr = true; }
655 void SetAnimated(bool fEnabled, int iDelay); // starts/stops cycling through all phases of the specified facet
656};
657
658// picture displaying two facets
659class OverlayPicture : public Picture
660{
661protected:
662 int iBorderSize; // border of overlay image if not zoomed
663 C4Facet OverlayImage; // image to be displayed on top of the actual picture
664
665 virtual void DrawElement(C4FacetEx &cgo) override; // draw the image
666
667public:
668 OverlayPicture(const C4Rect &rcBounds, bool fAspect, const C4Facet &rOverlayImage, int iBorderSize); // does not load image
669};
670
671// icon indices
672enum Icons
673{
674 Ico_Empty = -2, // for context menus only
675 Ico_None = -1,
676 Ico_Clonk = 0,
677 Ico_Notify = 1,
678 Ico_Wait = 2,
679 Ico_NetWait = 3,
680 Ico_Host = 4,
681 Ico_Client = 5,
682 Ico_UnknownClient = 6,
683 Ico_UnknownPlayer = 7,
684 Ico_ObserverClient = 8,
685 Ico_Player = 9,
686 Ico_Resource = 10,
687 Ico_Error = 11,
688 Ico_SavegamePlayer = 12,
689 Ico_Save = 13,
690 Ico_Active = 14,
691 Ico_Options = 14,
692 Ico_Inactive = 15,
693 Ico_Kick = 16,
694 Ico_Loading = 17,
695 Ico_Confirm = 18,
696 Ico_Team = 19,
697 Ico_AddPlr = 20,
698 Ico_Record = 21,
699 Ico_Chart = 21,
700 Ico_Gfx = 22,
701 Ico_Sound = 23,
702 Ico_Keyboard = 24,
703 Ico_Gamepad = 25,
704 Ico_MouseOff = 26,
705 Ico_MouseOn = 27,
706 Ico_Help = 28,
707 Ico_Definition = 29,
708 Ico_GameRunning = 30,
709 Ico_Lobby = 31,
710 Ico_RuntimeJoin = 32,
711 Ico_Exit = 33,
712 Ico_Close = 34,
713 Ico_Rank1 = 35,
714 Ico_Rank2 = 36,
715 Ico_Rank3 = 37,
716 Ico_Rank4 = 38,
717 Ico_Rank5 = 39,
718 Ico_Rank6 = 40,
719 Ico_Rank7 = 41,
720 Ico_Rank8 = 42,
721 Ico_Rank9 = 43,
722 Ico_OfficialServer = 44,
723 Ico_Surrender = 45,
724 Ico_MeleeLeague = 46,
725 Ico_Ready = 47,
726 Ico_Star = 48,
727 Ico_Disconnect = 49,
728 Ico_View = 50,
729 Ico_RegJoinOnly = 51,
730 Ico_NoSound = 52,
731
732 Ico_Extended = 0x100, // icon index offset for extended icons
733
734 Ico_Ex_RecordOff = Ico_Extended + 0,
735 Ico_Ex_RecordOn = Ico_Extended + 1,
736 Ico_Ex_FairCrew = Ico_Extended + 2,
737 Ico_Ex_NormalCrew = Ico_Extended + 3,
738 Ico_Ex_LeagueOff = Ico_Extended + 4,
739 Ico_Ex_LeagueOn = Ico_Extended + 5,
740 Ico_Ex_InternetOff = Ico_Extended + 6,
741 Ico_Ex_InternetOn = Ico_Extended + 7,
742 Ico_Ex_League = Ico_Extended + 8,
743 Ico_Ex_FairCrewGray = Ico_Extended + 9,
744 Ico_Ex_NormalCrewGray = Ico_Extended + 10,
745 Ico_Ex_Locked = Ico_Extended + 11,
746 Ico_Ex_Unlocked = Ico_Extended + 12,
747 Ico_Ex_LockedFrontal = Ico_Extended + 13,
748 Ico_Ex_Update = Ico_Extended + 14,
749 Ico_Ex_Chat = Ico_Extended + 15,
750 Ico_Ex_GameList = Ico_Extended + 16,
751 Ico_Ex_Comment = Ico_Extended + 17,
752};
753
754// cute, litte, useless thingy
755class Icon : public Picture
756{
757public:
758 Icon(const C4Rect &rcBounds, Icons icoIconIndex);
759
760 void SetIcon(Icons icoNewIconIndex);
761 static C4FacetEx GetIconFacet(Icons icoIconIndex);
762};
763
764// a collection of gui-elements
765class Container : public Element
766{
767protected:
768 Element *pFirst, *pLast; // contained elements
769
770 virtual void Draw(C4FacetEx &cgo) override; // draw all elements
771 virtual void ElementSizeChanged(Element *pOfElement) {} // called when an element size is changed
772 virtual void ElementPosChanged(Element *pOfElement) {} // called when an element position is changed
773
774 virtual void AfterElementRemoval()
775 {
776 if (pParent) pParent->AfterElementRemoval();
777 } // called by ScrollWindow to parent after an element has been removed
778
779 virtual bool OnHotkey(char cHotkey) override; // check all contained elements for hotkey
780
781public:
782 Container();
783 ~Container();
784
785 void Clear(); // delete all child elements
786 void ClearChildren(); // delete all child elements
787 virtual void RemoveElement(Element *pChild) override; // remove child element from container
788 void MakeLastElement(Element *pChild); // resort to the end of the list
789 void AddElement(Element *pChild); // add child element to container
790 void ReaddElement(Element *pChild); // resort child element to end of list
791 void InsertElement(Element *pChild, Element *pInsertBefore); // add child element to container, ordered directly before given, other element
792 Element *GetNextNestedElement(Element *pPrevElement, bool fBackwards); // get next element after given, applying recursion
793 virtual Element *GetFirstContained() override { return pFirst; }
794 virtual Element *GetLastContained() { return pLast; }
795 virtual Element *GetFirstNestedElement(bool fBackwards) override;
796 Element *GetFirst() { return pFirst; }
797 Element *GetLast() { return pLast; }
798 virtual Container *GetContainer() override { return this; } // returns parent for elements; this for containers
799 Element *GetElementByIndex(int32_t i); // get indexed child element
800 int32_t GetElementCount();
801 virtual void SetVisibility(bool fToValue) override;
802
803 virtual bool IsFocused(Control *pCtrl) { return pParent ? pParent->IsFocused(pCtrl) : false; }
804 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
805 virtual bool IsParentOf(Element *pEl) override; // whether this is the parent container (directly or recursively) of the passed element
806
807 virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) {} // apply child drawing offset
808 virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) {} // subtract child drawing offset
809
810 friend class Element; friend class ScrollWindow;
811};
812
813// a rectangled control that contains other elements
814class Window : public Container
815{
816protected:
817 C4Rect rcClientRect; // area for contained elements
818
819 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
820 virtual void Draw(C4FacetEx &cgo) override; // draw this window
821
822public:
823 Window();
824
825 void SetPos(int32_t iXPos, int32_t iYPos)
826 {
827 rcBounds.x = iXPos; rcBounds.y = iYPos; UpdatePos();
828 }
829
830 virtual void UpdateOwnPos() override; // update client rect
831 virtual C4Rect &GetClientRect() override { return rcClientRect; }
832
833 virtual void ApplyElementOffset(int32_t &riX, int32_t &riY) override
834 {
835 riX -= rcClientRect.x; riY -= rcClientRect.y;
836 }
837
838 virtual void ApplyInvElementOffset(int32_t &riX, int32_t &riY) override
839 {
840 riX += rcClientRect.x; riY += rcClientRect.y;
841 }
842
843 virtual bool IsComponentOutsideClientArea() { return false; } // if set, drawing routine of subcomponents will clip to Bounds rather than to ClientRect
844
845 // get margins from bounds to client rect
846 virtual int32_t GetMarginTop() { return 0; }
847 virtual int32_t GetMarginLeft() { return 0; }
848 virtual int32_t GetMarginRight() { return 0; }
849 virtual int32_t GetMarginBottom() { return 0; }
850};
851
852// a scroll bar
853class ScrollBar : public Element
854{
855protected:
856 bool fScrolling; // if set, scrolling is currently enabled
857 bool fAutoHide; // if set, bar is made invisible if scrolling is not possible anyway
858 int32_t iScrollThumbSize; // height(/width) of scroll thumb
859 int32_t iScrollPos; // y(/x) offset of scroll thumb
860 bool fTopDown, fBottomDown; // whether scrolling buttons are pressed
861 bool fHorizontal; // if set, the scroll bar is horizontal instead of vertical
862 int32_t iCBMaxRange; // range for callback class
863
864 ScrollWindow *pScrollWindow; // associated scrolled window - may be 0 for callback scrollbars
865 BaseParCallbackHandler<int32_t> *pScrollCallback; // callback called when scroll pos changes
866
867 ScrollBarFacets *pCustomGfx;
868
869 void Update(); // update scroll bar according to window
870 void OnPosChanged(); // update window according to scroll bar, and/or do callbacks
871
872 // mouse handling
873 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
874 virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // dragging: allow dragging of thumb
875 virtual void MouseLeave(CMouse &rMouse) override; // mouse leaves with button down: reset down state of buttons
876
877 virtual void DrawElement(C4FacetEx &cgo) override; // draw scroll bar
878
879 // suppress scrolling pin for very narrow menus
880 bool HasPin()
881 {
882 if (fHorizontal) return rcBounds.Wdt > (2 * C4GUI_ScrollArrowWdt + C4GUI_ScrollThumbWdt);
883 else return rcBounds.Hgt > (2 * C4GUI_ScrollArrowHgt + C4GUI_ScrollThumbHgt);
884 }
885
886 int32_t GetMaxScroll()
887 {
888 if (fHorizontal) return HasPin() ? GetBounds().Wdt - 2 * C4GUI_ScrollArrowWdt - iScrollThumbSize : 100;
889 else return HasPin() ? GetBounds().Hgt - 2 * C4GUI_ScrollArrowHgt - iScrollThumbSize : 100;
890 }
891
892 int32_t GetScrollByPos(int32_t iX, int32_t iY)
893 {
894 return BoundBy<int32_t>(bval: (fHorizontal ? iX - C4GUI_ScrollArrowWdt : iY - C4GUI_ScrollArrowHgt) - iScrollThumbSize / 2, lbound: 0, rbound: GetMaxScroll());
895 }
896
897 bool IsScrolling() { return fScrolling; }
898
899public:
900 ScrollBar(C4Rect &rcBounds, ScrollWindow *pWin); // ctor for scroll window
901 ScrollBar(C4Rect &rcBounds, bool fHorizontal, BaseParCallbackHandler<int32_t> *pCB, int32_t iCBMaxRange = 256); // ctor for callback
902 ~ScrollBar();
903
904 // change style
905 void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoHide)
906 {
907 pCustomGfx = pToGfx; this->fAutoHide = fAutoHide;
908 }
909
910 // change scroll pos in a [0, iCBMaxRange-1] scale
911 void SetScrollPos(int32_t iToPos) { iScrollPos = iToPos * GetMaxScroll() / (iCBMaxRange - 1); }
912
913 friend class ScrollWindow;
914};
915
916// a window that can be scrolled
917class ScrollWindow : public Window
918{
919protected:
920 ScrollBar *pScrollBar; // vertical scroll bar associated with the window
921 int32_t iScrollY; // vertical scroll pos
922 int32_t iClientHeight; // client rect height
923 bool fHasBar;
924 int32_t iFrozen; // if >0, no scrolling updates are done (used during window refill)
925
926 // pass element updates through to parent window
927 virtual void ElementSizeChanged(Element *pOfElement) override // called when an element size is changed
928 {
929 Window::ElementSizeChanged(pOfElement);
930 if (pParent) pParent->ElementSizeChanged(pOfElement);
931 }
932
933 virtual void ElementPosChanged(Element *pOfElement) override // called when an element position is changed
934 {
935 Window::ElementPosChanged(pOfElement);
936 if (pParent) pParent->ElementPosChanged(pOfElement);
937 }
938
939 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override;
940
941public:
942 ScrollWindow(Window *pParentWindow); // create scroll window in client area of window
943 ~ScrollWindow() { if (pScrollBar) pScrollBar->pScrollWindow = nullptr; }
944
945 virtual bool IsComponentOutsideClientArea() override { return true; } // always clip drawing routine of subcomponents to Bounds
946 void Update(); // update client rect and scroll bar according to window
947 virtual void UpdateOwnPos() override;
948 void Freeze() { ++iFrozen; }
949 void UnFreeze() { if (!--iFrozen) Update(); }
950 bool IsFrozen() const { return !!iFrozen; }
951
952 void SetClientHeight(int32_t iToHgt) // set new client height
953 {
954 iClientHeight = iToHgt; Update();
955 }
956
957 // change style
958 void SetDecoration(ScrollBarFacets *pToGfx, bool fAutoScroll)
959 {
960 if (pScrollBar) pScrollBar->SetDecoration(pToGfx, fAutoHide: fAutoScroll);
961 }
962
963 void SetScroll(int32_t iToScroll); // sets new scrolling; does not update scroll bar
964 void ScrollToBottom(); // set scrolling to bottom range; updates scroll bar
965 void ScrollPages(int iPageCount); // scroll down by multiples of visible height; updates scroll bar
966 void ScrollBy(int iAmount); // scroll down by vertical pixel amount; updates scroll bar
967 void ScrollRangeInView(int32_t iY, int32_t iHgt); // sets scrolling so range is in view; updates scroll bar
968 bool IsRangeInView(int32_t iY, int32_t iHgt); // returns whether scrolling range is in view
969
970 int32_t GetScrollY() { return iScrollY; }
971
972 void SetScrollBarEnabled(bool fToVal);
973 bool IsScrollBarEnabled() { return fHasBar; }
974
975 bool IsScrollingActive() { return fHasBar && pScrollBar && pScrollBar->IsScrolling(); }
976 bool IsScrollingNecessary() { return iClientHeight > rcBounds.Hgt; }
977
978 friend class ScrollBar;
979};
980
981// a collection of components
982class GroupBox : public Window
983{
984private:
985 StdStrBuf sTitle;
986 CStdFont *pFont;
987 uint32_t dwFrameClr, dwTitleClr, dwBackClr;
988 int32_t iMargin;
989
990 CStdFont *GetTitleFont() const;
991
992public:
993 GroupBox(const C4Rect &rtBounds) : Window(), pFont(nullptr), dwFrameClr(0u), dwTitleClr(C4GUI_CaptionFontClr), dwBackClr(0xffffffff), iMargin(4)
994 {
995 // init client rect
996 SetBounds(rtBounds);
997 }
998
999 void SetFont(CStdFont *pToFont) { pFont = pToFont; }
1000 void SetColors(uint32_t dwFrameClr, uint32_t dwTitleClr, uint32_t dwBackClr = 0xffffffff) { this->dwFrameClr = dwFrameClr; this->dwTitleClr = dwTitleClr; this->dwBackClr = dwBackClr; }
1001 void SetTitle(const char *szToTitle) { if (szToTitle && *szToTitle) sTitle.Copy(pnData: szToTitle); else sTitle.Clear(); UpdateOwnPos(); }
1002 void SetMargin(int32_t iNewMargin) { iMargin = iNewMargin; UpdateOwnPos(); }
1003
1004 bool HasTitle() const { return !!sTitle.getLength(); }
1005
1006 virtual void DrawElement(C4FacetEx &cgo) override; // draw frame
1007
1008 virtual int32_t GetMarginTop() override { return HasTitle() ? iMargin + GetTitleFont()->GetLineHeight() : iMargin; }
1009 virtual int32_t GetMarginLeft() override { return iMargin; }
1010 virtual int32_t GetMarginRight() override { return iMargin; }
1011 virtual int32_t GetMarginBottom() override { return iMargin; }
1012};
1013
1014// a control that may have focus
1015class Control : public Window
1016{
1017private:
1018 class C4KeyBinding *pKeyContext;
1019
1020protected:
1021 virtual bool CharIn(const char *c) { return false; } // input: character key pressed - should return false for none-character-inputs
1022 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse. left-click sets focus
1023
1024 void DisableFocus(); // called when control gets disabled: Make sure it loses focus
1025 virtual bool IsFocusOnClick() { return true; } // defaultly, controls get focused on left-down
1026 virtual Control *IsFocusElement() override { return this; } // this control can gain focus
1027 virtual void OnGetFocus(bool fByMouse) {} // callback when control gains focus
1028 virtual void OnLooseFocus() {} // callback when control looses focus
1029
1030 bool KeyContext() { return DoContext(); }
1031
1032public:
1033 Control(const C4Rect &rtBounds);
1034 ~Control();
1035
1036 bool HasFocus() { return pParent && pParent->IsFocused(pCtrl: this); }
1037 bool HasDrawFocus();
1038
1039 friend class Dialog; friend class ListBox;
1040};
1041
1042// generic callback-functions to the dialog
1043template <class CallbackDlg> class DlgCallback
1044{
1045public:
1046 typedef void (CallbackDlg::*Func)(Control *pFromControl);
1047 typedef ContextMenu *(CallbackDlg::*ContextFunc)(Element *pFromElement, int32_t iX, int32_t iY);
1048 typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement);
1049};
1050
1051// multi-param callback-functions to the dialog
1052template <class CallbackDlg, class TEx> class DlgCallbackEx
1053{
1054public:
1055 typedef void (CallbackDlg::*ContextClickFunc)(Element *pTargetElement, TEx Extra);
1056};
1057
1058// a button. may be pressed.
1059class Button : public Control
1060{
1061private:
1062 class C4KeyBinding *pKeyButton;
1063 DynBarFacet *pCustomGfx, *pCustomGfxDown;
1064
1065protected:
1066 StdStrBuf sText; // button label
1067 bool fDown; // if set, button is currently held down
1068 bool fMouseOver; // if set, the mouse hovers over the button
1069 char cHotkey; // hotkey for this button
1070 bool fEnabled;
1071
1072 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
1073 virtual void MouseEnter(CMouse &rMouse) override; // mouse re-enters with button down: set button down
1074 virtual void MouseLeave(CMouse &rMouse) override; // mouse leaves with button down: reset down state
1075 virtual bool IsFocusOnClick() override { return false; } // buttons don't get focus on click (for easier input, e.g. in chatbox)
1076
1077 virtual void DrawElement(C4FacetEx &cgo) override; // draw dlg bg
1078
1079 virtual void OnPress(); // called when button is pressed
1080
1081 bool KeyButtonDown();
1082 bool KeyButtonUp();
1083 void SetDown(); // mark down and play sound
1084 void SetUp(bool fPress); // mark up and play sound
1085
1086 virtual bool OnHotkey(char cHotkey) override; // press btn on correct hotkey
1087
1088public:
1089 Button(const char *szBtnText, const C4Rect &rtBounds);
1090 ~Button();
1091
1092 void SetText(const char *szToText); // update button text (and hotkey)
1093
1094 void SetCustomGraphics(DynBarFacet *pCustomGfx, DynBarFacet *pCustomGfxDown)
1095 {
1096 this->pCustomGfx = pCustomGfx; this->pCustomGfxDown = pCustomGfxDown;
1097 }
1098
1099 void SetEnabled(bool fToVal) { fEnabled = fToVal; if (!fEnabled) fDown = false; }
1100};
1101
1102// button using icon image
1103class IconButton : public Button
1104{
1105protected:
1106 C4Facet fctIcon;
1107 uint32_t dwClr;
1108 bool fHasClr;
1109 bool fHighlight; // if true, the button is highlighted permanently
1110
1111 virtual void DrawElement(C4FacetEx &cgo) override; // draw icon and highlight if necessary
1112
1113public:
1114 IconButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey);
1115 void SetIcon(Icons eUseIcon);
1116 void SetFacet(const C4Facet &rCpy, uint32_t dwClr = 0u) { fctIcon = rCpy; }
1117 void SetColor(uint32_t dwClr) { fHasClr = true; this->dwClr = dwClr; }
1118 void SetHighlight(bool fToVal) { fHighlight = fToVal; }
1119};
1120
1121// button using arrow image
1122class ArrowButton : public Button
1123{
1124public:
1125 enum ArrowFct { Left = 0, Right = 1, Down = 2 };
1126
1127protected:
1128 ArrowFct eDir;
1129
1130 virtual void DrawElement(C4FacetEx &cgo) override; // draw arrow; highlight and down if necessary
1131
1132public:
1133 ArrowButton(ArrowFct eDir, const C4Rect &rtBounds, char cHotkey = 0);
1134
1135 static int32_t GetDefaultWidth();
1136 static int32_t GetDefaultHeight();
1137};
1138
1139// button using facets for highlight
1140class FacetButton : public Button
1141{
1142protected:
1143 C4Facet fctBase, fctHighlight;
1144 uint32_t dwTextClrInact, dwTextClrAct; // text colors for inactive/active button
1145 FLOAT_RECT rcfDrawBounds; // drawing bounds
1146
1147 // title drawing parameters
1148 int32_t iTxtOffX, iTxtOffY;
1149 uint8_t byTxtAlign; // ALeft, ACenter or ARight
1150 CStdFont *pFont; float fFontZoom;
1151
1152 virtual void DrawElement(C4FacetEx &cgo) override; // draw base facet or highlight facet if necessary
1153
1154public:
1155 FacetButton(const C4Facet &rBaseFct, const C4Facet &rHighlightFct, const FLOAT_RECT &rtfBounds, char cHotkey);
1156
1157 void SetTextColors(uint32_t dwClrInact, uint32_t dwClrAct)
1158 {
1159 dwTextClrInact = dwClrInact; dwTextClrAct = dwClrAct;
1160 }
1161
1162 void SetTextPos(int32_t iOffX, int32_t iOffY, uint8_t byAlign = ACenter)
1163 {
1164 iTxtOffX = iOffX; iTxtOffY = iOffY; byTxtAlign = byAlign;
1165 }
1166
1167 void SetTextFont(CStdFont *pFont, float fFontZoom = 1.0f)
1168 {
1169 this->pFont = pFont; this->fFontZoom = fFontZoom;
1170 }
1171};
1172
1173// a button doing some callback...
1174template <class CallbackDlg, class Base = Button> class CallbackButton : public Base
1175{
1176protected:
1177 CallbackDlg *pCB;
1178
1179 typename DlgCallback<CallbackDlg>::Func pCallbackFn; // callback function
1180
1181 virtual void OnPress() override
1182 {
1183 if (pCallbackFn)
1184 {
1185 CallbackDlg *pC = pCB;
1186 if (!pC) if (!(pC = reinterpret_cast<CallbackDlg *>(Base::GetDlg()))) return;
1187 (pC->*pCallbackFn)(this);
1188 }
1189 }
1190
1191public:
1192 CallbackButton(ArrowButton::ArrowFct eDir, const C4Rect &rtBounds, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB = nullptr)
1193 : Base(eDir, rtBounds, 0), pCallbackFn(pFn), pCB(pCB) {}
1194 CallbackButton(const char *szBtnText, const C4Rect &rtBounds, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB = nullptr)
1195 : Base(szBtnText, rtBounds), pCallbackFn(pFn), pCB(pCB) {}
1196 CallbackButton(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB = nullptr)
1197 : Base(eUseIcon, rtBounds, cHotkey), pCallbackFn(pFn), pCB(pCB) {}
1198 CallbackButton(int32_t iID, const C4Rect &rtBounds, char cHotkey, typename DlgCallback<CallbackDlg>::Func pFn, CallbackDlg *pCB = nullptr)
1199 : Base(iID, rtBounds, cHotkey), pCallbackFn(pFn), pCB(pCB) {}
1200};
1201
1202// a button doing some callback to any class
1203template <class CallbackDlg, class Base = Button> class CallbackButtonEx : public Base
1204{
1205protected:
1206 typedef CallbackDlg *CallbackDlgPointer;
1207 CallbackDlgPointer pCBTarget; // callback target
1208 typename DlgCallback<CallbackDlg>::Func pCallbackFn; // callback function
1209
1210 virtual void OnPress() override
1211 {
1212 (pCBTarget->*pCallbackFn)(this);
1213 }
1214
1215public:
1216 CallbackButtonEx(const char *szBtnText, const C4Rect &rtBounds, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn)
1217 : Base(szBtnText, rtBounds), pCBTarget(pCBTarget), pCallbackFn(pFn) {}
1218 CallbackButtonEx(Icons eUseIcon, const C4Rect &rtBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn)
1219 : Base(eUseIcon, rtBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) {}
1220 CallbackButtonEx(const C4Facet &fctBase, const C4Facet &fctHighlight, const FLOAT_RECT &rtfBounds, char cHotkey, CallbackDlgPointer pCBTarget, typename DlgCallback<CallbackDlg>::Func pFn)
1221 : Base(fctBase, fctHighlight, rtfBounds, cHotkey), pCBTarget(pCBTarget), pCallbackFn(pFn) {}
1222};
1223
1224// checkbox with a text label right of it
1225class CheckBox : public Control
1226{
1227private:
1228 bool fChecked;
1229 class C4KeyBinding *pKeyCheck;
1230 StdStrBuf sCaption;
1231 bool fMouseOn;
1232 BaseCallbackHandler *pCBHandler; // callback handler called if check state changes
1233 bool fEnabled;
1234 CStdFont *pFont;
1235 uint32_t dwEnabledClr, dwDisabledClr;
1236 char cHotkey;
1237
1238public:
1239 CheckBox(const C4Rect &rtBounds, const std::string &caption, bool fChecked);
1240 ~CheckBox();
1241
1242private:
1243 bool KeyCheck() { ToggleCheck(fByUser: true); return true; }
1244
1245public:
1246 void ToggleCheck(bool fByUser); // check on/off; do callback
1247
1248protected:
1249 virtual void UpdateOwnPos() override;
1250 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
1251 virtual void MouseEnter(CMouse &rMouse) override;
1252 virtual void MouseLeave(CMouse &rMouse) override;
1253 virtual bool IsFocusOnClick() override { return false; } // just check/uncheck on click; do not gain keyboard focus as well
1254 virtual Control *IsFocusElement() override { return fEnabled ? Control::IsFocusElement() : nullptr; } // this control can gain focus if enabled
1255 virtual void DrawElement(C4FacetEx &cgo) override; // draw checkbox
1256 virtual bool OnHotkey(char cHotkey) override; // return true when hotkey has been processed
1257
1258public:
1259 void SetChecked(bool fToVal) { fChecked = fToVal; } // set w/o callback
1260 bool GetChecked() const { return fChecked; }
1261 void SetOnChecked(BaseCallbackHandler *pCB);
1262 void SetEnabled(bool fToVal) { if (!(fEnabled = fToVal)) DisableFocus(); }
1263 void SetCaption(const std::string &caption);
1264
1265 const char *GetText() { return sCaption.getData(); }
1266
1267 void SetFont(CStdFont *pFont, uint32_t dwEnabledClr, uint32_t dwDisabledClr)
1268 {
1269 this->pFont = pFont; this->dwEnabledClr = dwEnabledClr; this->dwDisabledClr = dwDisabledClr;
1270 }
1271
1272 static bool GetStandardCheckBoxSize(int *piWdt, int *piHgt, const char *szForCaptionText, CStdFont *pUseFont); // get needed size to construct a checkbox
1273};
1274
1275// scrollable text box
1276class TextWindow : public Control
1277{
1278protected:
1279 ScrollWindow *pClientWindow; // client scrolling window
1280 Picture *pTitlePicture; // [optional]: Picture shown atop the text
1281 MultilineLabel *pLogBuffer; // buffer holding text data
1282 bool fDrawBackground, fDrawFrame; // whether dark background should be drawn (default true)
1283 size_t iPicPadding;
1284
1285 virtual void DrawElement(C4FacetEx &cgo) override; // draw text window
1286
1287 virtual void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed
1288 virtual void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed
1289 virtual void UpdateSize() override;
1290
1291 virtual Control *IsFocusElement() override { return nullptr; } // no focus element for now, because there's nothing to do (2do: scroll?)
1292
1293public:
1294 TextWindow(const 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);
1295
1296 void AddTextLine(const char *szText, CStdFont *pFont, uint32_t dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont = nullptr) // add text in a new line
1297 {
1298 if (pLogBuffer) pLogBuffer->AddLine(szLine: szText, pFont, dwClr, fDoUpdate, fMakeReadableOnBlack, pCaptionFont);
1299 }
1300
1301 void ScrollToBottom() // set scrolling to bottom range
1302 {
1303 if (pClientWindow) pClientWindow->ScrollToBottom();
1304 }
1305
1306 void ClearText(bool fDoUpdate)
1307 {
1308 if (pLogBuffer) pLogBuffer->Clear(fDoUpdate);
1309 }
1310
1311 int32_t GetScrollPos()
1312 {
1313 return pClientWindow ? pClientWindow->GetScrollY() : 0;
1314 }
1315
1316 void SetScrollPos(int32_t iPos)
1317 {
1318 if (pClientWindow) pClientWindow->ScrollRangeInView(iY: iPos, iHgt: 0);
1319 }
1320
1321 void UpdateHeight()
1322 {
1323 if (pLogBuffer) pLogBuffer->UpdateHeight();
1324 }
1325
1326 void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
1327 {
1328 fDrawBackground = fDrawBG; this->fDrawFrame = fDrawFrame; if (pClientWindow) pClientWindow->SetDecoration(pToGfx, fAutoScroll);
1329 }
1330
1331 void SetPicture(const C4Facet &rNewPic);
1332
1333 // get margins from bounds to client rect
1334 virtual int32_t GetMarginTop() override { return 8; }
1335 virtual int32_t GetMarginLeft() override { return 10; }
1336 virtual int32_t GetMarginRight() override { return 5; }
1337 virtual int32_t GetMarginBottom() override { return 8; }
1338};
1339
1340// context menu opened on right-click
1341class ContextMenu : public Window
1342{
1343private:
1344 // key bindings
1345 class C4KeyBinding *pKeySelUp, *pKeySelDown, *pKeySubmenu, *pKeyBack, *pKeyAbort, *pKeyConfirm, *pKeyHotkey;
1346
1347 bool KeySelUp();
1348 bool KeySelDown();
1349 bool KeySubmenu();
1350 bool KeyBack();
1351 bool KeyAbort();
1352 bool KeyConfirm();
1353 bool KeyHotkey(C4KeyCodeEx key);
1354
1355private:
1356 static int32_t iGlobalMenuIndex;
1357 int32_t iMenuIndex;
1358
1359 void UpdateElementPositions(); // stack all menu elements
1360
1361 // element adding made private: May only add entries
1362 bool AddElement(Element *pChild);
1363
1364public:
1365 // one text entry (icon+text+eventually right-arrow)
1366 class Entry : public Element
1367 {
1368 private:
1369 int32_t GetIconIndent() { return (icoIcon == -1) ? 0 : rcBounds.Hgt + 2; }
1370
1371 protected:
1372 StdStrBuf sText; // entry text
1373 char cHotkey; // entry hotkey
1374 Icons icoIcon; // icon to be drawn left of text
1375 MenuHandler *pMenuHandler; // callback when item is selected
1376 ContextHandler *pSubmenuHandler; // callback when submenu is opened
1377
1378 protected:
1379 virtual void DrawElement(C4FacetEx &cgo) override; // draw element
1380
1381 MenuHandler *GetAndZeroCallback()
1382 {
1383 MenuHandler *pMH = pMenuHandler; pMenuHandler = nullptr; return pMH;
1384 }
1385
1386 virtual void MouseLeave(CMouse &rMouse) override
1387 {
1388 if (GetParent()) static_cast<ContextMenu *>(GetParent())->MouseLeaveEntry(rMouse, pOldEntry: this);
1389 }
1390
1391 virtual bool OnHotkey(char cKey) override { return cKey == cHotkey; }
1392
1393 virtual bool IsMenu() override { return true; }
1394
1395 public:
1396 Entry(const char *szText, Icons icoIcon = Ico_None, MenuHandler *pMenuHandler = nullptr, ContextHandler *pSubmenuHandler = nullptr);
1397 ~Entry()
1398 {
1399 delete pMenuHandler;
1400 delete pSubmenuHandler;
1401 }
1402
1403 const char *GetText() { return sText.getData(); }
1404
1405 friend class ContextMenu;
1406 };
1407
1408protected:
1409 Element *pTarget; // target element; close menu if this is lost
1410 Element *pSelectedItem; // currently highlighted menu item
1411
1412 ContextMenu *pSubmenu; // currently open submenu
1413
1414 // mouse movement or buttons forwarded from screen:
1415 // Check bounds and forward as MouseInput to context menu or submenus
1416 // return whether inside context menu bounds
1417 bool CtxMouseInput(CMouse &rMouse, int32_t iButton, int32_t iScreenX, int32_t iScreenY, uint32_t dwKeyParam);
1418 virtual void RemoveElement(Element *pChild) override; // clear ptr - abort menu if target is destroyed
1419
1420 virtual bool CharIn(const char *c); // input: character key pressed - should return false for none-character-inputs
1421 virtual void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam) override; // input: mouse movement or buttons
1422 void MouseLeaveEntry(CMouse &rMouse, Entry *pOldEntry); // callback: mouse leaves - deselect menu item if no submenu is open (callback done by menu item)
1423
1424 virtual void DrawElement(C4FacetEx &cgo) override; // draw BG
1425 virtual void Draw(C4FacetEx &cgo) override; // draw inherited (element+contents) plus submenu
1426
1427 virtual bool IsMenu() override { return true; }
1428
1429 virtual Screen *GetScreen() override; // return screen by static var
1430
1431 virtual int32_t GetMarginTop() override { return 5; }
1432 virtual int32_t GetMarginLeft() override { return 5; }
1433 virtual int32_t GetMarginRight() override { return 5; }
1434 virtual int32_t GetMarginBottom() override { return 5; }
1435
1436 void SelectionChanged(bool fByUser); // other item selected: Close any submenu; do callbacks
1437
1438 virtual void ElementSizeChanged(Element *pOfElement) override; // called when an element size is changed
1439 virtual void ElementPosChanged(Element *pOfElement) override; // called when an element position is changed
1440
1441 void CheckOpenSubmenu(); // open submenu if present for selected item
1442 bool IsSubmenu(); // return whether it's a submenu
1443 void DoOK(); // do OK for selected item
1444
1445public:
1446 ContextMenu();
1447 ~ContextMenu();
1448
1449 void Open(Element *pTarget, int32_t iScreenX, int32_t iScreenY);
1450 void Abort(bool fByUser);
1451
1452 void AddItem(const char *szText, const char *szToolTip = nullptr, Icons icoIcon = Ico_None, MenuHandler *pMenuHandler = nullptr, ContextHandler *pSubmenuHandler = nullptr)
1453 {
1454 Element *pNew = new ContextMenu::Entry(szText, icoIcon, pMenuHandler, pSubmenuHandler);
1455 AddElement(pChild: pNew); pNew->SetToolTip(szToolTip);
1456 }
1457
1458 Entry *GetIndexedEntry(int32_t iIndex) { return static_cast<Entry *>(GetElementByIndex(i: iIndex)); }
1459
1460 int32_t GetMenuIndex() { return iMenuIndex; }
1461 static int32_t GetLastMenuIndex() { return iGlobalMenuIndex; }
1462
1463 friend class Screen; friend class Entry;
1464};
1465
1466// combo box filler
1467// should be ComboBox::FillCB; but nested class will cause internal compiler faults
1468class ComboBox_FillCB
1469{
1470private:
1471 ComboBox *pCombo;
1472 ContextMenu *pDrop;
1473
1474public:
1475 virtual ~ComboBox_FillCB() {}
1476
1477 void FillDropDown(ComboBox *pComboBox, ContextMenu *pDropdownList)
1478 {
1479 pCombo = pComboBox; pDrop = pDropdownList; FillDropDownCB();
1480 }
1481
1482 virtual void FillDropDownCB() = 0;
1483 virtual bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) = 0;
1484
1485 // to be used in fill-callback only (crash otherwise!)
1486 void AddEntry(const char *szText, int32_t id, const char *desc = nullptr);
1487 bool FindEntry(const char *szText);
1488 void ClearEntries();
1489};
1490
1491template <class CB> class ComboBox_FillCallback : public ComboBox_FillCB
1492{
1493public:
1494 typedef void (CB::*ComboFillFunc)(ComboBox_FillCB *pFiller);
1495 typedef bool (CB::*ComboSelFunc)(ComboBox *pForCombo, int32_t idNewSelection);
1496
1497private:
1498 CB *pCBClass;
1499 ComboFillFunc FillFunc;
1500 ComboSelFunc SelFunc;
1501
1502protected:
1503 virtual void FillDropDownCB() override
1504 {
1505 if (pCBClass && FillFunc)(pCBClass->*FillFunc)(this);
1506 }
1507
1508 virtual bool OnComboSelChange(ComboBox *pForCombo, int32_t idNewSelection) override
1509 {
1510 if (pCBClass && SelFunc) return (pCBClass->*SelFunc)(pForCombo, idNewSelection); else return false;
1511 }
1512
1513public:
1514 ComboBox_FillCallback(CB *pCBClass, ComboFillFunc FillFunc, ComboSelFunc SelFunc) :
1515 pCBClass(pCBClass), FillFunc(FillFunc), SelFunc(SelFunc) {}
1516};
1517
1518// information on how to draw dialog borders and face
1519class FrameDecoration
1520{
1521private:
1522 int iRefCount;
1523
1524 bool SetFacetByAction(C4Def *pOfDef, class C4FacetEx &rfctTarget, const char *szFacetName);
1525
1526public:
1527 C4ID idSourceDef;
1528 uint32_t dwBackClr; // background face color
1529 C4FacetEx fctTop, fctTopRight, fctRight, fctBottomRight, fctBottom, fctBottomLeft, fctLeft, fctTopLeft;
1530 int iBorderTop, iBorderLeft, iBorderRight, iBorderBottom;
1531 bool fHasGfxOutsideClientArea;
1532
1533 FrameDecoration() : iRefCount(0) { Clear(); }
1534 void Clear(); // zero data
1535
1536 // create from ActMap and graphics of a definition (does some script callbacks to get parameters)
1537 bool SetByDef(C4ID idSourceDef);
1538 bool UpdateGfx(); // update Surface, e.g. after def reload
1539
1540 void Ref() { ++iRefCount; }
1541 void Deref() { if (!--iRefCount) delete this; }
1542
1543 void Draw(C4FacetEx &cgo, C4Rect &rcDrawArea); // draw deco for given rect (rect includes border)
1544};
1545
1546// a button closing the Dlg
1547class CloseButton : public Button
1548{
1549protected:
1550 bool fCloseResult;
1551
1552 virtual void OnPress() override;
1553
1554public:
1555 CloseButton(const char *szBtnText, const C4Rect &rtBounds, bool fResult)
1556 : Button(szBtnText, rtBounds), fCloseResult(fResult) {}
1557};
1558
1559class CloseIconButton : public IconButton
1560{
1561protected:
1562 bool fCloseResult;
1563
1564 virtual void OnPress() override;
1565
1566public:
1567 CloseIconButton(const C4Rect &rtBounds, Icons eIcon, bool fResult)
1568 : IconButton(eIcon, rtBounds, 0), fCloseResult(fResult) {}
1569};
1570
1571inline Button *newOKIconButton(const C4Rect &bounds, Icons icon) { return new CloseIconButton{bounds, icon, true}; }
1572inline Button *newCancelIconButton(const C4Rect &bounds, Icons icon) { return new CloseIconButton{bounds, icon, false}; }
1573
1574inline Button *newOKButton(const C4Rect &bounds) { return new CloseButton{LoadResStr(id: C4ResStrTableKey::IDS_DLG_OK), bounds, true}; }
1575inline Button *newCancelButton(const C4Rect &bounds) { return new CloseButton{LoadResStr(id: C4ResStrTableKey::IDS_DLG_CANCEL), bounds, false}; }
1576inline Button *newYesButton(const C4Rect &bounds) { return new CloseButton{LoadResStr(id: C4ResStrTableKey::IDS_DLG_YES), bounds, true}; }
1577inline Button *newNoButton(const C4Rect &bounds) { return new CloseButton{LoadResStr(id: C4ResStrTableKey::IDS_DLG_NO), bounds, false}; }
1578
1579class BaseInputCallback
1580{
1581public:
1582 virtual void OnOK(const StdStrBuf &sText) = 0;
1583 virtual ~BaseInputCallback() {}
1584};
1585
1586template <class T> class InputCallback : public BaseInputCallback
1587{
1588public:
1589 typedef void (T::*CBFunc)(const StdStrBuf &);
1590
1591private:
1592 T *pTarget;
1593 CBFunc pCBFunc;
1594
1595public:
1596 InputCallback(T *pTarget, CBFunc pCBFunc) : pTarget(pTarget), pCBFunc(pCBFunc) {}
1597
1598 virtual void OnOK(const StdStrBuf &sText) override { (pTarget->*pCBFunc)(sText); }
1599};
1600
1601// a keyboard event that's only executed if the dialog is activated
1602template <class TargetClass, template <typename Class, typename...> typename BaseTemplate = C4KeyCB, typename... BasePars>
1603class DlgKeyCB : public BaseTemplate<TargetClass, BasePars...>
1604{
1605private:
1606 using Base = BaseTemplate<TargetClass, BasePars...>;
1607 using CallbackFunc = Base::CallbackFunc;
1608
1609public:
1610 DlgKeyCB(TargetClass &rTarget, const BasePars &... pars, CallbackFunc pFuncDown, CallbackFunc pFuncUp = nullptr, CallbackFunc pFuncPressed = nullptr)
1611 : Base(rTarget, pars..., pFuncDown, pFuncUp, pFuncPressed) {}
1612
1613 virtual bool CheckCondition() override { return Base::rTarget.IsInActiveDlg(true) && Base::rTarget.IsVisible(); }
1614};
1615
1616template <class TargetClass>
1617using DlgKeyCBPassKey = DlgKeyCB<TargetClass, C4KeyCBPassKey>;
1618
1619template <class TargetClass, class ParameterType>
1620using DlgKeyCBEx = DlgKeyCB<TargetClass, C4KeyCBEx, ParameterType>;
1621
1622// a keyboard event that's only executed if the control has focus
1623template <class TargetClass, template <typename Class, typename...> typename BaseTemplate = C4KeyCB, typename... BasePars>
1624class ControlKeyCB : public BaseTemplate<TargetClass, BasePars...>
1625{
1626private:
1627 using Base = BaseTemplate<TargetClass, BasePars...>;
1628 using CallbackFunc = Base::CallbackFunc;
1629
1630public:
1631 ControlKeyCB(TargetClass &rTarget, const BasePars &... pars, CallbackFunc pFuncDown, CallbackFunc pFuncUp = nullptr, CallbackFunc pFuncPressed = nullptr)
1632 : Base(rTarget, pars..., pFuncDown, pFuncUp, pFuncPressed) {}
1633
1634 virtual bool CheckCondition() override { return Base::rTarget.HasDrawFocus(); }
1635};
1636
1637template <class TargetClass, class ParameterType>
1638using ControlKeyCBExPassKey = ControlKeyCB<TargetClass, C4KeyCBExPassKey, ParameterType>;
1639
1640// key event that checks whether a control is active, but forwards to the dialog
1641template <class TargetClass> class ControlKeyDlgCB : public C4KeyCB<TargetClass>
1642{
1643private:
1644 class Control *pCtrl;
1645 typedef C4KeyCB<TargetClass> Base;
1646 typedef bool(TargetClass::*CallbackFunc)();
1647
1648public:
1649 ControlKeyDlgCB(Control *pCtrl, TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp = nullptr, CallbackFunc pFuncPressed = nullptr)
1650 : Base(rTarget, pFuncDown, pFuncUp, pFuncPressed), pCtrl(pCtrl) {}
1651
1652 virtual bool CheckCondition() override { return pCtrl && pCtrl->IsInActiveDlg(fForKeyboard: true) && pCtrl->IsVisible(); }
1653};
1654
1655// mouse cursor control
1656class CMouse
1657{
1658public:
1659 int32_t x, y; // cursor position
1660 bool LDown, MDown, RDown; // mouse button states
1661 uint32_t dwKeys; // shift, ctrl, etc.
1662 bool fActive;
1663 time_t tLastMovementTime; // timeGetTime() when the mouse pos changed last
1664
1665 // whether last input was done by mouse
1666 // set to true whenever mouse pos changes or buttons are pressed
1667 // reset by keyboard actions to avoid tooltips where the user isn't even doing anything
1668 bool fActiveInput;
1669
1670public:
1671 Element *pMouseOverElement, *pPrevMouseOverElement; // elements at mouse position (after and before processing of mouse event)
1672 Element *pDragElement; // element at pos where left mouse button was clicked last
1673
1674public:
1675 CMouse(int32_t iX, int32_t iY);
1676 ~CMouse();
1677
1678 void Draw(C4FacetEx &cgo, bool fDrawToolTip); // draw cursor
1679
1680 void Input(int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam); // process mouse input
1681 bool IsLDown() { return LDown; }
1682
1683 void GetLastXY(int32_t &rX, int32_t &rY, uint32_t &rdwKeys) { rX = x; rY = y; rdwKeys = dwKeys; }
1684
1685 void ResetElements() // reset MouseOver/etc.-controls
1686 {
1687 pMouseOverElement = pPrevMouseOverElement = pDragElement = nullptr;
1688 }
1689
1690 void ReleaseElements(); // reset MouseOver/etc.-controls, doing the appropriate callbacks
1691 void OnElementGetsInvisible(Element *pChild); // clear ptr
1692
1693 void RemoveElement(Element *pChild); // clear ptr
1694
1695 void SetOwnedMouse(bool fToVal) { fActive = fToVal; }
1696
1697 void ResetToolTipTime() { tLastMovementTime = timeGetTime(); }
1698 bool IsMouseStill() { return timeGetTime() - tLastMovementTime >= C4GUI_ToolTipShowTime; }
1699 void ResetActiveInput() { fActiveInput = false; }
1700 bool IsActiveInput() { return fActiveInput; }
1701
1702 void ReleaseButtons() { LDown = RDown = MDown = false; }
1703};
1704
1705// the dialog client area
1706class Screen : public Window
1707{
1708public:
1709 CMouse Mouse;
1710
1711protected:
1712 Dialog *pActiveDlg; // current, active dialog
1713 ContextMenu *pContext; // currently opened context menu (lowest submenu)
1714 bool fExclusive; // default true. if false, input is shared with the game
1715 C4Rect PreferredDlgRect; // rectangle in which dialogs should be placed
1716 C4GamePadOpener *pGamePadOpener;
1717
1718 static Screen *pScreen; // static singleton var
1719
1720public:
1721 virtual void RemoveElement(Element *pChild) override; // clear ptr
1722 const C4Rect &GetPreferredDlgRect() { return PreferredDlgRect; }
1723
1724protected:
1725 virtual void Draw(C4FacetEx &cgo, bool fDoBG); // draw screen contents
1726
1727 virtual void ElementPosChanged(Element *pOfElement) override; // called when a dialog is moved
1728
1729public:
1730 void ShowDialog(Dialog *pDlg, bool fFade); // present dialog to user
1731 void CloseDialog(Dialog *pDlg, bool fFade); // hide dialog from user
1732 void ActivateDialog(Dialog *pDlg); // set dlg as active
1733 void RecheckActiveDialog();
1734
1735 Dialog *GetTopDialog(); // get topmost dlg
1736
1737public:
1738 Screen(int32_t tx, int32_t ty, int32_t twdt, int32_t thgt);
1739 ~Screen();
1740
1741 void Render(bool fDoBG); // render to lpDDraw
1742 void RenderMouse(C4FacetEx &cgo); // draw mouse only
1743
1744 virtual Screen *GetScreen() override { return this; } // return contained screen
1745 static Screen *GetScreenS() { return pScreen; } // get global screen
1746
1747 bool IsActive() { return !!GetTopDialog(); } // return whether GUI is active
1748
1749 bool KeyAny(); // to be called on keystrokes; resets some tooltip-times
1750 virtual bool CharIn(const char *c); // input: character key pressed - should return false for none-character-inputs
1751 bool MouseInput(int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam, Dialog *pDlg, C4Viewport *pVP); // input: mouse movement or buttons; sends MouseEnter/Leave; return whether inside dialog
1752
1753 bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, bool *pbConfigDontShowAgainSetting = nullptr); // show message
1754 bool ShowErrorMessage(const char *szMessage); // show message: Error caption and icon
1755 bool ShowMessageModal(const char *szMessage, const char *szCaption, uint32_t dwButtons, Icons icoIcon, bool *pbConfigDontShowAgainSetting = nullptr); // show modal message dlg
1756 bool ShowModalDlg(Dialog *pDlg, bool fDestruct = true); // show any dialog modal and destruct it afterwards
1757 bool ShowRemoveDlg(Dialog *pDlg); // show dialog, and flag it to delete itself when closed; return immediately
1758
1759 void CloseAllDialogs(bool fWithOK); // close all dialogs on the screen; top dlgs first
1760 void SetPreferredDlgRect(const C4Rect &rtNewPref) { PreferredDlgRect = rtNewPref; }
1761 void DoContext(ContextMenu *pNewCtx, Element *pAtElement, int32_t iX, int32_t iY); // open context menu (closes any other contextmenu)
1762 void AbortContext(bool fByUser) { if (pContext) pContext->Abort(fByUser); } // close context menu
1763 int32_t GetContextMenuIndex() { return pContext ? pContext->GetMenuIndex() : 0; } // get current context-menu (lowest level)
1764 int32_t GetLastContextMenuIndex() { return ContextMenu::GetLastMenuIndex(); } // get last opened context-menu (lowest level)
1765 bool HasContext() { return !!pContext; }
1766
1767 bool HasFullscreenDialog(bool fIncludeFading); // true if on fullscreen dialog is visible
1768 Dialog *GetFullscreenDialog(bool fIncludeFading); // return dlg pointer to first found fullscreen dialog
1769
1770 // exclusive
1771 void SetExclusive(bool fToState) { fExclusive = fToState; Mouse.SetOwnedMouse(fExclusive); UpdateMouseFocus(); }
1772 bool IsExclusive() { return fExclusive; }
1773
1774 bool HasKeyboardFocus();
1775
1776 bool HasMouseFocus(); // return whether mouse controls GUI only
1777
1778 int32_t GetMouseControlledDialogCount(); // count dlgs with that flag set
1779 void UpdateMouseFocus(); // when exclusive mode has changed: Make sure mouse clip is correct
1780
1781 // draw a tooltip in a box
1782 static void DrawToolTip(const char *szTip, C4FacetEx &cgo, int32_t x, int32_t y);
1783
1784 // gamepad
1785 void UpdateGamepadGUIControlEnabled(); // callback when gamepad gui option changed
1786
1787 friend class Dialog; friend class ContextMenu;
1788};
1789
1790// menu handler bound to member functions of another class
1791template <class CBClass> class CBMenuHandler : public MenuHandler
1792{
1793private:
1794 CBClass *pCBTarget; // callback target
1795 typename DlgCallback<CBClass>::ContextClickFunc pCallbackFn; // callback function
1796
1797public:
1798 CBMenuHandler(CBClass *pCBTarget, typename DlgCallback<CBClass>::ContextClickFunc pCallbackFn, int32_t iaExtra = 0)
1799 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) {}
1800
1801 virtual void OnOK(Element *pTargetElement) override
1802 {
1803 if (!pCBTarget || !pCallbackFn) return;
1804 // do callback
1805 (pCBTarget->*pCallbackFn)(pTargetElement);
1806 }
1807};
1808
1809// menu handler bound to member functions of another class, providing extra storage for parameters
1810template <class CBClass, class TEx> class CBMenuHandlerEx : public MenuHandler
1811{
1812private:
1813 CBClass *pCBTarget; // callback target
1814 typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn; // callback function
1815 TEx Extra; // extra data
1816
1817public:
1818 CBMenuHandlerEx(CBClass *pCBTarget, typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn, TEx aExtra)
1819 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra(aExtra) {}
1820 CBMenuHandlerEx(CBClass *pCBTarget, typename DlgCallbackEx<CBClass, const TEx &>::ContextClickFunc pCallbackFn)
1821 : MenuHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn), Extra() {}
1822
1823 void SetExtra(const TEx &e) { Extra = e; }
1824
1825 virtual void OnOK(Element *pTargetElement) override
1826 {
1827 if (!pCBTarget || !pCallbackFn) return;
1828 // do callback
1829 (pCBTarget->*pCallbackFn)(pTargetElement, Extra);
1830 }
1831};
1832
1833// context handler bound to member functions of another class
1834template <class CBClass> class CBContextHandler : public ContextHandler
1835{
1836private:
1837 CBClass *pCBTarget; // callback target
1838 typename DlgCallback<CBClass>::ContextFunc pCallbackFn; // callback function
1839
1840public:
1841 CBContextHandler(CBClass *pCBTarget, typename DlgCallback<CBClass>::ContextFunc pCallbackFn)
1842 : ContextHandler(), pCBTarget(pCBTarget), pCallbackFn(pCallbackFn) {}
1843
1844 virtual bool OnContext(Element *pOnElement, int32_t iX, int32_t iY) override
1845 {
1846 if (!pCBTarget || !pCallbackFn) return false;
1847 Screen *pScreen = pOnElement->GetScreen();
1848 if (!pScreen) return false;
1849 // let CB func create context menu
1850 ContextMenu *pNewMenu = (pCBTarget->*pCallbackFn)(pOnElement, iX, iY);
1851 if (!pNewMenu) return false;
1852 // open it on screen
1853 pScreen->DoContext(pNewCtx: pNewMenu, pAtElement: pOnElement, iX, iY);
1854 // done, success
1855 return true;
1856 }
1857
1858 virtual C4GUI::ContextMenu *OnSubcontext(Element *pOnElement) override
1859 {
1860 // query directly
1861 return (pCBTarget->*pCallbackFn)(pOnElement, 0, 0);
1862 }
1863
1864 friend class Dialog;
1865};
1866
1867// a helper used to align elements
1868class ComponentAligner
1869{
1870protected:
1871 C4Rect rcClientArea; // free client area
1872 int32_t iMarginX, iMarginY; // horizontal and vertical margins of components
1873
1874 static C4Rect rcTemp; // temp rect
1875
1876public:
1877 ComponentAligner(const C4Rect &rcArea, int32_t iMarginX, int32_t iMarginY, bool fZeroAreaXY = false)
1878 : rcClientArea(rcArea), iMarginX(iMarginX), iMarginY(iMarginY)
1879 {
1880 if (fZeroAreaXY) rcClientArea.x = rcClientArea.y = 0;
1881 }
1882
1883 bool GetFromTop (int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the top
1884 bool GetFromLeft (int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the left
1885 bool GetFromRight (int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get a portion from the right
1886 bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut); // get a portion from the bottom
1887 void GetAll(C4Rect &rcOut); // get remaining client area
1888 bool GetCentered(int32_t iWdt, int32_t iHgt, C4Rect &rcOut); // get centered subregion within area
1889
1890 C4Rect &GetFromTop (int32_t iHgt, int32_t iWdt = -1) { GetFromTop (iHgt, iWdt, rcOut&: rcTemp); return rcTemp; } // get a portion from the top
1891 C4Rect &GetFromLeft (int32_t iWdt, int32_t iHgt = -1) { GetFromLeft (iWdt, iHgt, rcOut&: rcTemp); return rcTemp; } // get a portion from the left
1892 C4Rect &GetFromRight (int32_t iWdt, int32_t iHgt = -1) { GetFromRight (iWdt, iHgt, rcOut&: rcTemp); return rcTemp; } // get a portion from the right
1893 C4Rect &GetFromBottom(int32_t iHgt, int32_t iWdt = -1) { GetFromBottom(iHgt, iWdt, rcOut&: rcTemp); return rcTemp; } // get a portion from the bottom
1894 C4Rect &GetAll() { GetAll(rcOut&: rcTemp); return rcTemp; } // get remaining client are
1895 C4Rect &GetCentered(int32_t iWdt, int32_t iHgt) { GetCentered(iWdt, iHgt, rcOut&: rcTemp); return rcTemp; } // get centered subregion within area (w/o size checks)
1896
1897 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);
1898
1899 int32_t GetWidth() const { return rcClientArea.Wdt; }
1900 int32_t GetHeight() const { return rcClientArea.Hgt; }
1901 int32_t GetHMargin() const { return iMarginX; }
1902 int32_t GetVMargin() const { return iMarginY; }
1903
1904 int32_t GetInnerWidth() const { return rcClientArea.Wdt - 2 * iMarginX; }
1905 int32_t GetInnerHeight() const { return rcClientArea.Hgt - 2 * iMarginY; }
1906
1907 void ExpandTop (int32_t iByHgt) { rcClientArea.y -= iByHgt; rcClientArea.Hgt += iByHgt; } // enlarge client rect
1908 void ExpandLeft (int32_t iByWdt) { rcClientArea.x -= iByWdt; rcClientArea.Wdt += iByWdt; } // enlarge client rect
1909 void ExpandRight (int32_t iByWdt) { rcClientArea.Wdt += iByWdt; } // enlarge client rect
1910 void ExpandBottom(int32_t iByHgt) { rcClientArea.Hgt += iByHgt; } // enlarge client rect
1911};
1912
1913// shortcut for check whether GUI is active
1914inline bool IsGUIValid() { return !!Screen::GetScreenS(); }
1915inline bool IsActive() { return Screen::GetScreenS() && Screen::GetScreenS()->IsActive(); }
1916inline bool IsExclusive() { return Screen::GetScreenS() && Screen::GetScreenS()->IsExclusive(); }
1917
1918// shortcut for GUI screen size
1919int32_t GetScreenWdt();
1920int32_t GetScreenHgt();
1921
1922// sound effect in GUI: Only if enabled
1923void GUISound(const char *szSound);
1924
1925} // end of namespace
1926