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