FifeGUI 0.3.0
A C++ GUI library designed for games.
checkbox.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5// Corresponding header include
6#include "fifechan/widgets/checkbox.hpp"
7
8// Standard library includes
9#include <algorithm>
10#include <string>
11
12// Project headers (subdirs before local)
13#include "fifechan/exception.hpp"
14#include "fifechan/font.hpp"
15#include "fifechan/graphics.hpp"
16#include "fifechan/image.hpp"
17
18namespace fcn
19{
20
21 namespace
22 {
23 void drawMarkerBrush(Graphics* graphics, int x, int y, int thickness)
24 {
25 int const offset = (thickness - 1) / 2;
26 graphics->fillRectangle(x - offset, y - offset, thickness, thickness);
27 }
28
29 void drawMarkerStroke(Graphics* graphics, Point const & start, Point const & end, int thickness)
30 {
31 int x = start.x;
32 int y = start.y;
33
34 int const deltaX = std::abs(end.x - start.x);
35 int const stepX = (start.x < end.x) ? 1 : -1;
36 int const deltaY = -std::abs(end.y - start.y);
37 int const stepY = (start.y < end.y) ? 1 : -1;
38 int error = deltaX + deltaY;
39
40 while (true) {
41 drawMarkerBrush(graphics, x, y, thickness);
42
43 if ((x == end.x) && (y == end.y)) {
44 break;
45 }
46
47 int const doubledError = 2 * error;
48 if (doubledError >= deltaY) {
49 error += deltaY;
50 x += stepX;
51 }
52 if (doubledError <= deltaX) {
53 error += deltaX;
54 y += stepY;
55 }
56 }
57 }
58
59 int scaleMarkerCoordinate(int origin, int size, int numerator)
60 {
61 return origin + (((size - 1) * numerator) / 16);
62 }
63 } // namespace
64
65 CheckBox::CheckBox()
66 {
68 }
69
70 CheckBox::CheckBox(std::string const & caption, bool selected) : mSelected(selected)
71 {
72 setCaption(caption);
73
75 }
76
77 CheckBox::~CheckBox()
78 {
80 delete mBackgroundImage;
81 }
82 }
83
84 void CheckBox::draw(Graphics* graphics)
85 {
86 if (mMode == MarkerStyle::Rhombus) {
87 drawRhombus(graphics);
88 } else {
89 drawBox(graphics);
90 }
91
92 if (!getCaption().empty()) {
93 graphics->setFont(getFont());
94 graphics->setColor(getForegroundColor());
95
96 int const h = getHeight() - (2 * getBorderSize()) - getPaddingTop() - getPaddingBottom();
97 int const textX = getBorderSize() + getPaddingLeft() + h;
98 int const textY = getBorderSize() + getPaddingTop() + ((h - getFont()->getHeight()) / 2);
99 graphics->drawText(getCaption(), textX, textY);
100 }
101 }
102
104 {
105 bool const active = isFocused();
106
107 // draw background
108 Rectangle const background(
110 if (mBackgroundImage != nullptr) {
111 graphics->drawImage(
112 mBackgroundImage, 0, 0, background.x, background.y, background.width, background.height);
113 } else {
114 Color faceColor = getBaseColor();
115 if (active &&
116 ((getSelectionMode() & Widget::SelectionMode::Background) == Widget::SelectionMode::Background)) {
117 faceColor = getSelectionColor();
118 }
119 graphics->setColor(faceColor);
120 graphics->fillRectangle(background.x, background.y, background.width, background.height);
121 }
122
123 // rec for inner box
124 int const h = getHeight() - (2 * getBorderSize()) - getPaddingTop() - getPaddingBottom();
126 if (mMode == MarkerStyle::Image) {
127 // draw marker image
128 drawMarkerImage(graphics, rec);
129 // draw border
130 if (getBorderSize() > 0) {
131 if (active && (getSelectionMode() & Widget::SelectionMode::Border) == Widget::SelectionMode::Border) {
132 drawSelectionFrame(graphics);
133 } else {
134 drawBorder(graphics);
135 }
136 }
137 } else {
138 // draw inner box background
139 graphics->setColor(getBackgroundColor());
140 graphics->fillRectangle(rec.x, rec.y, h, h);
141 // draw border
142 if (getBorderSize() > 0) {
143 if (active && (getSelectionMode() & Widget::SelectionMode::Border) == Widget::SelectionMode::Border) {
144 drawSelectionFrame(graphics);
145 } else {
146 drawBorder(graphics);
147 }
148 }
149 Color faceColor = getBaseColor();
150 if (active &&
151 ((getSelectionMode() & Widget::SelectionMode::Background) == Widget::SelectionMode::Background)) {
152 faceColor = getSelectionColor();
153 }
154 // border around the inner box background
155 int const alpha = getBaseColor().a;
156 faceColor.a = alpha;
157 Color highlightColor = faceColor + 0x303030;
158 highlightColor.a = alpha;
159 Color shadowColor = faceColor - 0x303030;
160 shadowColor.a = alpha;
161
162 int const recRight = rec.x + rec.width - 1;
163 int const recBottom = rec.y + rec.height - 1;
164
165 graphics->setColor(shadowColor);
166 graphics->drawLine(rec.x, rec.y, recRight, rec.y);
167 graphics->drawLine(rec.x, rec.y, rec.x, recBottom);
168
169 graphics->setColor(highlightColor);
170 graphics->drawLine(recRight, rec.y, recRight, recBottom);
171 graphics->drawLine(rec.x, recBottom, recRight, recBottom);
172
173 // draws marker
174 if (mSelected) {
175 graphics->setColor(getForegroundColor());
176 switch (mMode) {
177 case MarkerStyle::Checkmark:
178 drawCheckmark(graphics, rec);
179 break;
180 case MarkerStyle::Cross:
181 drawCross(graphics, rec);
182 break;
183 case MarkerStyle::Dot:
184 drawDot(graphics, rec);
185 break;
186 default:
187 throwException("Unknown marker.");
188 }
189 }
190 }
191 }
192
193 void CheckBox::drawCheckmark(Graphics* graphics, Rectangle const & rec)
194 {
195 int const thickness = std::max(1, std::min(rec.width, rec.height) / 6);
196
197 Point const start(scaleMarkerCoordinate(rec.x, rec.width, 3), scaleMarkerCoordinate(rec.y, rec.height, 8));
198 Point const joint(scaleMarkerCoordinate(rec.x, rec.width, 7), scaleMarkerCoordinate(rec.y, rec.height, 12));
199 Point const end(scaleMarkerCoordinate(rec.x, rec.width, 13), scaleMarkerCoordinate(rec.y, rec.height, 4));
200
201 drawMarkerStroke(graphics, start, joint, thickness);
202 drawMarkerStroke(graphics, joint, end, thickness);
203 }
204
205 void CheckBox::drawCross(Graphics* graphics, Rectangle const & rec)
206 {
207 int const thickness = std::max(1, std::min(rec.width, rec.height) / 6);
208
209 Point const topLeft(scaleMarkerCoordinate(rec.x, rec.width, 3), scaleMarkerCoordinate(rec.y, rec.height, 3));
210 Point const bottomRight(
211 scaleMarkerCoordinate(rec.x, rec.width, 13), scaleMarkerCoordinate(rec.y, rec.height, 13));
212 Point const bottomLeft(
213 scaleMarkerCoordinate(rec.x, rec.width, 3), scaleMarkerCoordinate(rec.y, rec.height, 13));
214 Point const topRight(scaleMarkerCoordinate(rec.x, rec.width, 13), scaleMarkerCoordinate(rec.y, rec.height, 3));
215
216 drawMarkerStroke(graphics, topLeft, bottomRight, thickness);
217 drawMarkerStroke(graphics, bottomLeft, topRight, thickness);
218 }
219
220 void CheckBox::drawDot(Graphics* graphics, Rectangle const & rec)
221 {
222 Point const p(rec.x + (rec.width / 2), rec.y + (rec.height / 2));
223
224 int const radius = (rec.width - 3) / 2;
225
226 graphics->drawFillCircle(p, radius);
227 }
228
229 void CheckBox::drawMarkerImage(Graphics* graphics, Rectangle const & rec)
230 {
231 Rectangle rect = rec;
232 Image const * img = nullptr;
233 if (isSelected()) {
234 rect.x += getDownXOffset();
235 rect.y += getDownYOffset();
236 if (!isActive()) {
237 if (getInactiveDownImage() != nullptr) {
238 img = getInactiveDownImage();
239 }
240 } else {
241 img = (getDownImage() != nullptr) ? getDownImage() : getUpImage();
242 }
243 } else if (mHasMouse) {
244 if (!isActive()) {
245 if (getInactiveHoverImage() != nullptr) {
246 img = getInactiveHoverImage();
247 }
248 } else {
249 img = (getHoverImage() != nullptr) ? getHoverImage() : getUpImage();
250 }
251 }
252
253 if (img != nullptr) {
254 graphics->drawImage(img, 0, 0, rect.x, rect.y, std::max(rect.width, img->getWidth()), rect.height);
255 }
256 }
257
259 {
260 // ToDo: Rewrite this part, its only c&p from RadioButton
261 graphics->pushClipArea(Rectangle(1, 1, getWidth() - 1, getHeight() - 1));
262 int h = 0;
263
264 if (getHeight() % 2 == 0) {
265 h = getHeight() - 4;
266 } else {
267 h = getHeight() - 3;
268 }
269
270 int const alpha = getBaseColor().a;
271 Color faceColor = getBaseColor();
272 faceColor.a = alpha;
273 Color highlightColor = faceColor + 0x303030;
274 highlightColor.a = alpha;
275 Color shadowColor = faceColor - 0x303030;
276 shadowColor.a = alpha;
277
278 graphics->setColor(getBackgroundColor());
279
280 int i = 0;
281 int const hh = (h + 1) / 2;
282
283 for (i = 1; i <= hh; ++i) {
284 graphics->drawLine(hh - i + 1, i, hh + i - 1, i);
285 }
286
287 for (i = 1; i < hh; ++i) {
288 graphics->drawLine(hh - i + 1, h - i, hh + i - 1, h - i);
289 }
290
291 graphics->setColor(shadowColor);
292 graphics->drawLine(hh, 0, 0, hh);
293 graphics->drawLine(hh + 1, 1, h - 1, hh - 1);
294
295 graphics->setColor(highlightColor);
296 graphics->drawLine(1, hh + 1, hh, h);
297 graphics->drawLine(hh + 1, h - 1, h, hh);
298
299 graphics->setColor(getForegroundColor());
300
301 int const hhh = hh - 3;
302
303 if (mSelected) {
304 for (i = 0; i < hhh; ++i) {
305 graphics->drawLine(hh - i, 4 + i, hh + i, 4 + i);
306 }
307 for (i = 0; i < hhh; ++i) {
308 graphics->drawLine(hh - i, h - 4 - i, hh + i, h - 4 - i);
309 }
310 }
311 graphics->popClipArea();
312
313 if (isFocused()) {
314 int fh = 0;
315
316 if (getHeight() % 2 == 0) {
317 fh = getHeight() - 4;
318 } else {
319 fh = getHeight() - 3;
320 }
321
322 int const fhh = (fh + 1) / 2;
323
324 graphics->setColor(getSelectionColor());
325 graphics->drawLine(0, fhh + 1, fhh + 1, 0);
326 graphics->drawLine(fhh + 2, 1, fh + 2, fhh + 1);
327 graphics->drawLine(fh + 1, fhh + 2, fhh + 1, fh + 2);
328 graphics->drawLine(fhh + 1, fh + 2, 1, fhh + 2);
329 }
330 }
331
333 {
334 return mSelected;
335 }
336
337 void CheckBox::setSelected(bool selected)
338 {
339 mSelected = selected;
340 }
341
343 {
345 }
346
347 void CheckBox::setBackgroundImage(std::string const & filename)
348 {
350 delete mBackgroundImage;
351 }
352 mBackgroundImage = Image::load(filename);
355 }
356
358 {
360 delete mBackgroundImage;
361 }
362 mBackgroundImage = image;
365 }
366
368 {
369 return mBackgroundImage;
370 }
371
373 {
374 return mMode;
375 }
376
378 {
379 mMode = mode;
380 }
381
383 {
384 Key const key = keyEvent.getKey();
385
386 if (key.getValue() == fcn::Key::KEY_RETURN || key.getValue() == fcn::Key::SPACE) {
388 keyEvent.consume();
390 }
391 }
392
394 {
395 Key const key = keyEvent.getKey();
396
397 if (key.getValue() == fcn::Key::KEY_RETURN || key.getValue() == fcn::Key::SPACE) {
398 keyEvent.consume();
399 }
400 }
401
403 {
404 if (mHasMouse) {
405 mouseEvent.consume();
406 }
407 }
408
410 {
411 if (mHasMouse) {
412 mouseEvent.consume();
413 }
414 }
415
417 {
418 if (mouseEvent.isConsumed()) {
419 return;
420 }
421
422 if (mouseEvent.getButton() == MouseEvent::Button::Left) {
424 mouseEvent.consume();
426 }
427 }
428
430 {
432 }
433
435 {
436 int w = 0;
437 int h = 0;
438 if (mBackgroundImage != nullptr) {
439 w += mBackgroundImage->getWidth() + (2 * getBorderSize());
440 h += mBackgroundImage->getHeight() + (2 * getBorderSize());
441 } else {
442 if (!getCaption().empty()) {
443 w = getFont()->getWidth(getCaption());
444 h = getFont()->getHeight();
445 }
446
447 if (getUpImage() != nullptr) {
448 w += getUpImage()->getWidth();
449 h = std::max(getUpImage()->getHeight(), h);
450 } else {
451 // without image we need extra space for the mark
452 w += h;
453 }
454 w += (2 * getBorderSize()) + getPaddingLeft() + getPaddingRight();
455 h += (2 * getBorderSize()) + getPaddingTop() + getPaddingBottom();
456 }
457 setSize(w, h);
458 }
459} // namespace fcn
std::string const & getCaption() const
Gets the caption of the button.
Definition button.cpp:52
bool isActive() const
Returns the button state.
Definition button.cpp:62
bool mHasMouse
True if the mouse is on top of the button, false otherwise.
Definition button.hpp:205
int getDownYOffset() const
Gets the number of pixels the image or text will be offset.
Definition button.cpp:92
int getDownXOffset() const
Gets the number of pixels the image or text will be offset.
Definition button.cpp:82
void setCaption(std::string const &caption)
Sets the caption of the button.
Definition button.cpp:46
void drawMarkerImage(Graphics *graphics, Rectangle const &rec)
Draws the marker image.
Definition checkbox.cpp:229
static void drawCheckmark(Graphics *graphics, Rectangle const &rec)
Draws the checkmark.
Definition checkbox.cpp:193
void setBackgroundImage(std::string const &filename)
Sets the background image to display, that includes the caption region.
Definition checkbox.cpp:347
void keyPressed(KeyEvent &keyEvent) override
Called if a key is pressed when the widget has keyboard focus.
Definition checkbox.cpp:382
Image const * getBackgroundImage() const
Gets background image.
Definition checkbox.cpp:367
MarkerStyle getMarkerStyle() const
Gets the marker mode of the check box.
Definition checkbox.cpp:372
virtual void setSelected(bool selected)
Sets the check box to be selected or not.
Definition checkbox.cpp:337
void keyReleased(KeyEvent &keyEvent) override
Called if a key is released when the widget has keyboard focus.
Definition checkbox.cpp:393
MarkerStyle mMode
Holds the marker style of the check box.
Definition checkbox.hpp:216
void mouseClicked(MouseEvent &mouseEvent) override
Called when a mouse button is pressed and released (clicked) on the widget area.
Definition checkbox.cpp:416
void draw(Graphics *graphics) override
Draws the widget.
Definition checkbox.cpp:84
Image const * mBackgroundImage
Holds the background image, that includes the caption region.
Definition checkbox.hpp:201
bool mSelected
True if the check box is selected, false otherwise.
Definition checkbox.hpp:211
virtual void drawBox(Graphics *graphics)
Draws the box of the check box.
Definition checkbox.cpp:103
virtual void toggleSelected()
Toggles the check box between being selected and not being selected.
Definition checkbox.cpp:342
void drawRhombus(Graphics *graphics)
Draws the rhombus.
Definition checkbox.cpp:258
bool mInternalBackgroundImage
True if the background image was loaded internally, false otherwise.
Definition checkbox.hpp:206
static void drawCross(Graphics *graphics, Rectangle const &rec)
Draws the cross.
Definition checkbox.cpp:205
void adjustSize() override
Resizes the widget's size to fit the content exactly.
Definition checkbox.cpp:429
void mousePressed(MouseEvent &mouseEvent) override
Called when a mouse button has been pressed down on the widget area.
Definition checkbox.cpp:402
MarkerStyle
Marker style.
Definition checkbox.hpp:39
void adjustSizeImpl() override
Adjusts the size of the checkbox to fit the caption.
Definition checkbox.cpp:434
void mouseReleased(MouseEvent &mouseEvent) override
Called when a mouse button has been released on the widget area.
Definition checkbox.cpp:409
void setMarkerStyle(MarkerStyle mode)
Set the marker style of the check box.
Definition checkbox.cpp:377
virtual bool isSelected() const
Checks if the check box is selected.
Definition checkbox.cpp:332
static void drawDot(Graphics *graphics, Rectangle const &rec)
Draws the dot.
Definition checkbox.cpp:220
Color.
Definition color.hpp:58
uint8_t a
Alpha color component (0-255).
Definition color.hpp:350
virtual int getWidth(std::string const &text) const =0
Gets the width of a string.
virtual int getHeight() const =0
Gets the height of the glyphs in the font.
Abstract interface providing primitive drawing functions (lines, rectangles, etc.).
Definition graphics.hpp:58
virtual void popClipArea()
Removes the top most clip area from the stack.
Definition graphics.cpp:65
virtual void drawImage(Image const *image, int srcX, int srcY, int dstX, int dstY, int width, int height)=0
Draws a part of an image.
void drawText(std::string const &text, int x, int y)
Draws text with a default left alignment.
Definition graphics.hpp:401
virtual bool pushClipArea(Rectangle area)
Pushes a clip area onto the stack.
Definition graphics.cpp:25
virtual void setFont(Font *font)
Sets the font to use when drawing text.
Definition graphics.cpp:91
virtual void drawLine(int x1, int y1, int x2, int y2)=0
Draws a line.
virtual void drawFillCircle(Point const &p, unsigned int radius)=0
Draws a filled circle.
virtual void setColor(Color const &color)=0
Sets the color to use when drawing.
virtual void fillRectangle(Rectangle const &rectangle)=0
Draws a filled rectangle.
Image const * getInactiveDownImage() const
Gets inactive down image.
Image const * getUpImage() const
Gets current up image.
Image const * getDownImage() const
Gets current down image.
Image const * getHoverImage() const
Gets current hover image.
Image const * getInactiveHoverImage() const
Gets inactive hover image.
Abstract holder for image data.
Definition image.hpp:34
static Image * load(std::string const &filename, bool convertToDisplayFormat=true)
Loads an image by using the class' image loader.
Definition image.cpp:44
virtual int getWidth() const =0
Gets the width of the image.
bool isConsumed() const
Checks if the input event is consumed.
void consume()
Marks this event as consumed.
Represents a key event.
Definition keyevent.hpp:26
Key const & getKey() const
Gets the key of the event.
Definition keyevent.cpp:48
Represents a mouse event.
MouseEvent::Button getButton() const
Gets the button of the mouse event.
Represents a 2D coordinate (X, Y).
Definition point.hpp:34
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:22
int width
Holds the width of the rectangle.
int y
Holds the x coordinate of the rectangle.
int x
Holds the x coordinate of the rectangle.
int height
Holds the height of the rectangle.
Color const & getBaseColor() const
Gets the base color.
Definition widget.cpp:742
int getWidth() const
Gets the width of the widget.
Definition widget.cpp:252
virtual bool isFocused() const
Checks if the widget is focused.
Definition widget.cpp:624
unsigned int getPaddingLeft() const
Gets the left padding.
Definition widget.cpp:604
virtual void setSize(int width, int height)
Sets the size of the widget.
Definition widget.cpp:1065
Color const & getBackgroundColor() const
Gets the background color.
Definition widget.cpp:762
virtual void drawBorder(Graphics *graphics)
Called when a widget have a border.
Definition widget.cpp:156
unsigned int getPaddingTop() const
Gets the top padding.
Definition widget.cpp:574
unsigned int getBorderSize() const
Gets the size of the widget's border.
Definition widget.cpp:474
virtual void drawSelectionFrame(Graphics *graphics)
Called when a widget is "active" and the selection mode is Frame or FrameWithBackground.
Definition widget.cpp:217
unsigned int getPaddingBottom() const
Gets the bottom padding.
Definition widget.cpp:594
Color const & getForegroundColor() const
Gets the foreground color.
Definition widget.cpp:752
SelectionMode getSelectionMode() const
Gets the selection mode.
Definition widget.cpp:802
Font * getFont() const
Gets the font set for the widget.
Definition widget.cpp:1000
void distributeActionEvent()
Distributes an action event to all action listeners of the widget.
Definition widget.cpp:1291
int getHeight() const
Gets the height of the widget.
Definition widget.cpp:265
unsigned int getPaddingRight() const
Gets the right padding.
Definition widget.cpp:584
Color const & getSelectionColor() const
Gets the selection color.
Definition widget.cpp:772
Used replacement tokens by configure_file():
void throwException(std::string const &message, std::source_location location=std::source_location::current())
Throw an Exception capturing the current source location.