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