1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2020, 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// that which can be pressed
20
21#include "C4GuiResource.h"
22#include <C4Include.h>
23#include <C4Gui.h>
24#include <C4FullScreen.h>
25#include <C4LoaderScreen.h>
26#include <C4Application.h>
27
28namespace C4GUI
29{
30
31// Button
32
33Button::Button(const char *szBtnText, const C4Rect &rtBounds)
34 : Control(rtBounds), pCustomGfx(nullptr), pCustomGfxDown(nullptr), fDown(false), fMouseOver(false), fEnabled(true)
35{
36 // key callbacks
37 C4CustomKey::CodeList keys;
38 keys.push_back(x: C4KeyCodeEx(K_SPACE));
39 keys.push_back(x: C4KeyCodeEx(K_RETURN));
40 if (Config.Controls.GamepadGuiControl)
41 keys.push_back(x: C4KeyCodeEx(KEY_Gamepad(idGamepad: 0, idButton: KEY_JOY_AnyLowButton)));
42 pKeyButton = new C4KeyBinding(keys, "GUIButtonPress", KEYSCOPE_Gui,
43 new ControlKeyCB<Button>(*this, &Button::KeyButtonDown, &Button::KeyButtonUp), C4CustomKey::PRIO_Ctrl);
44 sText = "";
45 // set new button text
46 SetText(szBtnText);
47}
48
49Button::~Button()
50{
51 delete pKeyButton;
52}
53
54void Button::SetText(const char *szToText)
55{
56 // set new button text
57 if (szToText)
58 {
59 sText.Copy(pnData: szToText);
60 // expand hotkey markup
61 ExpandHotkeyMarkup(sText, rcHotkey&: cHotkey);
62 }
63 else
64 {
65 sText = "";
66 cHotkey = 0;
67 }
68}
69
70bool Button::OnHotkey(char cHotkey)
71{
72 // if hotkey matches, press the button
73 if (this->cHotkey == cHotkey && fEnabled)
74 {
75 OnPress();
76 return true;
77 }
78 else return false;
79}
80
81void Button::DrawElement(C4FacetEx &cgo)
82{
83 // draw base
84 if (fDown)
85 // pressed
86 DrawBar(cgo, rFacets&: pCustomGfxDown ? *pCustomGfxDown : GetRes()->barButtonD);
87 else
88 // released
89 DrawBar(cgo, rFacets&: pCustomGfx ? *pCustomGfx : GetRes()->barButton);
90 // get text pos
91 int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y, x1 = cgo.TargetX + rcBounds.x + rcBounds.Wdt - 1, y1 = cgo.TargetY + rcBounds.y + rcBounds.Hgt - 1;
92 int32_t iTxtOff = fDown ? 1 : 0;
93 // draw selection highlight
94 if (fEnabled) if (HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false)))
95 {
96 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
97 GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0 + 5, iY: y0 + 3, iWdt: rcBounds.Wdt - 10, iHgt: rcBounds.Hgt - 6);
98 lpDDraw->ResetBlitMode();
99 }
100 // draw text
101 int32_t iTextHgt = rcBounds.Hgt - 2;
102 CStdFont &rUseFont =
103 (GetRes()->TitleFont.GetLineHeight() > iTextHgt ?
104 (GetRes()->CaptionFont.GetLineHeight() > iTextHgt ?
105 GetRes()->TextFont :
106 GetRes()->CaptionFont) :
107 GetRes()->TitleFont);
108 iTextHgt = rUseFont.GetLineHeight();
109 lpDDraw->TextOut(szText: sText.getData(), rFont&: rUseFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: (x0 + x1) / 2 + iTxtOff, iTy: (y0 + y1 - iTextHgt) / 2 + iTxtOff, C4GUI_ButtonFontClr, byForm: ACenter, fDoMarkup: true);
110}
111
112bool Button::KeyButtonDown()
113{
114 // not on disabled
115 if (!fEnabled) return false;
116 // space downs button
117 SetDown();
118 return true;
119}
120
121bool Button::KeyButtonUp()
122{
123 // space press activates button
124 if (!fDown) return false;
125 SetUp(true);
126 if (fEnabled) OnPress();
127 return true;
128}
129
130void Button::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam)
131{
132 // inherited
133 Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
134 // process left down and up
135 if (fEnabled) switch (iButton)
136 {
137 case C4MC_Button_LeftDown:
138 // mark button as being down
139 SetDown();
140 // remember drag target
141 // no dragging movement will be done w/o drag component assigned
142 // but it should be remembered if the user leaves the button with the mouse down
143 // and then re-enters w/o having released the button
144 if (!rMouse.pDragElement) rMouse.pDragElement = this;
145 break;
146
147 case C4MC_Button_LeftUp:
148 // only if button was down... (might have dragged here)
149 if (fDown)
150 {
151 // it's now up :)
152 SetUp(true);
153 // process event
154 OnPress();
155 }
156 break;
157 };
158}
159
160void Button::MouseEnter(CMouse &rMouse)
161{
162 Control::MouseEnter(rMouse);
163 // remember mouse state for button highlight
164 fMouseOver = true;
165 // mouse re-enters with left button down?
166 if (rMouse.pDragElement == this) if (fEnabled) SetDown();
167}
168
169void Button::MouseLeave(CMouse &rMouse)
170{
171 Control::MouseLeave(rMouse);
172 // mouse left
173 fMouseOver = false;
174 // reset down-state if mouse leves with button down
175 if (rMouse.pDragElement == this) if (fEnabled) SetUp(false);
176}
177
178void Button::OnPress()
179{
180 // nothing in base
181}
182
183void Button::SetDown()
184{
185 // already down?
186 if (fDown) return;
187 // play sound
188 GUISound(szSound: "ArrowHit");
189 // set down
190 fDown = true;
191}
192
193void Button::SetUp(bool fPress)
194{
195 // already up?
196 if (!fDown) return;
197 // play sound
198 GUISound(szSound: fPress ? "Click" : "ArrowHit");
199 // set up
200 fDown = false;
201}
202
203// IconButton
204
205void IconButton::DrawElement(C4FacetEx &cgo)
206{
207 // get drawing bounds
208 int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y;
209 // draw selection highlight
210 if (fEnabled) if (fHighlight || HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false)))
211 {
212 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
213 GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Wdt, iHgt: rcBounds.Hgt);
214 lpDDraw->ResetBlitMode();
215 }
216 // draw the icon
217 if (fHasClr && fctIcon.Surface) fctIcon.Surface->SetClr(dwClr);
218 fctIcon.DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Wdt, iHgt: rcBounds.Hgt);
219 // "button" down?
220 if (fEnabled) if (fDown || fHighlight)
221 {
222 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
223 GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Wdt, iHgt: rcBounds.Hgt);
224 lpDDraw->ResetBlitMode();
225 }
226 // some icon buttons have captions. draw caption below button
227 if (sText.getLength())
228 {
229 CStdFont &rUseFont = GetRes()->TextFont;
230 lpDDraw->TextOut(szText: sText.getData(), rFont&: rUseFont, fZoom: 1.0f, sfcDest: cgo.Surface, iTx: x0 + rcBounds.Wdt / 2, iTy: y0 + rcBounds.Hgt - rUseFont.GetLineHeight() * 4 / 5, C4GUI_CaptionFontClr, byForm: ACenter);
231 }
232}
233
234IconButton::IconButton(Icons eUseIcon, const C4Rect &rtBounds, char caHotkey)
235 : Button("", rtBounds), fHighlight(false), fHasClr(false), dwClr(0u)
236{
237 cHotkey = caHotkey;
238 SetIcon(eUseIcon);
239}
240
241void IconButton::SetIcon(Icons eUseIcon)
242{
243 if (eUseIcon >= 0) fctIcon = Icon::GetIconFacet(icoIconIndex: eUseIcon); else fctIcon.Surface = nullptr;
244}
245
246// ArrowButton
247
248ArrowButton::ArrowButton(ArrowFct eDir, const C4Rect &rtBounds, char cHotkey)
249 : Button("", rtBounds), eDir(eDir)
250{
251 this->cHotkey = cHotkey;
252}
253
254void ArrowButton::DrawElement(C4FacetEx &cgo)
255{
256 // get drawing bounds
257 int32_t x0 = cgo.TargetX + rcBounds.x, y0 = cgo.TargetY + rcBounds.y;
258 // draw selection highlight
259 if (fEnabled) if (HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false)))
260 {
261 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
262 GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Wdt, iHgt: rcBounds.Hgt);
263 lpDDraw->ResetBlitMode();
264 }
265 // draw the arrow - down if pressed
266 int32_t iFctIdx = eDir;
267 if (fDown) iFctIdx += Down;
268 GetRes()->fctBigArrows.GetPhase(iPhaseX: iFctIdx).DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Wdt, iHgt: rcBounds.Hgt);
269}
270
271int32_t ArrowButton::GetDefaultWidth()
272{
273 // default by gfx size
274 return GetRes()->fctBigArrows.Wdt;
275}
276
277int32_t ArrowButton::GetDefaultHeight()
278{
279 // default by gfx size
280 return GetRes()->fctBigArrows.Hgt;
281}
282
283// FacetButton
284
285FacetButton::FacetButton(const C4Facet &rBaseFct, const C4Facet &rHighlightFct, const FLOAT_RECT &rtfBounds, char cHotkey)
286 : Button("", C4Rect(rtfBounds)), fctBase(rBaseFct), fctHighlight(rHighlightFct), rcfDrawBounds(rtfBounds), dwTextClrInact(0x7f000000), dwTextClrAct(0xff000000), pFont(nullptr), fFontZoom(1.0f)
287{
288 this->cHotkey = cHotkey;
289 iTxtOffX = iTxtOffY = 0;
290 byTxtAlign = ALeft;
291}
292
293void FacetButton::DrawElement(C4FacetEx &cgo)
294{
295 // get drawing bounds
296 float x0 = rcfDrawBounds.left + cgo.TargetX, y0 = rcfDrawBounds.top + cgo.TargetY;
297 // draw button or highlight facet
298 uint32_t dwTextClr;
299 if ((HasDrawFocus() || (fMouseOver && IsInActiveDlg(fForKeyboard: false))) && fEnabled)
300 {
301 fctHighlight.DrawXFloat(sfcTarget: cgo.Surface, fX: x0, fY: y0, fWdt: rcfDrawBounds.right - rcfDrawBounds.left, fHgt: rcfDrawBounds.bottom - rcfDrawBounds.top);
302 dwTextClr = dwTextClrAct;
303 }
304 else
305 {
306 fctBase.DrawXFloat(sfcTarget: cgo.Surface, fX: x0, fY: y0, fWdt: rcfDrawBounds.right - rcfDrawBounds.left, fHgt: rcfDrawBounds.bottom - rcfDrawBounds.top);
307 dwTextClr = dwTextClrInact;
308 }
309 // draw caption text
310 if (sText.getLength() > 0)
311 {
312 CStdFont *pUseFont = pFont ? pFont : &(GetRes()->GetFontByHeight(iHgt: rcBounds.Hgt, pfZoom: &fFontZoom));
313 lpDDraw->TextOut(szText: sText.getData(), rFont&: *pUseFont, fZoom: fFontZoom, sfcDest: cgo.Surface, iTx: static_cast<int>(x0 + iTxtOffX), iTy: static_cast<int>(y0 + iTxtOffY), dwFCol: dwTextClr, byForm: byTxtAlign, fDoMarkup: true);
314 }
315}
316} // end of namespace
317