FifeGUI 0.3.0
A C++ GUI library designed for games.
listbox.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/listbox.hpp"
7
8// Standard library includes
9#include <algorithm>
10#include <cassert>
11
12// Project headers (subdirs before local)
13#include "fifechan/font.hpp"
14#include "fifechan/graphics.hpp"
15#include "fifechan/key.hpp"
16#include "fifechan/listeners/selectionlistener.hpp"
17#include "fifechan/listmodel.hpp"
18#include "fifechan/mouseinput.hpp"
19
20namespace fcn
21{
22 ListBox::ListBox()
23 {
24 setWidth(100);
25 setFocusable(true);
26
27 addMouseListener(this);
28 addKeyListener(this);
29 }
30
31 ListBox::ListBox(ListModel* listModel)
32 {
33 setWidth(100);
34 setListModel(listModel);
35 setFocusable(true);
36
37 addMouseListener(this);
38 addKeyListener(this);
39 }
40
41 void ListBox::draw(Graphics* graphics)
42 {
43 assert("graphics must not be null" && graphics != nullptr);
44 assert("font must not be null" && getFont() != nullptr);
45
46 graphics->setColor(getBackgroundColor());
47 graphics->fillRectangle(0, 0, getWidth(), getHeight());
48
49 if (mListModel == nullptr) {
50 return;
51 }
52
53 graphics->setColor(getForegroundColor());
54 graphics->setFont(getFont());
55
56 // Check the current clip area so we don't draw unnecessary items
57 // that are not visible.
58 ClipRectangle const currentClipArea = graphics->getCurrentClipArea();
59 int const rowHeight = getRowHeight();
60
61 // Calculate the number of rows to draw by checking the clip area.
62 // The addition of two makes covers a partial visible row at the top
63 // and a partial visible row at the bottom.
64 int numberOfRows = (currentClipArea.height / rowHeight) + 2;
65
66 numberOfRows = std::min(numberOfRows, mListModel->getNumberOfElements());
67
68 // Calculate which row to start drawing. If the list box
69 // has a negative y coordinate value we should check if
70 // we should drop rows in the beginning of the list as
71 // they might not be visible. A negative y value is very
72 // common if the list box for instance resides in a scroll
73 // area and the user has scrolled the list box downwards.
74 int startRow = 0;
75 if (getY() < 0) {
76 startRow = -1 * (getY() / rowHeight);
77 } else {
78 startRow = 0;
79 }
80
81 int i = 0;
82 // The y coordinate where we start to draw the text is
83 // simply the y coordinate multiplied with the font height.
84 int y = rowHeight * startRow;
85 for (i = startRow; i < startRow + numberOfRows; ++i) {
86 if (i == mSelected) {
87 graphics->setColor(getSelectionColor());
88 graphics->fillRectangle(0, y, getWidth(), rowHeight);
89 graphics->setColor(getForegroundColor());
90 }
91
92 // If the row height is greater than the font height we
93 // draw the text with a center vertical alignment.
94 if (rowHeight > getFont()->getHeight()) {
95 graphics->drawText(mListModel->getElementAt(i), 1, y + (rowHeight / 2) - (getFont()->getHeight() / 2));
96 } else {
97 graphics->drawText(mListModel->getElementAt(i), 1, y);
98 }
99
100 y += rowHeight;
101 }
102 }
103
105 {
107 }
108
110 {
111 return mSelected;
112 }
113
114 void ListBox::setSelected(int selected)
115 {
116 if (mListModel == nullptr) {
117 mSelected = -1;
118 } else {
119 if (selected < 0) {
120 mSelected = -1;
121 } else if (selected >= mListModel->getNumberOfElements()) {
122 mSelected = mListModel->getNumberOfElements() - 1;
123 } else {
124 mSelected = selected;
125 }
126 }
127
128 Rectangle scroll;
129
130 if (mSelected < 0) {
131 scroll.y = 0;
132 } else {
133 scroll.y = getRowHeight() * mSelected;
134 }
135
136 scroll.height = getRowHeight();
137 showPart(scroll);
138
140 }
141
143 {
144 Key const key = keyEvent.getKey();
145
146 if (key.getValue() == fcn::Key::KEY_RETURN || key.getValue() == fcn::Key::SPACE) {
148 keyEvent.consume();
149 } else if (key.getValue() == fcn::Key::UP) {
150 assert("list model must exist for UP" && mListModel != nullptr);
152
153 if (mSelected == -1) {
154 if (mWrappingEnabled) {
155 setSelected(getListModel()->getNumberOfElements() - 1);
156 } else {
157 setSelected(0);
158 }
159 }
160
161 keyEvent.consume();
162 } else if (key.getValue() == fcn::Key::DOWN) {
163 assert("list model must exist for DOWN" && mListModel != nullptr);
164 if (mWrappingEnabled && getSelected() == getListModel()->getNumberOfElements() - 1) {
165 setSelected(0);
166 } else {
168 }
169
170 keyEvent.consume();
171 } else if (key.getValue() == fcn::Key::HOME) {
172 setSelected(0);
173 keyEvent.consume();
174 } else if (key.getValue() == fcn::Key::END) {
175 assert("list model must exist for END" && mListModel != nullptr);
176 setSelected(getListModel()->getNumberOfElements() - 1);
177 keyEvent.consume();
178 }
179 }
180
182 {
183 if (mouseEvent.getButton() == MouseEvent::Button::Left) {
184 setSelected(mouseEvent.getY() / getRowHeight());
186 }
187 }
188
190 {
191 if (isFocused()) {
192 if (getSelected() > 0) {
194 }
195
196 mouseEvent.consume();
197 }
198 }
199
201 {
202 if (isFocused()) {
204
205 mouseEvent.consume();
206 }
207 }
208
210 {
211 mouseEvent.consume();
212 }
213
215 {
216 mSelected = -1;
217 mListModel = listModel;
219 }
220
222 {
223 return mListModel;
224 }
225
226 void ListBox::resizeToContent(bool recursion)
227 {
228 static_cast<void>(recursion);
230 }
232 {
233 if (mListModel != nullptr) {
234 // min width in case the lit contains no element
235 int w = getRowHeight();
236 int const elements = mListModel->getNumberOfElements();
237 for (int i = 0; i < elements; ++i) {
238 // std::string element = mListModel->getElementAt(i);
239 w = std::max(w, getFont()->getWidth(mListModel->getElementAt(i)));
240 }
241 setWidth(w);
242 setHeight(getRowHeight() * mListModel->getNumberOfElements());
243 }
244 }
245
247 {
249 }
250
252 {
253 return mWrappingEnabled;
254 }
255
256 void ListBox::setWrappingEnabled(bool wrappingEnabled)
257 {
258 mWrappingEnabled = wrappingEnabled;
259 }
260
262 {
263 mSelectionListeners.push_back(selectionListener);
264 }
265
266 // cppcheck-suppress constParameterPointer
268 {
269 mSelectionListeners.remove(selectionListener);
270 }
271
273 {
275
276 for (iter = mSelectionListeners.begin(); iter != mSelectionListeners.end(); ++iter) {
277 SelectionEvent const event(this);
278 (*iter)->valueChanged(event);
279 }
280 }
281
282 unsigned int ListBox::getRowHeight() const
283 {
284 return getFont()->getHeight();
285 }
286} // namespace fcn
A rectangle specifically used for clipping rendering regions.
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
void drawText(std::string const &text, int x, int y)
Draws text with a default left alignment.
Definition graphics.hpp:401
virtual ClipRectangle const & getCurrentClipArea()
Gets the current clip area.
Definition graphics.cpp:74
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.
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
void setListModel(ListModel *listModel)
Sets the list model to use.
Definition listbox.cpp:214
void setWrappingEnabled(bool wrappingEnabled)
Sets the list box to wrap or not when selecting items with a keyboard.
Definition listbox.cpp:256
void adjustSize() override
Resizes the widget's size to fit the content exactly.
Definition listbox.cpp:246
int getSelected() const
Gets the selected item as an index in the list model.
Definition listbox.cpp:109
void mouseWheelMovedDown(MouseEvent &mouseEvent) override
Called when the mouse wheel has moved down on the widget area.
Definition listbox.cpp:200
void mouseDragged(MouseEvent &mouseEvent) override
Called when the mouse has moved and the mouse has previously been pressed on the widget.
Definition listbox.cpp:209
bool isWrappingEnabled() const
Checks whether the list box wraps when selecting items with a keyboard.
Definition listbox.cpp:251
void mouseWheelMovedUp(MouseEvent &mouseEvent) override
Called when the mouse wheel has moved up on the widget area.
Definition listbox.cpp:189
SelectionListenerList::iterator SelectionListenerIterator
Iterator for SelectionListenerList.
Definition listbox.hpp:205
int mSelected
The selected item as an index in the list model.
Definition listbox.hpp:182
void logic() override
Called for all widgets in the GUI each time Gui::logic is called.
Definition listbox.cpp:104
void addSelectionListener(SelectionListener *selectionListener)
Adds a selection listener to the list box.
Definition listbox.cpp:261
void adjustSizeImpl()
Concrete implementation of adjustSize.
Definition listbox.cpp:231
void removeSelectionListener(SelectionListener *selectionListener)
Removes a selection listener from the list box.
Definition listbox.cpp:267
bool mWrappingEnabled
True if wrapping is enabled, false otherwise.
Definition listbox.hpp:192
void mousePressed(MouseEvent &mouseEvent) override
Called when a mouse button has been pressed down on the widget area.
Definition listbox.cpp:181
void resizeToContent(bool recursion=true) override
Resizes the widget's size to fit the content exactly, calls recursively all childs.
Definition listbox.cpp:226
void keyPressed(KeyEvent &keyEvent) override
Called if a key is pressed when the widget has keyboard focus.
Definition listbox.cpp:142
ListModel * mListModel
The list model to use.
Definition listbox.hpp:187
ListModel * getListModel() const
Gets the list model used.
Definition listbox.cpp:221
void draw(Graphics *graphics) override
Draws the widget.
Definition listbox.cpp:41
SelectionListenerList mSelectionListeners
The selection listeners of the list box.
Definition listbox.hpp:202
void distributeValueChangedEvent()
Notifies all registered selection listeners of a value change.
Definition listbox.cpp:272
void setSelected(int selected)
Sets the selected item.
Definition listbox.cpp:114
virtual unsigned int getRowHeight() const
Gets the height of a row.
Definition listbox.cpp:282
Interface for a data model representing a list (used by ListBox/DropDown).
Definition listmodel.hpp:26
Represents a mouse event.
int getY() const
Gets the y coordinate of the mouse event.
MouseEvent::Button getButton() const
Gets the button of the mouse event.
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:22
int y
Holds the x coordinate of the rectangle.
int height
Holds the height of the rectangle.
Represents a change in selection state (e.g., list item selected).
Interface for listening to selection change events.
int getY() const
Gets the y coordinate of the widget.
Definition widget.cpp:301
void setFocusable(bool focusable)
Sets the widget to be focusable, or not.
Definition widget.cpp:646
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
virtual void showPart(Rectangle rectangle)
Shows a certain part of a widget in the widget's parent.
Definition widget.cpp:1309
Color const & getBackgroundColor() const
Gets the background color.
Definition widget.cpp:762
void addMouseListener(MouseListener *mouseListener)
Adds a mouse listener to the widget.
Definition widget.cpp:907
virtual void setWidth(int width)
Sets the width of the widget.
Definition widget.cpp:244
void addKeyListener(KeyListener *keyListener)
Adds a key listener to the widget.
Definition widget.cpp:885
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
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
virtual void setHeight(int height)
Sets the height of the widget.
Definition widget.cpp:257
Color const & getSelectionColor() const
Gets the selection color.
Definition widget.cpp:772
Used replacement tokens by configure_file():