FifeGUI 0.2.0
A C++ GUI library designed for games.
focushandler.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/focushandler.hpp"
6
7#include <algorithm>
8#include <list>
9#include <ranges>
10#include <utility>
11
12#include "fifechan/event.hpp"
13#include "fifechan/exception.hpp"
14#include "fifechan/focuslistener.hpp"
15#include "fifechan/widget.hpp"
16
17namespace fcn
18{
19 FocusHandler::FocusHandler() :
20 mFocusedWidget(nullptr),
21 mModalFocusedWidget(nullptr),
22 mModalMouseInputFocusedWidget(nullptr),
23 mDraggedWidget(nullptr),
24 mLastWidgetWithMouse(nullptr),
25 mLastWidgetWithModalFocus(nullptr),
26 mLastWidgetWithModalMouseInputFocus(nullptr),
27 mLastWidgetPressed(nullptr)
28 {
29 }
30
32 {
33 if (widget == nullptr || widget == mFocusedWidget) {
34 return;
35 }
36
37 unsigned int i = 0;
38 int toBeFocusedIndex = -1;
39 for (i = 0; i < mWidgets.size(); ++i) {
40 if (mWidgets[i] == widget) {
41 toBeFocusedIndex = i;
42 break;
43 }
44 }
45
46 if (toBeFocusedIndex < 0) {
47 throwException("Trying to focus a none existing widget.");
48 }
49
50 Widget* oldFocused = mFocusedWidget;
51
52 if (oldFocused != widget) {
53 mFocusedWidget = mWidgets.at(toBeFocusedIndex);
54
55 if (oldFocused != nullptr) {
56 Event const focusEvent(oldFocused);
57 distributeFocusLostEvent(focusEvent);
58 }
59
60 Event const focusEvent(mWidgets.at(toBeFocusedIndex));
62 }
63 }
64
66 {
67 if (mModalFocusedWidget != nullptr && mModalFocusedWidget != widget) {
68 throwException("Another widget already has modal focus.");
69 }
70
71 mModalFocusedWidget = widget;
72
73 if (mFocusedWidget != nullptr && !mFocusedWidget->isModalFocused()) {
74 focusNone();
75 }
76 }
77
79 {
81 throwException("Another widget already has modal input focus.");
82 }
83
85 }
86
88 {
89 if (mModalFocusedWidget == widget) {
90 mModalFocusedWidget = nullptr;
91 }
92 }
93
100
102 {
103 return mFocusedWidget;
104 }
105
110
115
117 {
118 int i = 0;
119 int focusedWidget = -1;
120
121 for (i = 0; std::cmp_less(i, mWidgets.size()); ++i) {
122 if (mWidgets[i] == mFocusedWidget) {
123 focusedWidget = i;
124 }
125 }
126
127 int const focused = focusedWidget;
128
129 // i is a counter that ensures that the following loop
130 // won't get stuck in an infinite loop
131 int num_widgets = static_cast<int>(mWidgets.size());
132
133 for (; num_widgets > 0; --num_widgets) {
134 ++focusedWidget;
135
136 if (std::cmp_greater_equal(focusedWidget, mWidgets.size())) {
137 focusedWidget = 0;
138 }
139
140 if (focusedWidget == focused) {
141 return;
142 }
143
144 if (mWidgets.at(focusedWidget)->isFocusable()) {
145 break;
146 }
147
148 if (num_widgets == 1) {
149 focusedWidget = -1;
150 break;
151 }
152 }
153
154 if (focusedWidget >= 0) {
155 mFocusedWidget = mWidgets.at(focusedWidget);
156 Event const focusEvent(mFocusedWidget);
157 distributeFocusGainedEvent(focusEvent);
158 }
159
160 if (focused >= 0) {
161 Event const focusEvent(mWidgets.at(focused));
162 distributeFocusLostEvent(focusEvent);
163 }
164 }
165
167 {
168 if (mWidgets.empty()) {
169 mFocusedWidget = nullptr;
170 return;
171 }
172
173 int i = 0;
174 int focusedWidget = -1;
175 for (i = 0; std::cmp_less(i, mWidgets.size()); ++i) {
176 if (mWidgets[i] == mFocusedWidget) {
177 focusedWidget = i;
178 }
179 }
180 int const focused = focusedWidget;
181
182 // i is a counter that ensures that the following loop
183 // won't get stuck in an infinite loop
184 i = static_cast<int>(mWidgets.size());
185 while (i > 0) {
186 --focusedWidget;
187
188 if (i == 1) {
189 focusedWidget = -1;
190 break;
191 }
192
193 --i;
194
195 if (focusedWidget < 0) {
196 focusedWidget = mWidgets.size() - 1;
197 }
198
199 if (focusedWidget == focused) {
200 return;
201 }
202
203 if (mWidgets.at(focusedWidget)->isFocusable()) {
204 break;
205 }
206 }
207
208 if (focusedWidget >= 0) {
209 mFocusedWidget = mWidgets.at(focusedWidget);
210 Event const focusEvent(mFocusedWidget);
211 distributeFocusGainedEvent(focusEvent);
212 }
213
214 if (focused >= 0) {
215 Event const focusEvent(mWidgets.at(focused));
216 distributeFocusLostEvent(focusEvent);
217 }
218 }
219
220 bool FocusHandler::isFocused(Widget const * widget) const
221 {
222 return mFocusedWidget == widget;
223 }
224
226 {
227 mWidgets.push_back(widget);
228 }
229
231 {
232 if (isFocused(widget)) {
233 mFocusedWidget = nullptr;
234 }
235
236 auto iter = std::ranges::find(mWidgets, widget);
237 if (iter != std::ranges::end(mWidgets)) {
238 mWidgets.erase(iter);
239 }
240
241 if (mDraggedWidget == widget) {
242 mDraggedWidget = nullptr;
243 return;
244 }
245
246 if (mLastWidgetWithMouse == widget) {
247 mLastWidgetWithMouse = nullptr;
248 return;
249 }
250
251 if (mLastWidgetWithModalFocus == widget) {
253 return;
254 }
255
258 return;
259 }
260
261 if (mLastWidgetPressed == widget) {
262 mLastWidgetPressed = nullptr;
263 return;
264 }
265 }
266
268 {
269 if (mFocusedWidget != nullptr) {
270 Widget* focused = mFocusedWidget;
271 mFocusedWidget = nullptr;
272
273 Event const focusEvent(focused);
274 distributeFocusLostEvent(focusEvent);
275 }
276 }
277
279 {
280 if (mFocusedWidget != nullptr) {
281 if (!mFocusedWidget->isTabOutEnabled()) {
282 return;
283 }
284 }
285
286 if (mWidgets.empty()) {
287 mFocusedWidget = nullptr;
288 return;
289 }
290
291 int i = 0;
292 int focusedWidget = -1;
293 for (i = 0; std::cmp_less(i, mWidgets.size()); ++i) {
294 if (mWidgets[i] == mFocusedWidget) {
295 focusedWidget = i;
296 }
297 }
298 int const focused = focusedWidget;
299
300 i = static_cast<int>(mWidgets.size());
301
302 while (i > 0) {
303 ++focusedWidget;
304
305 if (i == 1) {
306 focusedWidget = -1;
307 break;
308 }
309
310 --i;
311
312 if (std::cmp_greater_equal(focusedWidget, mWidgets.size())) {
313 focusedWidget = 0;
314 }
315
316 if (focusedWidget == focused) {
317 return;
318 }
319
320 if (mWidgets.at(focusedWidget)->isFocusable() && mWidgets.at(focusedWidget)->isTabInEnabled() &&
321 (mModalFocusedWidget == nullptr || mWidgets.at(focusedWidget)->isModalFocused())) {
322 break;
323 }
324 }
325
326 if (focusedWidget >= 0) {
327 mFocusedWidget = mWidgets.at(focusedWidget);
328 Event const focusEvent(mFocusedWidget);
329 distributeFocusGainedEvent(focusEvent);
330 }
331
332 if (focused >= 0) {
333 Event const focusEvent(mWidgets.at(focused));
334 distributeFocusLostEvent(focusEvent);
335 }
336 }
337
339 {
340 if (mFocusedWidget != nullptr) {
341 if (!mFocusedWidget->isTabOutEnabled()) {
342 return;
343 }
344 }
345
346 if (mWidgets.empty()) {
347 mFocusedWidget = nullptr;
348 return;
349 }
350
351 int i = 0;
352 int focusedWidget = -1;
353 for (i = 0; std::cmp_less(i, mWidgets.size()); ++i) {
354 if (mWidgets[i] == mFocusedWidget) {
355 focusedWidget = i;
356 }
357 }
358 int const focused = focusedWidget;
359
360 i = static_cast<int>(mWidgets.size());
361
362 while (i > 0) {
363 --focusedWidget;
364
365 if (i == 1) {
366 focusedWidget = -1;
367 break;
368 }
369
370 --i;
371
372 if (focusedWidget <= 0) {
373 focusedWidget = mWidgets.size() - 1;
374 }
375
376 if (focusedWidget == focused) {
377 return;
378 }
379
380 if (mWidgets.at(focusedWidget)->isFocusable() && mWidgets.at(focusedWidget)->isTabInEnabled() &&
381 (mModalFocusedWidget == nullptr || mWidgets.at(focusedWidget)->isModalFocused())) {
382 break;
383 }
384 }
385
386 if (focusedWidget >= 0) {
387 mFocusedWidget = mWidgets.at(focusedWidget);
388 Event const focusEvent(mFocusedWidget);
389 distributeFocusGainedEvent(focusEvent);
390 }
391
392 if (focused >= 0) {
393 Event const focusEvent(mWidgets.at(focused));
394 distributeFocusLostEvent(focusEvent);
395 }
396 }
397
399 {
400 Widget* sourceWidget = focusEvent.getSource();
401
402 std::list<FocusListener*> focusListeners = sourceWidget->_getFocusListeners();
403
404 // Send the event to all focus listeners of the widget.
405 for (auto& focusListener : focusListeners) {
406 focusListener->focusLost(focusEvent);
407 }
408 }
409
411 {
412 Widget* sourceWidget = focusEvent.getSource();
413
414 std::list<FocusListener*> focusListeners = sourceWidget->_getFocusListeners();
415
416 // Send the event to all focus listeners of the widget.
417 for (auto& focusListener : focusListeners) {
418 focusListener->focusGained(focusEvent);
419 }
420 }
421
426
428 {
429 mDraggedWidget = draggedWidget;
430 }
431
436
438 {
439 mLastWidgetWithMouse = lastWidgetWithMouse;
440 }
441
446
447 void FocusHandler::setLastWidgetWithModalFocus(Widget* lastWidgetWithModalFocus)
448 {
449 mLastWidgetWithModalFocus = lastWidgetWithModalFocus;
450 }
451
456
457 void FocusHandler::setLastWidgetWithModalMouseInputFocus(Widget* lastWidgetWithModalMouseInputFocus)
458 {
459 mLastWidgetWithModalMouseInputFocus = lastWidgetWithModalMouseInputFocus;
460 }
461
466
468 {
469 mLastWidgetPressed = lastWidgetPressed;
470 }
471
473} // namespace fcn
Base class for all GUI event objects.
Definition event.hpp:24
Widget * getSource() const
Gets the source widget of the event.
Definition event.cpp:11
Widget * mFocusedWidget
Holds the focused widget.
virtual Widget * getLastWidgetWithMouse()
Gets the last widget with the mouse.
WidgetVector mWidgets
Holds the widgets currently being handled by the focus handler.
Widget * mDraggedWidget
Holds the dragged widget.
virtual void focusPrevious()
Focuses the previous widget added to a container.
virtual void widgetHidden(Widget *widget)
Informs the focus handler that a widget was hidden.
virtual void setLastWidgetWithMouse(Widget *lastWidgetWithMouse)
Sets the last widget with the mouse.
virtual void focusNone()
Focuses nothing.
virtual Widget * getDraggedWidget()
Gets the widget being dragged.
Widget * mLastWidgetWithModalMouseInputFocus
Holds the last widget with modal mouse input focus.
Widget * mLastWidgetPressed
Holds the last widget pressed.
virtual void add(Widget *widget)
Adds a widget to by handles by the focus handler.
virtual void setLastWidgetWithModalFocus(Widget *lastWidgetWithModalFocus)
Sets the last widget with modal focus.
virtual void releaseModalFocus(Widget *widget)
Releases modal focus if the widget has modal focus.
virtual void setLastWidgetPressed(Widget *lastWidgetPressed)
Sets the last widget pressed.
virtual Widget * getLastWidgetWithModalMouseInputFocus()
Gets the last widget with modal mouse input focus.
virtual Widget * getLastWidgetWithModalFocus()
Gets the last widget with modal focus.
virtual void remove(Widget *widget)
Removes a widget from the focus handler.
Widget * mModalMouseInputFocusedWidget
Holds the modal mouse input focused widget.
virtual bool isFocused(Widget const *widget) const
Checks if a widget is focused.
virtual void distributeFocusGainedEvent(Event const &focusEvent)
Distributes a focus gained event.
virtual void tabPrevious()
Focuses the previous widget which allows tabbing in unless current focused widget disallows tabbing o...
Widget * mLastWidgetWithModalFocus
Holds the last widget with modal focus.
virtual void requestModalFocus(Widget *widget)
Requests modal focus for a widget.
virtual void tabNext()
Focuses the next widget which allows tabbing in unless the current focused Widget disallows tabbing o...
virtual void distributeFocusLostEvent(Event const &focusEvent)
Distributes a focus lost event.
virtual void focusNext()
Focuses the next widget added to a container.
Widget * mModalFocusedWidget
Holds the modal focused widget.
virtual Widget * getModalFocused() const
Gets the widget with modal focus.
virtual void setDraggedWidget(Widget *draggedWidget)
Sets the widget being dragged.
virtual void requestFocus(Widget *widget)
Requests focus for a widget.
virtual Widget * getModalMouseInputFocused() const
Gets the widget with modal mouse input focus.
virtual Widget * getLastWidgetPressed()
Gets the last widget pressed.
virtual void requestModalMouseInputFocus(Widget *widget)
Requests modal mouse input focus for a widget.
Widget * mLastWidgetWithMouse
Holds the last widget with the mouse.
virtual void setLastWidgetWithModalMouseInputFocus(Widget *lastWidgetWithModalMouseInputFocus)
Sets the last widget with modal mouse input focus.
virtual void releaseModalMouseInputFocus(Widget *widget)
Releases modal mouse input focus if the widget has modal mouse input focus.
virtual Widget * getFocused() const
Gets the widget with focus.
Abstract base class defining the common behavior, properties, and lifecycle of all GUI elements.
Definition widget.hpp:45
virtual std::list< FocusListener * > const & _getFocusListeners()
Gets the focus listeners of the widget.
Definition widget.cpp:987