FifeGUI 0.2.0
A C++ GUI library designed for games.
menuitem.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2026 Fifengine contributors
3
4// Corresponding header include
5#include "fifechan/widgets/menuitem.hpp"
6
7// Standard library includes
8#include <algorithm>
9#include <memory>
10#include <string>
11
12// Project headers
13#include "fifechan/font.hpp"
14#include "fifechan/graphics.hpp"
15
16namespace fcn
17{
18 MenuItem::MenuItem(std::string const & text)
19 {
20 setCaption(text);
22 }
23
25 {
26 mType = type;
27 }
28
30 {
31 return mType;
32 }
33
35 {
36 mSubmenu = popup;
37 if (popup != nullptr) {
38 mType = Type::Submenu;
39 }
40 }
41
43 {
44 return mSubmenu;
45 }
46
47 void MenuItem::setText(std::string const & text)
48 {
49 setCaption(text);
50 }
51
52 std::string const & MenuItem::getText() const
53 {
54 return getCaption();
55 }
56
57 void MenuItem::setIcon(Image const * image)
58 {
59 mIcon = image;
60 }
61
62 Image const * MenuItem::getIcon() const
63 {
64 return mIcon;
65 }
66
67 void MenuItem::setIconGlyph(std::string const & glyph, Font* font)
68 {
69 mIconGlyph = glyph;
70 mIconGlyphFont = font;
71 }
72
73 std::string const & MenuItem::getIconGlyph() const
74 {
75 return mIconGlyph;
76 }
77
79 {
80 mIconGlyphFont = font;
81 }
82
84 {
85 return mIconGlyphFont;
86 }
87
88 void MenuItem::setShortcut(std::string const & shortcut)
89 {
90 mShortcut = shortcut;
91 }
92
93 std::string const & MenuItem::getShortcut() const
94 {
95 return mShortcut;
96 }
97
98 void MenuItem::setChecked(bool checked)
99 {
100 mChecked = checked;
101 }
102
104 {
105 return mChecked;
106 }
107
108 // cppcheck-suppress duplInheritedMember
109 void MenuItem::setEnabled(bool enabled)
110 {
111 mEnabled = enabled;
112 }
113
114 // cppcheck-suppress duplInheritedMember
116 {
117 return mEnabled;
118 }
119
120 void MenuItem::draw(Graphics* graphics)
121 {
122 // Skip if separator
123 if (mType == Type::Separator) {
124 return;
125 }
126
127 // Draw background on hover
128 if (mHasMouse && mEnabled) {
129 graphics->setColor(getSelectionColor());
130 graphics->fillRectangle(Rectangle(0, 0, getWidth(), getHeight()));
131 }
132
133 // Use column layout if available, otherwise use legacy positioning
134 if (mColumnLayout != nullptr) {
135 // Compute vertical text position for the main font
136 int const textY =
138
139 // Draw icon glyph (if present) or image/checkmark in icon column
140 if (!mIconGlyph.empty()) {
141 Font* iconFont = mIconGlyphFont != nullptr ? mIconGlyphFont : getFont();
142 graphics->setFont(iconFont);
143 // Use white for emoji glyphs to preserve native OT-SVG color font colors
144 unsigned char const first = static_cast<unsigned char>(mIconGlyph.at(0));
145 if (first >= 0xF0 || first == 0xE2) {
146 graphics->setColor(Color(255, 255, 255, 255));
147 } else {
148 graphics->setColor(getForegroundColor());
149 }
150 int const iconY = getPaddingTop() +
151 ((getHeight() - getPaddingTop() - getPaddingBottom() - iconFont->getHeight()) / 2);
152 graphics->drawText(mIconGlyph, mColumnLayout->xIcon, iconY, Graphics::Alignment::Left);
153 } else if (mIcon != nullptr) {
154 int const iconY = (getHeight() - mIcon->getHeight()) / 2;
155 graphics->drawImage(mIcon, mColumnLayout->xIcon, iconY);
156 } else if (mType == Type::Checkable && mChecked) {
157 graphics->setFont(getFont());
158 graphics->setColor(getForegroundColor());
159 graphics->drawText("[x]", mColumnLayout->xIcon, textY, Graphics::Alignment::Left);
160 }
161
162 // Draw caption (left-aligned in caption column) using main font
163 if (!mCaption.empty()) {
164 graphics->setFont(getFont());
165 graphics->setColor(getForegroundColor());
166 graphics->drawText(mCaption, mColumnLayout->xCaption, textY, Graphics::Alignment::Left);
167 }
168
169 // Draw shortcut (right-aligned in shortcut column)
170 if (!mShortcut.empty()) {
171 graphics->setFont(getFont());
172 graphics->setColor(getForegroundColor());
173 graphics->drawText(
174 mShortcut,
175 mColumnLayout->xShortcut + mColumnLayout->cols.shortcutW,
176 textY,
177 Graphics::Alignment::Right);
178 }
179
180 // Draw arrow for submenu items (right-aligned)
181 if (mType == Type::Submenu) {
182 graphics->setFont(getFont());
183 graphics->setColor(getForegroundColor());
184 graphics->drawText(
185 " ", mColumnLayout->xArrow + mColumnLayout->cols.arrowW, textY, Graphics::Alignment::Right);
186 }
187 } else {
188 // Legacy rendering (fallback when no column layout)
189 int textX = 4;
190
191 // Draw icon if present
192 if (!mIconGlyph.empty()) {
193 Font* iconFont = mIconGlyphFont != nullptr ? mIconGlyphFont : getFont();
194 graphics->setFont(iconFont);
195 int const iconY = getPaddingTop() +
196 ((getHeight() - getPaddingTop() - getPaddingBottom() - iconFont->getHeight()) / 2);
197 graphics->setColor(getForegroundColor());
198 graphics->drawText(mIconGlyph, 4, iconY, Graphics::Alignment::Left);
199 textX += iconFont->getWidth(mIconGlyph) + 4;
200 } else if (mIcon != nullptr) {
201 graphics->drawImage(mIcon, 4, (getHeight() - mIcon->getHeight()) / 2);
202 textX += mIcon->getWidth() + 4;
203 }
204
205 // Ensure font and color are set for text rendering
206 graphics->setFont(getFont());
207 graphics->setColor(getForegroundColor());
208
209 // Compute vertical text position similar to Button::draw so text is
210 // vertically centered relative to the font metrics and padding.
211 int const textY =
213
214 // Draw text
215 if (!mCaption.empty()) {
216 graphics->drawText(mCaption, textX, textY, Graphics::Alignment::Left);
217 }
218
219 // Draw shortcut text (right-aligned)
220 if (!mShortcut.empty()) {
221 graphics->drawText(mShortcut, getWidth() - 4, textY, Graphics::Alignment::Right);
222 }
223
224 // Draw checkmark for checked items
225 if (mType == Type::Checkable && mChecked) {
226 graphics->setColor(getForegroundColor());
227 graphics->drawText("[x]", 4, textY, Graphics::Alignment::Left);
228 }
229
230 // Draw arrow for submenu items
231 if (mType == Type::Submenu) {
232 graphics->setColor(getForegroundColor());
233 graphics->drawText(" ", getWidth() - 4, textY, Graphics::Alignment::Right);
234 }
235 }
236 }
237
238 // Mouse events use the base `Button` implementations; no overrides needed.
239
241 {
243
244 // For separators, return minimal dimensions
245 if (mType == Type::Separator) {
246 m.height = 2;
247 return m;
248 }
249
250 // Icon width: image, glyph, or checkmark space
251 if (mIcon != nullptr) {
252 m.iconW = mIcon->getWidth();
253 } else if (!mIconGlyph.empty()) {
254 if (mIconGlyphFont != nullptr) {
255 m.iconW = mIconGlyphFont->getWidth(mIconGlyph);
256 } else {
257 m.iconW = font.getWidth(mIconGlyph);
258 }
259 } else if (mType == Type::Checkable) {
260 // Space for "[x]" checkmark
261 m.iconW = font.getWidth("[x]");
262 }
263
264 // Caption width
265 if (!mCaption.empty()) {
266 m.captionW = font.getWidth(mCaption);
267 }
268
269 // Shortcut width
270 if (!mShortcut.empty()) {
271 m.shortcutW = font.getWidth(mShortcut);
272 }
273
274 // Arrow width for submenus
275 if (mType == Type::Submenu) {
276 m.arrowW = font.getWidth(" >") + 8; // Add some padding for the arrow
277 }
278
279 // Height: max of icon height or font height, plus vertical padding
280 int contentH = font.getHeight();
281 if (mIcon != nullptr) {
282 contentH = std::max(contentH, mIcon->getHeight());
283 }
284 if (!mIconGlyph.empty()) {
285 int const iconFontH = mIconGlyphFont != nullptr ? mIconGlyphFont->getHeight() : font.getHeight();
286 contentH = std::max(contentH, iconFontH);
287 }
288 // Add vertical padding (top + bottom)
289 int const vpad = getPaddingTop() + getPaddingBottom();
290 m.height = contentH + vpad;
291
292 return m;
293 }
294
296 {
297 mColumnLayout = std::make_unique<ColumnLayout>(layout);
298 }
299} // namespace fcn
std::string const & getCaption() const
Gets the caption of the button.
Definition button.cpp:52
bool mHasMouse
True if the mouse is on top of the button, false otherwise.
Definition button.hpp:205
std::string mCaption
Holds the caption of the button.
Definition button.hpp:200
void setCaption(std::string const &caption)
Sets the caption of the button.
Definition button.cpp:46
Color.
Definition color.hpp:58
Abstract interface for font rendering.
Definition font.hpp:26
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 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 void setFont(Font *font)
Sets the font to use when drawing text.
Definition graphics.cpp:91
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.
Abstract holder for image data.
Definition image.hpp:34
void setEnabled(bool enabled)
Sets whether the item is enabled.
Definition menuitem.cpp:109
void setChecked(bool checked)
Sets whether the item is checked (for checkable items).
Definition menuitem.cpp:98
bool mChecked
Checked state.
Definition menuitem.hpp:332
void setSubmenu(MenuPopup *popup)
Sets the submenu popup (for submenu-type items).
Definition menuitem.cpp:34
Font * getIconGlyphFont() const
Get the font used for the icon glyph.
Definition menuitem.cpp:83
void setShortcut(std::string const &shortcut)
Sets the shortcut text (displayed on the right).
Definition menuitem.cpp:88
std::string const & getIconGlyph() const
Get the icon glyph string.
Definition menuitem.cpp:73
void draw(Graphics *graphics) override
Draws the widget.
Definition menuitem.cpp:120
void setIconGlyphFont(Font *font)
Set the font used to render the icon glyph.
Definition menuitem.cpp:78
MenuPopup * getSubmenu() const
Gets the submenu popup.
Definition menuitem.cpp:42
Type mType
Type of menu item.
Definition menuitem.hpp:303
MenuPopup * mSubmenu
Submenu popup (for submenu items).
Definition menuitem.hpp:308
std::string mIconGlyph
Optional icon glyph (emoji or glyph string) and font to render it.
Definition menuitem.hpp:323
Image const * getIcon() const
Get the optional icon image.
Definition menuitem.cpp:62
void setIconGlyph(std::string const &glyph, Font *font=nullptr)
Icon glyph support using a font (e.g.
Definition menuitem.cpp:67
bool isChecked() const
Checks if the item is checked.
Definition menuitem.cpp:103
Type
The type of menu item.
Definition menuitem.hpp:135
void setType(Type type)
Sets the menu item type.
Definition menuitem.cpp:24
void setIcon(Image const *image)
Icon support (optional image to render at the left).
Definition menuitem.cpp:57
std::string const & getText() const
Get the menu item text.
Definition menuitem.cpp:52
MenuItem(std::string const &text)
Constructor.
Definition menuitem.cpp:18
Image const * mIcon
Optional icon image.
Definition menuitem.hpp:318
bool isEnabled() const
Checks if the item is enabled.
Definition menuitem.cpp:115
MenuItemMetrics measure(Font const &font) const
Measures the content metrics of this menu item.
Definition menuitem.cpp:240
std::string const & getShortcut() const
Gets the shortcut text.
Definition menuitem.cpp:93
void layoutColumns(ColumnLayout const &layout)
Applies column layout positions for rendering.
Definition menuitem.cpp:295
void setText(std::string const &text)
Text API aliases for compatibility.
Definition menuitem.cpp:47
std::string mShortcut
Shortcut text.
Definition menuitem.hpp:313
bool mEnabled
Enabled state.
Definition menuitem.hpp:338
std::unique_ptr< ColumnLayout > mColumnLayout
Cached column layout for rendering.
Definition menuitem.hpp:343
Type getType() const
Gets the menu item type.
Definition menuitem.cpp:29
Font * mIconGlyphFont
Font used to render mIconGlyph when set.
Definition menuitem.hpp:327
A menu popup widget that displays a dropdown menu.
Definition menupopup.hpp:38
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:22
int getWidth() const
Gets the width of the widget.
Definition widget.cpp:251
void setBorderSize(unsigned int size)
Sets the size of the widget's border.
Definition widget.cpp:468
unsigned int getPaddingTop() const
Gets the top padding.
Definition widget.cpp:573
unsigned int getPaddingBottom() const
Gets the bottom padding.
Definition widget.cpp:593
Color const & getForegroundColor() const
Gets the foreground color.
Definition widget.cpp:751
Font * getFont() const
Gets the font set for the widget.
Definition widget.cpp:999
int getHeight() const
Gets the height of the widget.
Definition widget.cpp:264
Color const & getSelectionColor() const
Gets the selection color.
Definition widget.cpp:771
Used replacement tokens by configure_file():
Column layout specification passed to items for rendering.
Definition menuitem.hpp:85
Layout metrics for a single menu item (content only, no padding/borders).
Definition menuitem.hpp:28
int captionW
Width of caption text.
Definition menuitem.hpp:37
int height
Height of the item.
Definition menuitem.hpp:52
int iconW
Width of icon column (or checkmark space).
Definition menuitem.hpp:32
int shortcutW
Width of shortcut text.
Definition menuitem.hpp:42
int arrowW
Width of submenu arrow.
Definition menuitem.hpp:47