1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2005, 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// checkbox
20
21#include "C4GuiResource.h"
22#include <C4Include.h>
23#include <C4Gui.h>
24#include <C4FacetEx.h>
25#include <C4Wrappers.h>
26#include <C4MouseControl.h>
27
28#include <StdWindow.h>
29
30namespace C4GUI
31{
32
33// CheckBox
34
35CheckBox::CheckBox(const C4Rect &rtBounds, const std::string &caption, bool fChecked)
36 : Control(rtBounds), fChecked(fChecked), fMouseOn(false), fEnabled(true), pFont(nullptr)
37 , dwEnabledClr(C4GUI_CheckboxFontClr), dwDisabledClr(C4GUI_CheckboxDisabledFontClr), cHotkey(0)
38{
39 if (!caption.empty())
40 {
41 SetCaption(caption);
42 }
43 // key callbacks: Check/Uncheck on space and primary joy button
44 C4CustomKey::CodeList Keys;
45 Keys.push_back(x: C4KeyCodeEx(K_SPACE));
46 if (Config.Controls.GamepadGuiControl)
47 {
48 Keys.push_back(x: C4KeyCodeEx(KEY_Gamepad(idGamepad: 0, idButton: KEY_JOY_AnyLowButton)));
49 }
50 pKeyCheck = new C4KeyBinding(Keys, "GUICheckboxToggle", KEYSCOPE_Gui,
51 new ControlKeyCB<CheckBox>(*this, &CheckBox::KeyCheck), C4CustomKey::PRIO_Ctrl);
52 pCBHandler = nullptr;
53}
54
55CheckBox::~CheckBox()
56{
57 delete pKeyCheck;
58 if (pCBHandler) pCBHandler->DeRef();
59}
60
61void CheckBox::UpdateOwnPos() {}
62
63bool CheckBox::OnHotkey(char cHotkey)
64{
65 if (cHotkey != this->cHotkey) return false;
66 ToggleCheck(fByUser: true);
67 return true;
68}
69
70void CheckBox::ToggleCheck(bool fByUser)
71{
72 // user can't toggle if disabled
73 if (fByUser && !fEnabled) return;
74 // sound
75 if (fByUser) GUISound(szSound: "ArrowHit");
76 // toggle state
77 fChecked = !fChecked;
78 // callback (last call; may destroy element)
79 if (pCBHandler) pCBHandler->DoCall(pElement: this);
80}
81
82void CheckBox::MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, uint32_t dwKeyParam)
83{
84 if (fEnabled)
85 {
86 // set mouse-on flag depending on whether mouse is over box area
87 fMouseOn = Inside<int32_t>(ival: iX, lbound: 0, rbound: rcBounds.Hgt) && Inside<int32_t>(ival: iY, lbound: 0, rbound: rcBounds.Hgt);
88 // left-click within checkbox toggles it
89 if (iButton == C4MC_Button_LeftUp && fMouseOn)
90 {
91 ToggleCheck(fByUser: true);
92 return;
93 }
94 }
95 // not recognized; base call
96 Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
97}
98
99void CheckBox::MouseEnter(CMouse &rMouse)
100{
101 Control::MouseEnter(rMouse);
102}
103
104void CheckBox::MouseLeave(CMouse &rMouse)
105{
106 fMouseOn = false;
107 Control::MouseLeave(rMouse);
108}
109
110void CheckBox::DrawElement(C4FacetEx &cgo)
111{
112 // left side: check facet (squared)
113 int x0 = rcBounds.x + cgo.TargetX;
114 int y0 = rcBounds.y + cgo.TargetY;
115 GetRes()->fctCheckbox.GetPhase(iPhaseX: fChecked + 2 * !fEnabled).DrawX(sfcTarget: cgo.Surface, iX: x0, iY: y0, iWdt: rcBounds.Hgt, iHgt: rcBounds.Hgt);
116 // right of it: checkbox text
117 CStdFont *pUseFont = pFont ? pFont : &(GetRes()->TextFont);
118 int32_t yOff; float fZoom;
119 if (pUseFont->GetLineHeight() <= rcBounds.Hgt)
120 {
121 yOff = std::max<int32_t>(a: rcBounds.Hgt - pUseFont->GetLineHeight(), b: 0) / 2;
122 fZoom = 1.0f;
123 }
124 else
125 {
126 yOff = 0;
127 fZoom = static_cast<float>(rcBounds.Hgt) / (std::max)(a: pUseFont->GetLineHeight(), b: 1);
128 }
129 lpDDraw->TextOut(szText: sCaption.getData(), rFont&: *pUseFont, fZoom, sfcDest: cgo.Surface, iTx: x0 + rcBounds.Hgt + C4GUI_CheckBoxLabelSpacing, iTy: y0 + yOff, dwFCol: fEnabled ? dwEnabledClr : dwDisabledClr, byForm: ALeft, fDoMarkup: true);
130 // selection marker
131 if ((fMouseOn && IsInActiveDlg(fForKeyboard: false)) || HasDrawFocus())
132 {
133 lpDDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
134 GetRes()->fctButtonHighlight.DrawX(sfcTarget: cgo.Surface, iX: x0 + rcBounds.Hgt * 1 / 4, iY: y0 + rcBounds.Hgt * 1 / 4, iWdt: rcBounds.Hgt * 1 / 2, iHgt: rcBounds.Hgt * 1 / 2);
135 lpDDraw->ResetBlitMode();
136 }
137}
138
139void CheckBox::SetOnChecked(BaseCallbackHandler *pCB)
140{
141 if (pCBHandler) pCBHandler->DeRef();
142 if (pCBHandler = pCB) pCB->Ref();
143}
144
145void CheckBox::SetCaption(const std::string &caption)
146{
147 sCaption.Copy(pnData: caption.c_str());
148 ExpandHotkeyMarkup(sText&: sCaption, rcHotkey&: cHotkey);
149}
150
151bool CheckBox::GetStandardCheckBoxSize(int *piWdt, int *piHgt, const char *szForCaptionText, CStdFont *pUseFont)
152{
153 // get needed text size
154 if (!pUseFont) pUseFont = &(GetRes()->TextFont);
155 int32_t iWdt = 100, iHgt = 32;
156 pUseFont->GetTextExtent(szText: szForCaptionText, rsx&: iWdt, rsy&: iHgt, fCheckMarkup: true);
157 // check box height equals text height
158 // add check box plus indent
159 if (piWdt) *piWdt = iWdt + iHgt + C4GUI_CheckBoxLabelSpacing;
160 if (piHgt) *piHgt = iHgt;
161 return true;
162}
163
164} // namespace C4GUI
165