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