GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* |
||
2 |
* The ManaPlus Client |
||
3 |
* Copyright (C) 2004-2009 The Mana World Development Team |
||
4 |
* Copyright (C) 2009-2010 The Mana Developers |
||
5 |
* Copyright (C) 2011-2019 The ManaPlus Developers |
||
6 |
* Copyright (C) 2019-2021 Andrei Karas |
||
7 |
* |
||
8 |
* This file is part of The ManaPlus Client. |
||
9 |
* |
||
10 |
* This program is free software; you can redistribute it and/or modify |
||
11 |
* it under the terms of the GNU General Public License as published by |
||
12 |
* the Free Software Foundation; either version 2 of the License, or |
||
13 |
* any later version. |
||
14 |
* |
||
15 |
* This program is distributed in the hope that it will be useful, |
||
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 |
* GNU General Public License for more details. |
||
19 |
* |
||
20 |
* You should have received a copy of the GNU General Public License |
||
21 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
22 |
*/ |
||
23 |
|||
24 |
/* _______ __ __ __ ______ __ __ _______ __ __ |
||
25 |
* / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\ |
||
26 |
* / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / / |
||
27 |
* / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / / |
||
28 |
* / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / / |
||
29 |
* /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ / |
||
30 |
* \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/ |
||
31 |
* |
||
32 |
* Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson |
||
33 |
* |
||
34 |
* |
||
35 |
* Per Larsson a.k.a finalman |
||
36 |
* Olof Naessén a.k.a jansem/yakslem |
||
37 |
* |
||
38 |
* Visit: http://guichan.sourceforge.net |
||
39 |
* |
||
40 |
* License: (BSD) |
||
41 |
* Redistribution and use in source and binary forms, with or without |
||
42 |
* modification, are permitted provided that the following conditions |
||
43 |
* are met: |
||
44 |
* 1. Redistributions of source code must retain the above copyright |
||
45 |
* notice, this list of conditions and the following disclaimer. |
||
46 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
47 |
* notice, this list of conditions and the following disclaimer in |
||
48 |
* the documentation and/or other materials provided with the |
||
49 |
* distribution. |
||
50 |
* 3. Neither the name of Guichan nor the names of its contributors may |
||
51 |
* be used to endorse or promote products derived from this software |
||
52 |
* without specific prior written permission. |
||
53 |
* |
||
54 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
55 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
56 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
57 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
58 |
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
59 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
||
60 |
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||
61 |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||
62 |
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||
63 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
64 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
65 |
*/ |
||
66 |
|||
67 |
#include "gui/gui.h" |
||
68 |
|||
69 |
#include "gui/focushandler.h" |
||
70 |
#include "gui/sdlinput.h" |
||
71 |
#include "gui/viewport.h" |
||
72 |
|||
73 |
#include "gui/fonts/font.h" |
||
74 |
|||
75 |
#include "gui/widgets/label.h" |
||
76 |
#include "gui/widgets/window.h" |
||
77 |
|||
78 |
#include "gui/widgets/tabs/tab.h" |
||
79 |
|||
80 |
#ifndef DYECMD |
||
81 |
#include "dragdrop.h" |
||
82 |
#else // DYECMD |
||
83 |
#include "resources/image/image.h" |
||
84 |
#endif // DYECMD |
||
85 |
#include "settings.h" |
||
86 |
|||
87 |
#include "listeners/focuslistener.h" |
||
88 |
#include "listeners/guiconfiglistener.h" |
||
89 |
#include "listeners/keylistener.h" |
||
90 |
|||
91 |
#include "input/inputmanager.h" |
||
92 |
|||
93 |
#include "input/touch/touchmanager.h" |
||
94 |
|||
95 |
#include "render/renderers.h" |
||
96 |
|||
97 |
#include "resources/imageset.h" |
||
98 |
|||
99 |
#include "resources/resourcemanager/resourcemanager.h" |
||
100 |
|||
101 |
#include "utils/delete2.h" |
||
102 |
#include "utils/foreach.h" |
||
103 |
#include "utils/langs.h" |
||
104 |
#include "utils/sdlsharedhelper.h" |
||
105 |
#include "utils/timer.h" |
||
106 |
|||
107 |
#include "net/ipc.h" |
||
108 |
|||
109 |
#include "debug.h" |
||
110 |
|||
111 |
Gui *gui = nullptr; |
||
112 |
Font *boldFont = nullptr; |
||
113 |
|||
114 |
127 |
Gui::Gui() : |
|
115 |
mTop(nullptr), |
||
116 |
mGraphics(nullptr), |
||
117 |
mInput(nullptr), |
||
118 |
✓✗ | 127 |
mFocusHandler(new FocusHandler), |
119 |
mKeyListeners(), |
||
120 |
mLastMousePressButton(MouseButton::EMPTY), |
||
121 |
mLastMousePressTimeStamp(0U), |
||
122 |
mLastMouseX(0), |
||
123 |
mLastMouseY(0), |
||
124 |
mClickCount(1), |
||
125 |
mLastMouseDragButton(MouseButton::EMPTY), |
||
126 |
mWidgetWithMouseQueue(), |
||
127 |
✓✗ | 254 |
mConfigListener(new GuiConfigListener(this)), |
128 |
mGuiFont(nullptr), |
||
129 |
mInfoParticleFont(nullptr), |
||
130 |
mHelpFont(nullptr), |
||
131 |
mSecureFont(nullptr), |
||
132 |
mNpcFont(nullptr), |
||
133 |
mMouseCursors(nullptr), |
||
134 |
mMouseCursorAlpha(1.0F), |
||
135 |
mMouseInactivityTimer(0), |
||
136 |
mCursorType(Cursor::CURSOR_POINTER), |
||
137 |
#ifdef ANDROID |
||
138 |
mLastMouseRealX(0), |
||
139 |
mLastMouseRealY(0), |
||
140 |
#endif // ANDROID |
||
141 |
mFocusListeners(), |
||
142 |
✓✗ | 127 |
mForegroundColor(theme->getColor(ThemeColorId::TEXT, 255)), |
143 |
✓✗ | 127 |
mForegroundColor2(theme->getColor(ThemeColorId::TEXT_OUTLINE, 255)), |
144 |
mTime(0), |
||
145 |
mTime10(0), |
||
146 |
mCustomCursor(false), |
||
147 |
1016 |
mDoubleClick(true) |
|
148 |
{ |
||
149 |
127 |
} |
|
150 |
|||
151 |
127 |
void Gui::postInit(Graphics *const graphics) |
|
152 |
{ |
||
153 |
127 |
logger->log1("Initializing GUI..."); |
|
154 |
// Set graphics |
||
155 |
127 |
setGraphics(graphics); |
|
156 |
|||
157 |
// Set input |
||
158 |
✓✗ | 127 |
guiInput = new SDLInput; |
159 |
254 |
setInput(guiInput); |
|
160 |
|||
161 |
// Set focus handler |
||
162 |
✓✗ | 127 |
delete mFocusHandler; |
163 |
✓✗ | 127 |
mFocusHandler = new FocusHandler; |
164 |
|||
165 |
// Initialize top GUI widget |
||
166 |
✓✗ | 127 |
WindowContainer *const guiTop = new WindowContainer(nullptr); |
167 |
127 |
guiTop->setFocusable(true); |
|
168 |
127 |
guiTop->setSize(graphics->mWidth, graphics->mHeight); |
|
169 |
254 |
guiTop->setOpaque(Opaque_false); |
|
170 |
127 |
Window::setWindowContainer(guiTop); |
|
171 |
127 |
setTop(guiTop); |
|
172 |
|||
173 |
254 |
const StringVect langs = getLang(); |
|
174 |
✓✗ | 238 |
const bool isJapan = (!langs.empty() && langs[0].size() > 3 |
175 |
✓✓✓✗ ✓✗✓✓ ✗✗ |
587 |
&& langs[0].substr(0, 3) == "ja_"); |
176 |
✓✗ | 238 |
const bool isChinese = (!langs.empty() && langs[0].size() > 3 |
177 |
✓✓✓✗ ✓✗✓✓ ✗✗ |
587 |
&& langs[0].substr(0, 3) == "zh_"); |
178 |
|||
179 |
// Set global font |
||
180 |
✓✗✓✗ |
508 |
const int fontSize = config.getIntValue("fontSize"); |
181 |
✓✗✓✗ ✓✗ |
1016 |
std::string fontFile = config.getValue("font", ""); |
182 |
✗✓ | 127 |
if (isJapan) |
183 |
{ |
||
184 |
fontFile = config.getValue("japanFont", ""); |
||
185 |
if (fontFile.empty()) |
||
186 |
fontFile = branding.getStringValue("japanFont"); |
||
187 |
} |
||
188 |
✗✓ | 127 |
else if (isChinese) |
189 |
{ |
||
190 |
fontFile = config.getValue("chinaFont", ""); |
||
191 |
if (fontFile.empty()) |
||
192 |
fontFile = branding.getStringValue("chinaFont"); |
||
193 |
} |
||
194 |
✓✗ | 127 |
if (fontFile.empty()) |
195 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("font"); |
196 |
|||
197 |
✓✗✓✗ |
254 |
mGuiFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL); |
198 |
|||
199 |
// Set particle font |
||
200 |
✓✗✓✗ ✓✗ |
1016 |
fontFile = config.getValue("particleFont", ""); |
201 |
✗✓ | 127 |
if (isJapan) |
202 |
{ |
||
203 |
fontFile = config.getValue("japanFont", ""); |
||
204 |
if (fontFile.empty()) |
||
205 |
fontFile = branding.getStringValue("japanFont"); |
||
206 |
} |
||
207 |
✗✓ | 127 |
else if (isChinese) |
208 |
{ |
||
209 |
fontFile = config.getValue("chinaFont", ""); |
||
210 |
if (fontFile.empty()) |
||
211 |
fontFile = branding.getStringValue("chinaFont"); |
||
212 |
} |
||
213 |
✓✗ | 127 |
if (fontFile.empty()) |
214 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("particleFont"); |
215 |
|||
216 |
✓✗✓✗ |
254 |
mInfoParticleFont = new Font(fontFile, fontSize, TTF_STYLE_BOLD); |
217 |
|||
218 |
// Set bold font |
||
219 |
✓✗✓✗ ✓✗ |
1016 |
fontFile = config.getValue("boldFont", ""); |
220 |
✓✗ | 127 |
if (fontFile.empty()) |
221 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("boldFont"); |
222 |
|||
223 |
✓✗✓✗ |
254 |
boldFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL); |
224 |
|||
225 |
// Set help font |
||
226 |
✓✗✓✗ ✓✗ |
1016 |
fontFile = config.getValue("helpFont", ""); |
227 |
✓✗ | 127 |
if (fontFile.empty()) |
228 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("helpFont"); |
229 |
|||
230 |
✓✗✓✗ |
254 |
mHelpFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL); |
231 |
|||
232 |
// Set secure font |
||
233 |
✓✗✓✗ ✓✗ |
1016 |
fontFile = config.getValue("secureFont", ""); |
234 |
✓✗ | 127 |
if (fontFile.empty()) |
235 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("secureFont"); |
236 |
|||
237 |
✓✗✓✗ |
254 |
mSecureFont = new Font(fontFile, fontSize, TTF_STYLE_NORMAL); |
238 |
|||
239 |
// Set npc font |
||
240 |
✓✗✓✗ |
508 |
const int npcFontSize = config.getIntValue("npcfontSize"); |
241 |
✓✗✓✗ ✓✗ |
1016 |
fontFile = config.getValue("npcFont", ""); |
242 |
✗✓ | 127 |
if (isJapan) |
243 |
{ |
||
244 |
fontFile = config.getValue("japanFont", ""); |
||
245 |
if (fontFile.empty()) |
||
246 |
fontFile = branding.getStringValue("japanFont"); |
||
247 |
} |
||
248 |
✗✓ | 127 |
else if (isChinese) |
249 |
{ |
||
250 |
fontFile = config.getValue("chinaFont", ""); |
||
251 |
if (fontFile.empty()) |
||
252 |
fontFile = branding.getStringValue("chinaFont"); |
||
253 |
} |
||
254 |
✓✗ | 127 |
if (fontFile.empty()) |
255 |
✓✗✓✗ |
635 |
fontFile = branding.getStringValue("npcFont"); |
256 |
|||
257 |
✓✗✓✗ |
254 |
mNpcFont = new Font(fontFile, npcFontSize, TTF_STYLE_NORMAL); |
258 |
|||
259 |
✓✗ | 127 |
Widget::setGlobalFont(mGuiFont); |
260 |
|||
261 |
// Initialize mouse cursor and listen for changes to the option |
||
262 |
✓✗✓✗ ✓✗ |
508 |
setUseCustomCursor(config.getBoolValue("customcursor")); |
263 |
✓✗✓✗ |
635 |
setDoubleClick(config.getBoolValue("doubleClick")); |
264 |
✓✗✓✗ |
508 |
config.addListener("customcursor", mConfigListener); |
265 |
✓✗✓✗ |
508 |
config.addListener("doubleClick", mConfigListener); |
266 |
127 |
} |
|
267 |
|||
268 |
635 |
Gui::~Gui() |
|
269 |
{ |
||
270 |
127 |
config.removeListeners(mConfigListener); |
|
271 |
✓✗ | 254 |
delete2(mConfigListener) |
272 |
|||
273 |
✓✗ | 127 |
if (mMouseCursors != nullptr) |
274 |
{ |
||
275 |
127 |
mMouseCursors->decRef(); |
|
276 |
127 |
mMouseCursors = nullptr; |
|
277 |
} |
||
278 |
|||
279 |
✓✗ | 127 |
if (windowContainer != nullptr) |
280 |
127 |
windowContainer->slowLogic(); |
|
281 |
127 |
Widget *top = mTop; |
|
282 |
✓✗ | 127 |
if (Widget::widgetExists(mTop)) |
283 |
setTop(nullptr); |
||
284 |
127 |
Window::setWindowContainer(nullptr); |
|
285 |
✓✗ | 127 |
delete top; |
286 |
|||
287 |
✓✗ | 127 |
delete2(mGuiFont) |
288 |
✓✗ | 127 |
delete2(boldFont) |
289 |
✓✗ | 127 |
delete2(mHelpFont) |
290 |
✓✗ | 127 |
delete2(mSecureFont) |
291 |
✓✗ | 127 |
delete2(mInfoParticleFont) |
292 |
✓✗ | 127 |
delete2(mNpcFont) |
293 |
✓✗ | 254 |
delete2(guiInput) |
294 |
✓✓ | 127 |
delete2(theme) |
295 |
|||
296 |
✓✗ | 127 |
delete2(mFocusHandler) |
297 |
127 |
Label::finalCleanup(); |
|
298 |
127 |
Tab::finalCleanup(); |
|
299 |
Widget::cleanGlobalFont(); |
||
300 |
127 |
} |
|
301 |
|||
302 |
void Gui::logic() |
||
303 |
{ |
||
304 |
BLOCK_START("Gui::logic") |
||
305 |
ResourceManager::clearScheduled(); |
||
306 |
|||
307 |
if (mTop == nullptr) |
||
308 |
{ |
||
309 |
BLOCK_END("Gui::logic") |
||
310 |
return; |
||
311 |
} |
||
312 |
|||
313 |
handleModalFocus(); |
||
314 |
handleModalMouseInputFocus(); |
||
315 |
|||
316 |
if (guiInput != nullptr) |
||
317 |
handleMouseInput(); |
||
318 |
|||
319 |
mTop->logic(); |
||
320 |
BLOCK_END("Gui::logic") |
||
321 |
} |
||
322 |
|||
323 |
void Gui::slowLogic() |
||
324 |
{ |
||
325 |
BLOCK_START("Gui::slowLogic") |
||
326 |
Palette::advanceGradients(); |
||
327 |
|||
328 |
// Fade out mouse cursor after extended inactivity |
||
329 |
if (mMouseInactivityTimer < 100 * 15) |
||
330 |
{ |
||
331 |
++mMouseInactivityTimer; |
||
332 |
mMouseCursorAlpha = std::min(1.0F, mMouseCursorAlpha + 0.05F); |
||
333 |
} |
||
334 |
else |
||
335 |
{ |
||
336 |
mMouseCursorAlpha = std::max(0.0F, mMouseCursorAlpha - 0.005F); |
||
337 |
} |
||
338 |
if (mGuiFont != nullptr) |
||
339 |
mGuiFont->slowLogic(0); |
||
340 |
if (mInfoParticleFont != nullptr) |
||
341 |
mInfoParticleFont->slowLogic(1); |
||
342 |
if (mHelpFont != nullptr) |
||
343 |
mHelpFont->slowLogic(2); |
||
344 |
if (mSecureFont != nullptr) |
||
345 |
mSecureFont->slowLogic(3); |
||
346 |
if (boldFont != nullptr) |
||
347 |
boldFont->slowLogic(4); |
||
348 |
if (mNpcFont != nullptr) |
||
349 |
mNpcFont->slowLogic(5); |
||
350 |
if (windowContainer != nullptr) |
||
351 |
windowContainer->slowLogic(); |
||
352 |
|||
353 |
const time_t time = cur_time; |
||
354 |
if (mTime != time) |
||
355 |
{ |
||
356 |
logger->flush(); |
||
357 |
if (ipc != nullptr) |
||
358 |
ipc->flush(); |
||
359 |
mTime = time; |
||
360 |
|||
361 |
if (time > mTime10 || mTime10 - time > 10) |
||
362 |
{ |
||
363 |
mTime10 = time + 10; |
||
364 |
ResourceManager::cleanOrphans(false); |
||
365 |
guiInput->simulateMouseMove(); |
||
366 |
} |
||
367 |
} |
||
368 |
|||
369 |
BLOCK_END("Gui::slowLogic") |
||
370 |
} |
||
371 |
|||
372 |
void Gui::clearFonts() |
||
373 |
{ |
||
374 |
if (mGuiFont != nullptr) |
||
375 |
mGuiFont->clear(); |
||
376 |
if (mInfoParticleFont != nullptr) |
||
377 |
mInfoParticleFont->clear(); |
||
378 |
if (mHelpFont != nullptr) |
||
379 |
mHelpFont->clear(); |
||
380 |
if (mSecureFont != nullptr) |
||
381 |
mSecureFont->clear(); |
||
382 |
if (boldFont != nullptr) |
||
383 |
boldFont->clear(); |
||
384 |
if (mNpcFont != nullptr) |
||
385 |
mNpcFont->clear(); |
||
386 |
} |
||
387 |
|||
388 |
bool Gui::handleInput() |
||
389 |
{ |
||
390 |
if (mInput != nullptr) |
||
391 |
return handleKeyInput(); |
||
392 |
return false; |
||
393 |
} |
||
394 |
|||
395 |
bool Gui::handleKeyInput() |
||
396 |
{ |
||
397 |
if (guiInput == nullptr) |
||
398 |
return false; |
||
399 |
|||
400 |
BLOCK_START("Gui::handleKeyInput") |
||
401 |
bool consumed(false); |
||
402 |
|||
403 |
while (!mInput->isKeyQueueEmpty()) |
||
404 |
{ |
||
405 |
const KeyInput keyInput = guiInput->dequeueKeyInput(); |
||
406 |
|||
407 |
KeyEvent eventToGlobalKeyListeners(nullptr, |
||
408 |
keyInput.getType(), |
||
409 |
keyInput.getActionId(), keyInput.getKey()); |
||
410 |
|||
411 |
#ifdef USE_SDL2 |
||
412 |
if (!keyInput.getText().empty()) |
||
413 |
eventToGlobalKeyListeners.setText(keyInput.getText()); |
||
414 |
#endif // USE_SDL2 |
||
415 |
|||
416 |
distributeKeyEventToGlobalKeyListeners( |
||
417 |
eventToGlobalKeyListeners); |
||
418 |
|||
419 |
// If a global key listener consumes the event it will not be |
||
420 |
// sent further to the source of the event. |
||
421 |
if (eventToGlobalKeyListeners.isConsumed()) |
||
422 |
{ |
||
423 |
consumed = true; |
||
424 |
continue; |
||
425 |
} |
||
426 |
|||
427 |
if (mFocusHandler != nullptr) |
||
428 |
{ |
||
429 |
bool eventConsumed = false; |
||
430 |
|||
431 |
// Send key inputs to the focused widgets |
||
432 |
if (mFocusHandler->getFocused() != nullptr) |
||
433 |
{ |
||
434 |
KeyEvent event(getKeyEventSource(), |
||
435 |
keyInput.getType(), |
||
436 |
keyInput.getActionId(), keyInput.getKey()); |
||
437 |
#ifdef USE_SDL2 |
||
438 |
if (!keyInput.getText().empty()) |
||
439 |
event.setText(keyInput.getText()); |
||
440 |
#endif // USE_SDL2 |
||
441 |
|||
442 |
if (!mFocusHandler->getFocused()->isFocusable()) |
||
443 |
mFocusHandler->focusNone(); |
||
444 |
else |
||
445 |
distributeKeyEvent(event); |
||
446 |
|||
447 |
eventConsumed = event.isConsumed(); |
||
448 |
if (eventConsumed) |
||
449 |
consumed = true; |
||
450 |
} |
||
451 |
|||
452 |
// If the key event hasn't been consumed and |
||
453 |
// tabbing is enable check for tab press and |
||
454 |
// change focus. |
||
455 |
if (!eventConsumed && keyInput.getActionId() |
||
456 |
== InputAction::GUI_TAB && |
||
457 |
keyInput.getType() == KeyEventType::PRESSED) |
||
458 |
{ |
||
459 |
if (inputManager.isActionActive(InputAction::GUI_MOD)) |
||
460 |
mFocusHandler->tabPrevious(); |
||
461 |
else |
||
462 |
mFocusHandler->tabNext(); |
||
463 |
} |
||
464 |
} |
||
465 |
} // end while |
||
466 |
BLOCK_END("Gui::handleKeyInput") |
||
467 |
return consumed; |
||
468 |
} |
||
469 |
|||
470 |
67 |
void Gui::draw() |
|
471 |
{ |
||
472 |
BLOCK_START("Gui::draw 1") |
||
473 |
67 |
Widget *const top = getTop(); |
|
474 |
✓✗ | 67 |
if (top == nullptr) |
475 |
return; |
||
476 |
134 |
mGraphics->pushClipArea(top->getDimension()); |
|
477 |
|||
478 |
✓✗ | 67 |
if (isBatchDrawRenders(openGLMode)) |
479 |
{ |
||
480 |
67 |
top->draw(mGraphics); |
|
481 |
67 |
touchManager.draw(); |
|
482 |
} |
||
483 |
else |
||
484 |
{ |
||
485 |
top->safeDraw(mGraphics); |
||
486 |
touchManager.safeDraw(); |
||
487 |
} |
||
488 |
|||
489 |
int mouseX; |
||
490 |
int mouseY; |
||
491 |
67 |
const MouseStateType button = getMouseState(mouseX, mouseY); |
|
492 |
|||
493 |
✗✓✗✗ |
67 |
if ((settings.mouseFocused || |
494 |
✓✗ | 67 |
((button & SDL_BUTTON(1)) != 0)) && |
495 |
✓✗ | 134 |
mMouseCursors != nullptr && |
496 |
✓✗ | 134 |
mCustomCursor && |
497 |
67 |
mMouseCursorAlpha > 0.0F) |
|
498 |
{ |
||
499 |
#ifndef DYECMD |
||
500 |
67 |
const Image *const image = dragDrop.getItemImage(); |
|
501 |
✓✗ | 67 |
if (mGuiFont != nullptr) |
502 |
{ |
||
503 |
67 |
const std::string &str = dragDrop.getText(); |
|
504 |
✗✓ | 67 |
if (!str.empty()) |
505 |
{ |
||
506 |
const int posX = mouseX - mGuiFont->getWidth(str) / 2; |
||
507 |
const int posY = mouseY + |
||
508 |
(image != nullptr ? image->mBounds.h / 2 : 0); |
||
509 |
mGuiFont->drawString(mGraphics, |
||
510 |
mForegroundColor, mForegroundColor2, |
||
511 |
str, |
||
512 |
posX, posY); |
||
513 |
} |
||
514 |
} |
||
515 |
✗✓ | 67 |
if (image != nullptr) |
516 |
{ |
||
517 |
const int posX = mouseX - (image->mBounds.w / 2); |
||
518 |
const int posY = mouseY - (image->mBounds.h / 2); |
||
519 |
mGraphics->drawImage(image, posX, posY); |
||
520 |
} |
||
521 |
#endif // DYECMD |
||
522 |
|||
523 |
67 |
Image *const mouseCursor = mMouseCursors->get( |
|
524 |
134 |
CAST_SIZE(mCursorType)); |
|
525 |
✓✗ | 67 |
if (mouseCursor != nullptr) |
526 |
{ |
||
527 |
67 |
mouseCursor->setAlpha(mMouseCursorAlpha); |
|
528 |
67 |
mGraphics->drawImage(mouseCursor, mouseX - 15, mouseY - 17); |
|
529 |
} |
||
530 |
} |
||
531 |
|||
532 |
67 |
mGraphics->popClipArea(); |
|
533 |
BLOCK_END("Gui::draw 1") |
||
534 |
} |
||
535 |
|||
536 |
void Gui::videoResized() const |
||
537 |
{ |
||
538 |
WindowContainer *const top = static_cast<WindowContainer *>(getTop()); |
||
539 |
|||
540 |
if (top != nullptr) |
||
541 |
{ |
||
542 |
const int oldWidth = top->getWidth(); |
||
543 |
const int oldHeight = top->getHeight(); |
||
544 |
|||
545 |
top->setSize(mainGraphics->mWidth, mainGraphics->mHeight); |
||
546 |
top->adjustAfterResize(oldWidth, oldHeight); |
||
547 |
} |
||
548 |
|||
549 |
if (viewport != nullptr) |
||
550 |
viewport->videoResized(); |
||
551 |
Widget::distributeWindowResizeEvent(); |
||
552 |
} |
||
553 |
|||
554 |
127 |
void Gui::setUseCustomCursor(const bool customCursor) |
|
555 |
{ |
||
556 |
✗✓ | 127 |
if (settings.options.hideCursor) |
557 |
{ |
||
558 |
SDL::showCursor(false); |
||
559 |
return; |
||
560 |
} |
||
561 |
✓✗ | 127 |
if (customCursor != mCustomCursor) |
562 |
{ |
||
563 |
127 |
mCustomCursor = customCursor; |
|
564 |
|||
565 |
✓✗ | 127 |
if (mCustomCursor) |
566 |
{ |
||
567 |
// Hide the SDL mouse cursor |
||
568 |
127 |
SDL::showCursor(false); |
|
569 |
|||
570 |
// Load the mouse cursor |
||
571 |
✗✓ | 127 |
if (mMouseCursors != nullptr) |
572 |
mMouseCursors->decRef(); |
||
573 |
✓✗ | 508 |
mMouseCursors = Theme::getImageSetFromTheme("mouse.png", 40, 40); |
574 |
|||
575 |
✗✓ | 127 |
if (mMouseCursors == nullptr) |
576 |
logger->log("Error: Unable to load mouse cursors."); |
||
577 |
} |
||
578 |
else |
||
579 |
{ |
||
580 |
// Show the SDL mouse cursor |
||
581 |
SDL::showCursor(true); |
||
582 |
|||
583 |
// Unload the mouse cursor |
||
584 |
if (mMouseCursors != nullptr) |
||
585 |
{ |
||
586 |
mMouseCursors->decRef(); |
||
587 |
mMouseCursors = nullptr; |
||
588 |
} |
||
589 |
} |
||
590 |
} |
||
591 |
} |
||
592 |
|||
593 |
void Gui::handleMouseMoved(const MouseInput &mouseInput) |
||
594 |
{ |
||
595 |
// Check if the mouse leaves the application window. |
||
596 |
if (mTop != nullptr && |
||
597 |
!mWidgetWithMouseQueue.empty() && |
||
598 |
(mouseInput.getX() < 0 || |
||
599 |
mouseInput.getY() < 0 || |
||
600 |
!mTop->getDimension().isPointInRect(mouseInput.getX(), |
||
601 |
mouseInput.getY()))) |
||
602 |
{ |
||
603 |
// Distribute an event to all widgets in the |
||
604 |
// "widget with mouse" queue. |
||
605 |
while (!mWidgetWithMouseQueue.empty()) |
||
606 |
{ |
||
607 |
Widget *const widget = mWidgetWithMouseQueue.front(); |
||
608 |
|||
609 |
if (Widget::widgetExists(widget)) |
||
610 |
{ |
||
611 |
distributeMouseEvent(widget, |
||
612 |
MouseEventType::EXITED, |
||
613 |
mouseInput.getButton(), |
||
614 |
mouseInput.getX(), |
||
615 |
mouseInput.getY(), |
||
616 |
true, |
||
617 |
true); |
||
618 |
} |
||
619 |
|||
620 |
mWidgetWithMouseQueue.pop_front(); |
||
621 |
} |
||
622 |
|||
623 |
mMouseInactivityTimer = 0; |
||
624 |
return; |
||
625 |
} |
||
626 |
|||
627 |
const int mouseX = mouseInput.getX(); |
||
628 |
const int mouseY = mouseInput.getY(); |
||
629 |
const MouseButtonT button = mouseInput.getButton(); |
||
630 |
|||
631 |
// Check if there is a need to send mouse exited events by |
||
632 |
// traversing the "widget with mouse" queue. |
||
633 |
bool widgetWithMouseQueueCheckDone = mWidgetWithMouseQueue.empty(); |
||
634 |
while (!widgetWithMouseQueueCheckDone) |
||
635 |
{ |
||
636 |
unsigned int iterations = 0; |
||
637 |
for (std::deque<Widget*>::iterator |
||
638 |
iter = mWidgetWithMouseQueue.begin(); |
||
639 |
iter != mWidgetWithMouseQueue.end(); |
||
640 |
++ iter) |
||
641 |
{ |
||
642 |
Widget *const widget = *iter; |
||
643 |
|||
644 |
// If a widget in the "widget with mouse queue" doesn't |
||
645 |
// exists anymore it should be removed from the queue. |
||
646 |
if (!Widget::widgetExists(widget)) |
||
647 |
{ |
||
648 |
mWidgetWithMouseQueue.erase(iter); |
||
649 |
break; |
||
650 |
} |
||
651 |
else |
||
652 |
{ |
||
653 |
int x; |
||
654 |
int y; |
||
655 |
widget->getAbsolutePosition(x, y); |
||
656 |
|||
657 |
if (x > mouseX |
||
658 |
|| y > mouseY |
||
659 |
|| x + widget->getWidth() <= mouseX |
||
660 |
|| y + widget->getHeight() <= mouseY |
||
661 |
|| !widget->isVisible()) |
||
662 |
{ |
||
663 |
distributeMouseEvent(widget, |
||
664 |
MouseEventType::EXITED, |
||
665 |
button, |
||
666 |
mouseX, |
||
667 |
mouseY, |
||
668 |
true, |
||
669 |
true); |
||
670 |
mClickCount = 1; |
||
671 |
mLastMousePressTimeStamp = 0U; |
||
672 |
mWidgetWithMouseQueue.erase(iter); |
||
673 |
break; |
||
674 |
} |
||
675 |
} |
||
676 |
|||
677 |
iterations++; |
||
678 |
} |
||
679 |
|||
680 |
widgetWithMouseQueueCheckDone = |
||
681 |
(CAST_SIZE(iterations) == mWidgetWithMouseQueue.size()); |
||
682 |
} |
||
683 |
|||
684 |
// Check all widgets below the mouse to see if they are |
||
685 |
// present in the "widget with mouse" queue. If a widget |
||
686 |
// is not then it should be added and an entered event should |
||
687 |
// be sent to it. |
||
688 |
Widget* parent = getMouseEventSource(mouseX, mouseY); |
||
689 |
Widget* widget = parent; |
||
690 |
|||
691 |
// If a widget has modal mouse input focus then it will |
||
692 |
// always be returned from getMouseEventSource, but we only wan't to |
||
693 |
// send mouse entered events if the mouse has actually entered the |
||
694 |
// widget with modal mouse input focus, hence we need to check if |
||
695 |
// that's the case. If it's not we should simply ignore to send any |
||
696 |
// mouse entered events. |
||
697 |
if ((mFocusHandler->getModalMouseInputFocused() != nullptr) |
||
698 |
&& widget == mFocusHandler->getModalMouseInputFocused() |
||
699 |
&& Widget::widgetExists(widget) && |
||
700 |
(widget != nullptr)) |
||
701 |
{ |
||
702 |
int x; |
||
703 |
int y; |
||
704 |
widget->getAbsolutePosition(x, y); |
||
705 |
|||
706 |
if (x > mouseX || y > mouseY |
||
707 |
|| x + widget->getWidth() <= mouseX |
||
708 |
|| y + widget->getHeight() <= mouseY) |
||
709 |
{ |
||
710 |
parent = nullptr; |
||
711 |
} |
||
712 |
} |
||
713 |
|||
714 |
while (parent != nullptr) |
||
715 |
{ |
||
716 |
parent = widget->getParent(); |
||
717 |
|||
718 |
// Check if the widget is present in the "widget with mouse" queue. |
||
719 |
bool widgetIsPresentInQueue = false; |
||
720 |
FOR_EACH (std::deque<Widget*>::const_iterator, |
||
721 |
iter, mWidgetWithMouseQueue) |
||
722 |
{ |
||
723 |
if (*iter == widget) |
||
724 |
{ |
||
725 |
widgetIsPresentInQueue = true; |
||
726 |
break; |
||
727 |
} |
||
728 |
} |
||
729 |
|||
730 |
// Widget is not present, send an entered event and add |
||
731 |
// it to the "widget with mouse" queue. |
||
732 |
if (!widgetIsPresentInQueue |
||
733 |
&& Widget::widgetExists(widget)) |
||
734 |
{ |
||
735 |
distributeMouseEvent(widget, |
||
736 |
MouseEventType::ENTERED, |
||
737 |
button, |
||
738 |
mouseX, |
||
739 |
mouseY, |
||
740 |
true, |
||
741 |
true); |
||
742 |
mWidgetWithMouseQueue.push_front(widget); |
||
743 |
} |
||
744 |
|||
745 |
const Widget *const swap = widget; |
||
746 |
widget = parent; |
||
747 |
parent = swap->getParent(); |
||
748 |
} |
||
749 |
|||
750 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
751 |
{ |
||
752 |
distributeMouseEvent(mFocusHandler->getDraggedWidget(), |
||
753 |
MouseEventType::DRAGGED, |
||
754 |
mLastMouseDragButton, |
||
755 |
mouseX, |
||
756 |
mouseY, |
||
757 |
false, |
||
758 |
false); |
||
759 |
} |
||
760 |
else |
||
761 |
{ |
||
762 |
Widget *const sourceWidget = getMouseEventSource(mouseX, mouseY); |
||
763 |
distributeMouseEvent(sourceWidget, |
||
764 |
MouseEventType::MOVED, |
||
765 |
button, |
||
766 |
mouseX, |
||
767 |
mouseY, |
||
768 |
false, |
||
769 |
false); |
||
770 |
} |
||
771 |
mMouseInactivityTimer = 0; |
||
772 |
} |
||
773 |
|||
774 |
void Gui::handleMousePressed(const MouseInput &mouseInput) |
||
775 |
{ |
||
776 |
const int x = mouseInput.getX(); |
||
777 |
const int y = mouseInput.getY(); |
||
778 |
const MouseButtonT button = mouseInput.getButton(); |
||
779 |
const unsigned int timeStamp = mouseInput.getTimeStamp(); |
||
780 |
|||
781 |
Widget *sourceWidget = getMouseEventSource(x, y); |
||
782 |
|||
783 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
784 |
sourceWidget = mFocusHandler->getDraggedWidget(); |
||
785 |
|||
786 |
if (sourceWidget == nullptr) |
||
787 |
return; |
||
788 |
int sourceWidgetX; |
||
789 |
int sourceWidgetY; |
||
790 |
sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); |
||
791 |
|||
792 |
if (((mFocusHandler->getModalFocused() != nullptr) |
||
793 |
&& sourceWidget->isModalFocused()) |
||
794 |
|| (mFocusHandler->getModalFocused() == nullptr)) |
||
795 |
{ |
||
796 |
sourceWidget->requestFocus(); |
||
797 |
} |
||
798 |
|||
799 |
if (mDoubleClick && |
||
800 |
timeStamp - mLastMousePressTimeStamp < 250U && |
||
801 |
mLastMousePressButton == button) |
||
802 |
{ |
||
803 |
mClickCount ++; |
||
804 |
} |
||
805 |
else |
||
806 |
{ |
||
807 |
mClickCount = 1; |
||
808 |
} |
||
809 |
|||
810 |
distributeMouseEvent(sourceWidget, |
||
811 |
MouseEventType::PRESSED, |
||
812 |
button, |
||
813 |
x, |
||
814 |
y, |
||
815 |
false, |
||
816 |
false); |
||
817 |
mFocusHandler->setLastWidgetPressed(sourceWidget); |
||
818 |
mFocusHandler->setDraggedWidget(sourceWidget); |
||
819 |
mLastMouseDragButton = button; |
||
820 |
mLastMousePressButton = button; |
||
821 |
mLastMousePressTimeStamp = timeStamp; |
||
822 |
} |
||
823 |
|||
824 |
void Gui::updateFonts() |
||
825 |
{ |
||
826 |
const int fontSize = config.getIntValue("fontSize"); |
||
827 |
std::string fontFile = config.getValue("font", ""); |
||
828 |
if (fontFile.empty()) |
||
829 |
fontFile = branding.getStringValue("font"); |
||
830 |
|||
831 |
mGuiFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL); |
||
832 |
|||
833 |
fontFile = config.getValue("particleFont", ""); |
||
834 |
if (fontFile.empty()) |
||
835 |
fontFile = branding.getStringValue("particleFont"); |
||
836 |
|||
837 |
mInfoParticleFont->loadFont(fontFile, fontSize, TTF_STYLE_BOLD); |
||
838 |
|||
839 |
fontFile = config.getValue("boldFont", ""); |
||
840 |
if (fontFile.empty()) |
||
841 |
fontFile = branding.getStringValue("boldFont"); |
||
842 |
|||
843 |
boldFont->loadFont(fontFile, fontSize, TTF_STYLE_NORMAL); |
||
844 |
|||
845 |
const int npcFontSize = config.getIntValue("npcfontSize"); |
||
846 |
|||
847 |
fontFile = config.getValue("npcFont", ""); |
||
848 |
if (fontFile.empty()) |
||
849 |
fontFile = branding.getStringValue("npcFont"); |
||
850 |
|||
851 |
mNpcFont->loadFont(fontFile, npcFontSize, TTF_STYLE_NORMAL); |
||
852 |
} |
||
853 |
|||
854 |
void Gui::distributeMouseEvent(Widget *const source, |
||
855 |
const MouseEventTypeT type, |
||
856 |
const MouseButtonT button, |
||
857 |
const int x, const int y, |
||
858 |
const bool force, |
||
859 |
const bool toSourceOnly) |
||
860 |
{ |
||
861 |
if ((source == nullptr) || (mFocusHandler == nullptr)) |
||
862 |
return; |
||
863 |
|||
864 |
Widget *widget = source; |
||
865 |
|||
866 |
if (!force) |
||
867 |
{ |
||
868 |
if (mFocusHandler->getModalFocused() != nullptr |
||
869 |
&& !widget->isModalFocused()) |
||
870 |
{ |
||
871 |
return; |
||
872 |
} |
||
873 |
if (mFocusHandler->getModalMouseInputFocused() != nullptr |
||
874 |
&& !widget->isModalMouseInputFocused()) |
||
875 |
{ |
||
876 |
return; |
||
877 |
} |
||
878 |
} |
||
879 |
|||
880 |
MouseEvent event(source, |
||
881 |
type, button, |
||
882 |
x, y, mClickCount); |
||
883 |
|||
884 |
Widget* parent = source; |
||
885 |
while (parent != nullptr) |
||
886 |
{ |
||
887 |
// If the widget has been removed due to input |
||
888 |
// cancel the distribution. |
||
889 |
if (!Widget::widgetExists(widget)) |
||
890 |
break; |
||
891 |
|||
892 |
parent = widget->getParent(); |
||
893 |
|||
894 |
if (widget->isEnabled() || force) |
||
895 |
{ |
||
896 |
int widgetX; |
||
897 |
int widgetY; |
||
898 |
widget->getAbsolutePosition(widgetX, widgetY); |
||
899 |
|||
900 |
event.setX(x - widgetX); |
||
901 |
event.setY(y - widgetY); |
||
902 |
|||
903 |
std::list<MouseListener*> mouseListeners |
||
904 |
= widget->getMouseListeners(); |
||
905 |
|||
906 |
const MouseEventTypeT mouseType = event.getType(); |
||
907 |
// Send the event to all mouse listeners of the widget. |
||
908 |
FOR_EACH (std::list<MouseListener*>::const_iterator, |
||
909 |
it, mouseListeners) |
||
910 |
{ |
||
911 |
switch (mouseType) |
||
912 |
{ |
||
913 |
case MouseEventType::ENTERED: |
||
914 |
(*it)->mouseEntered(event); |
||
915 |
break; |
||
916 |
case MouseEventType::EXITED: |
||
917 |
(*it)->mouseExited(event); |
||
918 |
break; |
||
919 |
case MouseEventType::MOVED: |
||
920 |
(*it)->mouseMoved(event); |
||
921 |
break; |
||
922 |
case MouseEventType::PRESSED: |
||
923 |
(*it)->mousePressed(event); |
||
924 |
break; |
||
925 |
case MouseEventType::RELEASED: |
||
926 |
case MouseEventType::RELEASED2: |
||
927 |
(*it)->mouseReleased(event); |
||
928 |
break; |
||
929 |
case MouseEventType::WHEEL_MOVED_UP: |
||
930 |
(*it)->mouseWheelMovedUp(event); |
||
931 |
break; |
||
932 |
case MouseEventType::WHEEL_MOVED_DOWN: |
||
933 |
(*it)->mouseWheelMovedDown(event); |
||
934 |
break; |
||
935 |
case MouseEventType::DRAGGED: |
||
936 |
(*it)->mouseDragged(event); |
||
937 |
break; |
||
938 |
case MouseEventType::CLICKED: |
||
939 |
(*it)->mouseClicked(event); |
||
940 |
break; |
||
941 |
default: |
||
942 |
break; |
||
943 |
} |
||
944 |
} |
||
945 |
|||
946 |
if (toSourceOnly) |
||
947 |
break; |
||
948 |
} |
||
949 |
|||
950 |
const Widget *const swap = widget; |
||
951 |
widget = parent; |
||
952 |
parent = swap->getParent(); |
||
953 |
|||
954 |
#ifndef DYECMD |
||
955 |
if (type == MouseEventType::RELEASED) |
||
956 |
dragDrop.clear(); |
||
957 |
#endif // DYECMD |
||
958 |
|||
959 |
if ((widget == nullptr) || event.isConsumed()) |
||
960 |
break; |
||
961 |
|||
962 |
// If a non modal focused widget has been reach |
||
963 |
// and we have modal focus cancel the distribution. |
||
964 |
if ((mFocusHandler->getModalFocused() != nullptr) |
||
965 |
&& !widget->isModalFocused()) |
||
966 |
{ |
||
967 |
break; |
||
968 |
} |
||
969 |
|||
970 |
// If a non modal mouse input focused widget has been reach |
||
971 |
// and we have modal mouse input focus cancel the distribution. |
||
972 |
if ((mFocusHandler->getModalMouseInputFocused() != nullptr) |
||
973 |
&& !widget->isModalMouseInputFocused()) |
||
974 |
{ |
||
975 |
break; |
||
976 |
} |
||
977 |
} |
||
978 |
} |
||
979 |
|||
980 |
void Gui::resetClickCount() |
||
981 |
{ |
||
982 |
mClickCount = 1; |
||
983 |
mLastMousePressTimeStamp = 0; |
||
984 |
} |
||
985 |
|||
986 |
35 |
MouseEvent *Gui::createMouseEvent(Window *const widget) |
|
987 |
{ |
||
988 |
✗✓✗✗ |
35 |
if ((viewport == nullptr) || (widget == nullptr)) |
989 |
return nullptr; |
||
990 |
|||
991 |
int x = 0; |
||
992 |
int y = 0; |
||
993 |
int mouseX = 0; |
||
994 |
int mouseY = 0; |
||
995 |
|||
996 |
getAbsolutePosition(widget, x, y); |
||
997 |
getMouseState(mouseX, mouseY); |
||
998 |
|||
999 |
return new MouseEvent(widget, |
||
1000 |
MouseEventType::MOVED, |
||
1001 |
MouseButton::EMPTY, |
||
1002 |
mouseX - x, |
||
1003 |
mouseY - y, |
||
1004 |
mClickCount); |
||
1005 |
} |
||
1006 |
|||
1007 |
void Gui::getAbsolutePosition(Widget *restrict widget, |
||
1008 |
int &restrict x, |
||
1009 |
int &restrict y) |
||
1010 |
{ |
||
1011 |
if (widget == nullptr) |
||
1012 |
return; |
||
1013 |
x = 0; |
||
1014 |
y = 0; |
||
1015 |
while (widget->getParent() != nullptr) |
||
1016 |
{ |
||
1017 |
x += widget->getX(); |
||
1018 |
y += widget->getY(); |
||
1019 |
widget = widget->getParent(); |
||
1020 |
} |
||
1021 |
} |
||
1022 |
|||
1023 |
void Gui::handleMouseInput() |
||
1024 |
{ |
||
1025 |
BLOCK_START("Gui::handleMouseInput") |
||
1026 |
while (!mInput->isMouseQueueEmpty()) |
||
1027 |
{ |
||
1028 |
const MouseInput mouseInput = guiInput->dequeueMouseInput(); |
||
1029 |
|||
1030 |
if (touchManager.processEvent(mouseInput)) |
||
1031 |
{ |
||
1032 |
#ifdef ANDROID |
||
1033 |
#ifndef USE_SDL2 |
||
1034 |
SDL_WarpMouse(mLastMouseX, mLastMouseY, |
||
1035 |
mLastMouseRealX, mLastMouseRealY); |
||
1036 |
#endif // USE_SDL2 |
||
1037 |
#endif // ANDROID |
||
1038 |
|||
1039 |
mMouseInactivityTimer = 0; |
||
1040 |
continue; |
||
1041 |
} |
||
1042 |
|||
1043 |
// Save the current mouse state. It will be needed if modal focus |
||
1044 |
// changes or modal mouse input focus changes. |
||
1045 |
mLastMouseX = mouseInput.getX(); |
||
1046 |
mLastMouseY = mouseInput.getY(); |
||
1047 |
#ifdef ANDROID |
||
1048 |
mLastMouseRealX = mouseInput.getRealX(); |
||
1049 |
mLastMouseRealY = mouseInput.getRealY(); |
||
1050 |
#endif // ANDROID |
||
1051 |
|||
1052 |
switch (mouseInput.getType()) |
||
1053 |
{ |
||
1054 |
case MouseEventType::PRESSED: |
||
1055 |
handleMousePressed(mouseInput); |
||
1056 |
break; |
||
1057 |
case MouseEventType::RELEASED: |
||
1058 |
handleMouseReleased(mouseInput); |
||
1059 |
break; |
||
1060 |
case MouseEventType::MOVED: |
||
1061 |
handleMouseMoved(mouseInput); |
||
1062 |
break; |
||
1063 |
case MouseEventType::WHEEL_MOVED_DOWN: |
||
1064 |
handleMouseWheelMovedDown(mouseInput); |
||
1065 |
break; |
||
1066 |
case MouseEventType::WHEEL_MOVED_UP: |
||
1067 |
handleMouseWheelMovedUp(mouseInput); |
||
1068 |
break; |
||
1069 |
case MouseEventType::CLICKED: |
||
1070 |
case MouseEventType::ENTERED: |
||
1071 |
case MouseEventType::EXITED: |
||
1072 |
case MouseEventType::DRAGGED: |
||
1073 |
case MouseEventType::RELEASED2: |
||
1074 |
default: |
||
1075 |
break; |
||
1076 |
} |
||
1077 |
} |
||
1078 |
BLOCK_END("Gui::handleMouseInput") |
||
1079 |
} |
||
1080 |
|||
1081 |
void Gui::handleMouseReleased(const MouseInput &mouseInput) |
||
1082 |
{ |
||
1083 |
Widget *sourceWidget = getMouseEventSource( |
||
1084 |
mouseInput.getX(), mouseInput.getY()); |
||
1085 |
|||
1086 |
int sourceWidgetX; |
||
1087 |
int sourceWidgetY; |
||
1088 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
1089 |
{ |
||
1090 |
if (sourceWidget != mFocusHandler->getLastWidgetPressed()) |
||
1091 |
mFocusHandler->setLastWidgetPressed(nullptr); |
||
1092 |
|||
1093 |
Widget *const oldWidget = sourceWidget; |
||
1094 |
sourceWidget = mFocusHandler->getDraggedWidget(); |
||
1095 |
if ((oldWidget != nullptr) && oldWidget != sourceWidget) |
||
1096 |
{ |
||
1097 |
oldWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); |
||
1098 |
distributeMouseEvent(oldWidget, |
||
1099 |
MouseEventType::RELEASED2, |
||
1100 |
mouseInput.getButton(), |
||
1101 |
mouseInput.getX(), |
||
1102 |
mouseInput.getY(), |
||
1103 |
false, |
||
1104 |
false); |
||
1105 |
} |
||
1106 |
} |
||
1107 |
|||
1108 |
if (sourceWidget == nullptr) |
||
1109 |
return; |
||
1110 |
sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); |
||
1111 |
distributeMouseEvent(sourceWidget, |
||
1112 |
MouseEventType::RELEASED, |
||
1113 |
mouseInput.getButton(), |
||
1114 |
mouseInput.getX(), |
||
1115 |
mouseInput.getY(), |
||
1116 |
false, |
||
1117 |
false); |
||
1118 |
|||
1119 |
if (mouseInput.getButton() == mLastMousePressButton |
||
1120 |
&& mFocusHandler->getLastWidgetPressed() == sourceWidget) |
||
1121 |
{ |
||
1122 |
distributeMouseEvent(sourceWidget, |
||
1123 |
MouseEventType::CLICKED, |
||
1124 |
mouseInput.getButton(), |
||
1125 |
mouseInput.getX(), |
||
1126 |
mouseInput.getY(), |
||
1127 |
false, |
||
1128 |
false); |
||
1129 |
|||
1130 |
mFocusHandler->setLastWidgetPressed(nullptr); |
||
1131 |
} |
||
1132 |
else |
||
1133 |
{ |
||
1134 |
mLastMousePressButton = MouseButton::EMPTY; |
||
1135 |
mClickCount = 0; |
||
1136 |
} |
||
1137 |
|||
1138 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
1139 |
mFocusHandler->setDraggedWidget(nullptr); |
||
1140 |
} |
||
1141 |
|||
1142 |
99 |
void Gui::addGlobalFocusListener(FocusListener* focusListener) |
|
1143 |
{ |
||
1144 |
198 |
mFocusListeners.push_back(focusListener); |
|
1145 |
99 |
} |
|
1146 |
|||
1147 |
99 |
void Gui::removeGlobalFocusListener(FocusListener* focusListener) |
|
1148 |
{ |
||
1149 |
99 |
mFocusListeners.remove(focusListener); |
|
1150 |
99 |
} |
|
1151 |
|||
1152 |
24 |
void Gui::distributeGlobalFocusGainedEvent(const Event &focusEvent) |
|
1153 |
{ |
||
1154 |
✓✓ | 77 |
for (FocusListenerIterator iter = mFocusListeners.begin(); |
1155 |
58 |
iter != mFocusListeners.end(); |
|
1156 |
++ iter) |
||
1157 |
{ |
||
1158 |
5 |
(*iter)->focusGained(focusEvent); |
|
1159 |
} |
||
1160 |
24 |
} |
|
1161 |
|||
1162 |
2487 |
void Gui::removeDragged(const Widget *const widget) |
|
1163 |
{ |
||
1164 |
✓✗ | 2487 |
if (mFocusHandler == nullptr) |
1165 |
return; |
||
1166 |
|||
1167 |
✗✓ | 2487 |
if (mFocusHandler->getDraggedWidget() == widget) |
1168 |
mFocusHandler->setDraggedWidget(nullptr); |
||
1169 |
} |
||
1170 |
|||
1171 |
2 |
MouseStateType Gui::getMouseState(int &x, int &y) |
|
1172 |
{ |
||
1173 |
69 |
const MouseStateType res = SDL_GetMouseState(&x, &y); |
|
1174 |
69 |
const int scale = mainGraphics->getScale(); |
|
1175 |
69 |
x /= scale; |
|
1176 |
69 |
y /= scale; |
|
1177 |
2 |
return res; |
|
1178 |
} |
||
1179 |
|||
1180 |
127 |
void Gui::setTop(Widget *const top) |
|
1181 |
{ |
||
1182 |
✗✓✓✗ |
254 |
if (mTop != nullptr) |
1183 |
127 |
mTop->setFocusHandler(nullptr); |
|
1184 |
✓✗ | 127 |
if (top != nullptr) |
1185 |
127 |
top->setFocusHandler(mFocusHandler); |
|
1186 |
|||
1187 |
254 |
mTop = top; |
|
1188 |
127 |
} |
|
1189 |
|||
1190 |
void Gui::setGraphics(Graphics *const graphics) |
||
1191 |
{ |
||
1192 |
127 |
mGraphics = graphics; |
|
1193 |
} |
||
1194 |
|||
1195 |
Graphics* Gui::getGraphics() const |
||
1196 |
{ |
||
1197 |
return mGraphics; |
||
1198 |
} |
||
1199 |
|||
1200 |
void Gui::setInput(SDLInput *const input) |
||
1201 |
{ |
||
1202 |
127 |
mInput = input; |
|
1203 |
} |
||
1204 |
|||
1205 |
SDLInput* Gui::getInput() const |
||
1206 |
{ |
||
1207 |
return mInput; |
||
1208 |
} |
||
1209 |
|||
1210 |
void Gui::addGlobalKeyListener(KeyListener *const keyListener) |
||
1211 |
{ |
||
1212 |
mKeyListeners.push_back(keyListener); |
||
1213 |
} |
||
1214 |
|||
1215 |
void Gui::removeGlobalKeyListener(KeyListener *const keyListener) |
||
1216 |
{ |
||
1217 |
mKeyListeners.remove(keyListener); |
||
1218 |
} |
||
1219 |
|||
1220 |
void Gui::handleMouseWheelMovedDown(const MouseInput& mouseInput) |
||
1221 |
{ |
||
1222 |
Widget* sourceWidget = getMouseEventSource( |
||
1223 |
mouseInput.getX(), mouseInput.getY()); |
||
1224 |
|||
1225 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
1226 |
sourceWidget = mFocusHandler->getDraggedWidget(); |
||
1227 |
|||
1228 |
if (sourceWidget != nullptr) |
||
1229 |
{ |
||
1230 |
int sourceWidgetX = 0; |
||
1231 |
int sourceWidgetY = 0; |
||
1232 |
|||
1233 |
sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); |
||
1234 |
distributeMouseEvent(sourceWidget, |
||
1235 |
MouseEventType::WHEEL_MOVED_DOWN, |
||
1236 |
mouseInput.getButton(), |
||
1237 |
mouseInput.getX(), |
||
1238 |
mouseInput.getY(), |
||
1239 |
false, |
||
1240 |
false); |
||
1241 |
} |
||
1242 |
} |
||
1243 |
|||
1244 |
void Gui::handleMouseWheelMovedUp(const MouseInput& mouseInput) |
||
1245 |
{ |
||
1246 |
Widget* sourceWidget = getMouseEventSource( |
||
1247 |
mouseInput.getX(), mouseInput.getY()); |
||
1248 |
|||
1249 |
if (mFocusHandler->getDraggedWidget() != nullptr) |
||
1250 |
sourceWidget = mFocusHandler->getDraggedWidget(); |
||
1251 |
|||
1252 |
if (sourceWidget != nullptr) |
||
1253 |
{ |
||
1254 |
int sourceWidgetX; |
||
1255 |
int sourceWidgetY; |
||
1256 |
|||
1257 |
sourceWidget->getAbsolutePosition(sourceWidgetX, sourceWidgetY); |
||
1258 |
distributeMouseEvent(sourceWidget, |
||
1259 |
MouseEventType::WHEEL_MOVED_UP, |
||
1260 |
mouseInput.getButton(), |
||
1261 |
mouseInput.getX(), |
||
1262 |
mouseInput.getY(), |
||
1263 |
false, |
||
1264 |
false); |
||
1265 |
} |
||
1266 |
} |
||
1267 |
|||
1268 |
Widget* Gui::getWidgetAt(const int x, const int y) const |
||
1269 |
{ |
||
1270 |
// If the widget's parent has no child then we have found the widget.. |
||
1271 |
Widget* parent = mTop; |
||
1272 |
Widget* child = mTop; |
||
1273 |
Widget* selectable = mTop; |
||
1274 |
|||
1275 |
while (child != nullptr) |
||
1276 |
{ |
||
1277 |
Widget *const swap = child; |
||
1278 |
int parentX; |
||
1279 |
int parentY; |
||
1280 |
parent->getAbsolutePosition(parentX, parentY); |
||
1281 |
child = parent->getWidgetAt(x - parentX, y - parentY); |
||
1282 |
parent = swap; |
||
1283 |
if (parent->isSelectable()) |
||
1284 |
selectable = parent; |
||
1285 |
} |
||
1286 |
|||
1287 |
return selectable; |
||
1288 |
} |
||
1289 |
|||
1290 |
Widget* Gui::getMouseEventSource(const int x, const int y) const |
||
1291 |
{ |
||
1292 |
Widget *const widget = getWidgetAt(x, y); |
||
1293 |
if (widget == nullptr) |
||
1294 |
return nullptr; |
||
1295 |
|||
1296 |
if (mFocusHandler != nullptr && |
||
1297 |
mFocusHandler->getModalMouseInputFocused() != nullptr && |
||
1298 |
!widget->isModalMouseInputFocused()) |
||
1299 |
{ |
||
1300 |
return mFocusHandler->getModalMouseInputFocused(); |
||
1301 |
} |
||
1302 |
|||
1303 |
return widget; |
||
1304 |
} |
||
1305 |
|||
1306 |
Widget* Gui::getKeyEventSource() const |
||
1307 |
{ |
||
1308 |
Widget* widget = mFocusHandler->getFocused(); |
||
1309 |
|||
1310 |
while (widget != nullptr && |
||
1311 |
widget->getInternalFocusHandler() != nullptr && |
||
1312 |
widget->getInternalFocusHandler()->getFocused() != nullptr) |
||
1313 |
{ |
||
1314 |
widget = widget->getInternalFocusHandler()->getFocused(); |
||
1315 |
} |
||
1316 |
|||
1317 |
return widget; |
||
1318 |
} |
||
1319 |
|||
1320 |
void Gui::distributeKeyEvent(KeyEvent &event) const |
||
1321 |
{ |
||
1322 |
Widget* parent = event.getSource(); |
||
1323 |
Widget* widget = parent; |
||
1324 |
|||
1325 |
if (parent == nullptr) |
||
1326 |
return; |
||
1327 |
if (mFocusHandler->getModalFocused() != nullptr && |
||
1328 |
!widget->isModalFocused()) |
||
1329 |
{ |
||
1330 |
return; |
||
1331 |
} |
||
1332 |
if (mFocusHandler->getModalMouseInputFocused() != nullptr && |
||
1333 |
!widget->isModalMouseInputFocused()) |
||
1334 |
{ |
||
1335 |
return; |
||
1336 |
} |
||
1337 |
|||
1338 |
while (parent != nullptr) |
||
1339 |
{ |
||
1340 |
// If the widget has been removed due to input |
||
1341 |
// cancel the distribution. |
||
1342 |
if (!Widget::widgetExists(widget)) |
||
1343 |
break; |
||
1344 |
|||
1345 |
parent = widget->getParent(); |
||
1346 |
|||
1347 |
if (widget->isEnabled()) |
||
1348 |
{ |
||
1349 |
std::list<KeyListener*> keyListeners |
||
1350 |
= widget->getKeyListeners(); |
||
1351 |
|||
1352 |
const KeyEventTypeT eventType = event.getType(); |
||
1353 |
// Send the event to all key listeners of the source widget. |
||
1354 |
FOR_EACH (std::list<KeyListener*>::const_iterator, |
||
1355 |
it, keyListeners) |
||
1356 |
{ |
||
1357 |
switch (eventType) |
||
1358 |
{ |
||
1359 |
case KeyEventType::PRESSED: |
||
1360 |
(*it)->keyPressed(event); |
||
1361 |
break; |
||
1362 |
case KeyEventType::RELEASED: |
||
1363 |
(*it)->keyReleased(event); |
||
1364 |
break; |
||
1365 |
default: |
||
1366 |
break; |
||
1367 |
} |
||
1368 |
} |
||
1369 |
} |
||
1370 |
|||
1371 |
const Widget *const swap = widget; |
||
1372 |
widget = parent; |
||
1373 |
parent = swap->getParent(); |
||
1374 |
|||
1375 |
// If a non modal focused widget has been reach |
||
1376 |
// and we have modal focus cancel the distribution. |
||
1377 |
if ((widget != nullptr) && |
||
1378 |
(mFocusHandler->getModalFocused() != nullptr) && |
||
1379 |
!widget->isModalFocused()) |
||
1380 |
{ |
||
1381 |
break; |
||
1382 |
} |
||
1383 |
} |
||
1384 |
} |
||
1385 |
|||
1386 |
void Gui::distributeKeyEventToGlobalKeyListeners(KeyEvent& event) |
||
1387 |
{ |
||
1388 |
BLOCK_START("Gui::distributeKeyEventToGlobalKeyListeners") |
||
1389 |
const KeyEventTypeT eventType = event.getType(); |
||
1390 |
FOR_EACH (KeyListenerListIterator, it, mKeyListeners) |
||
1391 |
{ |
||
1392 |
switch (eventType) |
||
1393 |
{ |
||
1394 |
case KeyEventType::PRESSED: |
||
1395 |
(*it)->keyPressed(event); |
||
1396 |
break; |
||
1397 |
case KeyEventType::RELEASED: |
||
1398 |
(*it)->keyReleased(event); |
||
1399 |
break; |
||
1400 |
default: |
||
1401 |
break; |
||
1402 |
} |
||
1403 |
|||
1404 |
if (event.isConsumed()) |
||
1405 |
break; |
||
1406 |
} |
||
1407 |
BLOCK_END("Gui::distributeKeyEventToGlobalKeyListeners") |
||
1408 |
} |
||
1409 |
|||
1410 |
void Gui::handleModalMouseInputFocus() |
||
1411 |
{ |
||
1412 |
BLOCK_START("Gui::handleModalMouseInputFocus") |
||
1413 |
Widget *const lastModalWidget |
||
1414 |
= mFocusHandler->getLastWidgetWithModalMouseInputFocus(); |
||
1415 |
Widget *const modalWidget = mFocusHandler->getModalMouseInputFocused(); |
||
1416 |
if (lastModalWidget != modalWidget) |
||
1417 |
{ |
||
1418 |
// Check if modal mouse input focus has been gained by a widget. |
||
1419 |
if (lastModalWidget == nullptr) |
||
1420 |
{ |
||
1421 |
handleModalFocusGained(); |
||
1422 |
mFocusHandler->setLastWidgetWithModalMouseInputFocus(modalWidget); |
||
1423 |
} |
||
1424 |
// Check if modal mouse input focus has been released. |
||
1425 |
else |
||
1426 |
{ |
||
1427 |
handleModalFocusReleased(); |
||
1428 |
mFocusHandler->setLastWidgetWithModalMouseInputFocus(nullptr); |
||
1429 |
} |
||
1430 |
} |
||
1431 |
BLOCK_END("Gui::handleModalMouseInputFocus") |
||
1432 |
} |
||
1433 |
|||
1434 |
void Gui::handleModalFocus() |
||
1435 |
{ |
||
1436 |
BLOCK_START("Gui::handleModalFocus") |
||
1437 |
Widget *const lastModalWidget |
||
1438 |
= mFocusHandler->getLastWidgetWithModalFocus(); |
||
1439 |
Widget *const modalWidget = mFocusHandler->getModalFocused(); |
||
1440 |
|||
1441 |
if (lastModalWidget != modalWidget) |
||
1442 |
{ |
||
1443 |
// Check if modal focus has been gained by a widget. |
||
1444 |
if (lastModalWidget == nullptr) |
||
1445 |
{ |
||
1446 |
handleModalFocusGained(); |
||
1447 |
mFocusHandler->setLastWidgetWithModalFocus(modalWidget); |
||
1448 |
} |
||
1449 |
// Check if modal focus has been released. |
||
1450 |
else |
||
1451 |
{ |
||
1452 |
handleModalFocusReleased(); |
||
1453 |
mFocusHandler->setLastWidgetWithModalFocus(nullptr); |
||
1454 |
} |
||
1455 |
} |
||
1456 |
BLOCK_END("Gui::handleModalFocus") |
||
1457 |
} |
||
1458 |
|||
1459 |
void Gui::handleModalFocusGained() |
||
1460 |
{ |
||
1461 |
// Distribute an event to all widgets in the "widget with mouse" queue. |
||
1462 |
while (!mWidgetWithMouseQueue.empty()) |
||
1463 |
{ |
||
1464 |
Widget *const widget = mWidgetWithMouseQueue.front(); |
||
1465 |
|||
1466 |
if (Widget::widgetExists(widget)) |
||
1467 |
{ |
||
1468 |
distributeMouseEvent(widget, |
||
1469 |
MouseEventType::EXITED, |
||
1470 |
mLastMousePressButton, |
||
1471 |
mLastMouseX, |
||
1472 |
mLastMouseY, |
||
1473 |
true, |
||
1474 |
true); |
||
1475 |
} |
||
1476 |
|||
1477 |
mWidgetWithMouseQueue.pop_front(); |
||
1478 |
} |
||
1479 |
|||
1480 |
mFocusHandler->setLastWidgetWithModalMouseInputFocus( |
||
1481 |
mFocusHandler->getModalMouseInputFocused()); |
||
1482 |
} |
||
1483 |
|||
1484 |
void Gui::handleModalFocusReleased() |
||
1485 |
{ |
||
1486 |
// Check all widgets below the mouse to see if they are |
||
1487 |
// present in the "widget with mouse" queue. If a widget |
||
1488 |
// is not then it should be added and an entered event should |
||
1489 |
// be sent to it. |
||
1490 |
Widget* widget = getMouseEventSource(mLastMouseX, mLastMouseY); |
||
1491 |
Widget* parent = widget; |
||
1492 |
|||
1493 |
while (parent != nullptr && |
||
1494 |
widget != nullptr) |
||
1495 |
{ |
||
1496 |
parent = widget->getParent(); |
||
1497 |
|||
1498 |
// Check if the widget is present in the "widget with mouse" queue. |
||
1499 |
bool widgetIsPresentInQueue = false; |
||
1500 |
FOR_EACH (std::deque<Widget*>::const_iterator, |
||
1501 |
iter, mWidgetWithMouseQueue) |
||
1502 |
{ |
||
1503 |
if (*iter == widget) |
||
1504 |
{ |
||
1505 |
widgetIsPresentInQueue = true; |
||
1506 |
break; |
||
1507 |
} |
||
1508 |
} |
||
1509 |
|||
1510 |
// Widget is not present, send an entered event and add |
||
1511 |
// it to the "widget with mouse" queue. |
||
1512 |
if (!widgetIsPresentInQueue && Widget::widgetExists(widget)) |
||
1513 |
{ |
||
1514 |
distributeMouseEvent(widget, |
||
1515 |
MouseEventType::ENTERED, |
||
1516 |
mLastMousePressButton, |
||
1517 |
mLastMouseX, |
||
1518 |
mLastMouseY, |
||
1519 |
false, |
||
1520 |
true); |
||
1521 |
mWidgetWithMouseQueue.push_front(widget); |
||
1522 |
} |
||
1523 |
|||
1524 |
const Widget *const swap = widget; |
||
1525 |
widget = parent; |
||
1526 |
parent = swap->getParent(); |
||
1527 |
} |
||
1528 |
} |
||
1529 |
|||
1530 |
int Gui::getMousePressLength() const |
||
1531 |
{ |
||
1532 |
if (mLastMousePressTimeStamp == 0U) |
||
1533 |
return 0; |
||
1534 |
unsigned int ticks = SDL_GetTicks(); |
||
1535 |
if (ticks > mLastMousePressTimeStamp) |
||
1536 |
return ticks - mLastMousePressTimeStamp; |
||
1537 |
return mLastMousePressTimeStamp - ticks; |
||
1538 |
2 |
} |
Generated by: GCOVR (Version 3.3) |