• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 5735031298

pending completion
5735031298

Pull #1617

github

web-flow
Merge d291cd187 into a397539c1
Pull Request #1617: Show Popup on missing game files

11 of 11 new or added lines in 3 files covered. (100.0%)

21762 of 43119 relevant lines covered (50.47%)

32710.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.36
/libs/s25main/WindowManager.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "WindowManager.h"
6
#include "CollisionDetection.h"
7
#include "Loader.h"
8
#include "RttrConfig.h"
9
#include "Settings.h"
10
#include "Window.h"
11
#include "commonDefines.h"
12
#include "desktops/Desktop.h"
13
#include "drivers/ScreenResizeEvent.h"
14
#include "drivers/VideoDriverWrapper.h"
15
#include "files.h"
16
#include "helpers/containerUtils.h"
17
#include "helpers/reverse.h"
18
#include "ingameWindows/IngameWindow.h"
19
#include "ogl/FontStyle.h"
20
#include "ogl/SoundEffectItem.h"
21
#include "ogl/glFont.h"
22
#include "ogl/saveBitmap.h"
23
#include "gameData/const_gui_ids.h"
24
#include "libsiedler2/PixelBufferBGRA.h"
25
#include "s25util/Log.h"
26
#include "s25util/MyTime.h"
27
#include <algorithm>
28

29
WindowManager::WindowManager()
4✔
30
    : cursor_(Cursor::Hand), disable_mouse(false), lastMousePos(Position::Invalid()), curRenderSize(0, 0),
4✔
31
      lastLeftClickTime(0), lastLeftClickPos(0, 0)
8✔
32
{}
4✔
33

34
WindowManager::~WindowManager() = default;
8✔
35

36
void WindowManager::CleanUp()
1✔
37
{
38
    windows.clear();
1✔
39
    curDesktop.reset();
1✔
40
    nextdesktop.reset();
1✔
41
}
1✔
42

43
void WindowManager::SetCursor(Cursor cursor)
31✔
44
{
45
    cursor_ = cursor;
31✔
46
}
31✔
47

48
void WindowManager::DrawCursor()
76✔
49
{
50
    auto resId = static_cast<unsigned>(cursor_);
76✔
51
    switch(cursor_)
76✔
52
    {
53
        case Cursor::Hand:
75✔
54
        case Cursor::Remove: resId += VIDEODRIVER.IsLeftDown() ? 1 : 0; break;
75✔
55
        default: break;
1✔
56
    }
57
    if(resId)
76✔
58
        LOADER.GetImageN("resource", resId)->DrawFull(VIDEODRIVER.GetMousePos());
152✔
59
}
76✔
60

61
/**
62
 *  Zeichenfunktion WindowManager-Klasse.
63
 *  Zeichnet Desktop und alle Fenster.
64
 */
65
void WindowManager::Draw()
76✔
66
{
67
    // ist ein neuer Desktop eingetragen? Wenn ja, wechseln
68
    if(nextdesktop)
76✔
69
        DoDesktopSwitch();
25✔
70

71
    if(!curDesktop)
76✔
72
        return;
×
73

74
    curDesktop->Msg_PaintBefore();
76✔
75
    curDesktop->Draw();
76✔
76
    curDesktop->Msg_PaintAfter();
76✔
77

78
    // First close all marked windows
79
    CloseMarkedIngameWnds();
76✔
80
    for(auto& wnd : windows)
142✔
81
    {
82
        // If the window is not minimized, call paintAfter
83
        if(!wnd->IsMinimized())
66✔
84
            wnd->Msg_PaintBefore();
66✔
85
        wnd->Draw();
66✔
86
        // If the window is not minimized, call paintAfter
87
        if(!wnd->IsMinimized())
66✔
88
            wnd->Msg_PaintAfter();
66✔
89
    }
90

91
    DrawToolTip();
76✔
92
    DrawCursor();
76✔
93
}
94

95
/**
96
 *  liefert ob der aktuelle Desktop den Focus besitzt oder nicht.
97
 *
98
 *  @return liefert @p true bei aktivem Desktop,
99
 *                  @p false wenn der Desktop nicht den Fokus besitzt.
100
 */
101
bool WindowManager::IsDesktopActive()
×
102
{
103
    if(curDesktop)
×
104
        return curDesktop->IsActive();
×
105

106
    return false;
×
107
}
108

109
/**
110
 *  schickt eine Nachricht an das aktive Fenster bzw den aktiven Desktop.
111
 *
112
 *  @param[in] msg   Nachricht welche geschickt werden soll
113
 *  @param[in] id    ID des Steuerelements
114
 *  @param[in] param Parameter der Nachricht
115
 */
116

117
/// Sendet eine Tastaturnachricht an die Fenster.
118
void WindowManager::RelayKeyboardMessage(KeyboardMsgHandler msg, const KeyEvent& ke)
19✔
119
{
120
    // When there is no desktop, don't check it or any window
121
    if(!curDesktop)
19✔
122
        return;
×
123
    if(curDesktop->IsActive())
19✔
124
    {
125
        // Desktop active -> relay msg to desktop
126
        CALL_MEMBER_FN(*curDesktop, msg)(ke);
2✔
127
        curDesktop->RelayKeyboardMessage(msg, ke);
2✔
128
        return;
2✔
129
    }
130

131
    if(windows.empty())
17✔
132
        return; // No windows -> nothing to do
×
133

134
    // ESC or ALT+W closes the active window
135
    const auto escape = (ke.kt == KeyType::Escape);
17✔
136
    if(escape || (ke.c == 'w' && ke.alt))
17✔
137
    {
138
        // Find one which isn't yet marked for closing so multiple ESC in between draw calls can close multiple windows
139
        // ESC doesn't close pinned windows
140
        const auto itActiveWnd = std::find_if(windows.rbegin(), windows.rend(), [escape](const auto& wnd) {
11✔
141
            return !wnd->ShouldBeClosed() && !(escape && wnd->IsPinned());
12✔
142
        });
22✔
143
        if(itActiveWnd != windows.rend() && (*itActiveWnd)->getCloseBehavior() != CloseBehavior::Custom)
11✔
144
            (*itActiveWnd)->Close();
11✔
145
    } else if(!CALL_MEMBER_FN(*windows.back(), msg)(ke)) // send to active window
6✔
146
    {
147
        // If not handled yet, relay to active window
148
        if(!windows.back()->RelayKeyboardMessage(msg, ke))
×
149
        {
150
            // If message was not handled send to desktop
151
            CALL_MEMBER_FN(*curDesktop, msg)(ke);
×
152
            curDesktop->RelayKeyboardMessage(msg, ke);
×
153
        }
154
    }
155
}
156

157
/// Sendet eine Mausnachricht weiter an alle Fenster
158
void WindowManager::RelayMouseMessage(MouseMsgHandler msg, const MouseCoords& mc)
2✔
159
{
160
    // ist der Desktop gültig?
161
    if(!curDesktop)
2✔
162
        return;
×
163
    // ist der Desktop aktiv?
164
    if(curDesktop->IsActive())
2✔
165
    {
166
        // Ja, dann Nachricht an Desktop weiterleiten
167
        CALL_MEMBER_FN(*curDesktop, msg)(mc);
2✔
168
        curDesktop->RelayMouseMessage(msg, mc);
2✔
169
    } else if(!windows.empty())
×
170
    {
171
        // Nein, dann Nachricht an letztes Fenster weiterleiten
172
        CALL_MEMBER_FN(*windows.back(), msg)(mc);
×
173
        windows.back()->RelayMouseMessage(msg, mc);
×
174
    }
175
}
176

177
/**
178
 *  Öffnet ein IngameWindow und fügt es zur Fensterliste hinzu.
179
 */
180
IngameWindow& WindowManager::DoShow(std::unique_ptr<IngameWindow> window, bool mouse)
55✔
181
{
182
    RTTR_Assert(window);
55✔
183
    RTTR_Assert(!helpers::contains(windows, window));
55✔
184
    // No desktop -> Out
185
    if(!curDesktop)
55✔
186
        throw std::runtime_error("No desktop active for window to be shown on");
×
187

188
    SetToolTip(nullptr, "");
55✔
189

190
    // All windows are inserted before the first modal window (shown behind)
191
    auto itModal = helpers::find_if(windows, [](const auto& curWnd) { return curWnd->IsModal(); });
85✔
192
    // Note that if there is no other modal window it will be put at the back which is what we want
193
    auto& result = **windows.emplace(itModal, std::move(window));
55✔
194

195
    // Make the new window active (special cases handled in the function)
196
    SetActiveWindow(result);
55✔
197

198
    // Maus deaktivieren, bis sie losgelassen wurde (Fix des Switch-Anschließend-Drück-Bugs)
199
    disable_mouse = mouse;
55✔
200
    return result;
55✔
201
}
202

203
IngameWindow* WindowManager::ShowAfterSwitch(std::unique_ptr<IngameWindow> window)
1✔
204
{
205
    RTTR_Assert(window);
1✔
206
    nextWnds.emplace_back(std::move(window));
1✔
207
    return nextWnds.back().get();
1✔
208
}
209

210
/**
211
 *  merkt einen Desktop zum Wechsel vor.
212
 *
213
 *  @param[in] desktop       Pointer zum neuen Desktop, auf dem gewechselt werden soll
214
 *  @param[in] data          Daten für den neuen Desktop
215
 */
216
Desktop* WindowManager::Switch(std::unique_ptr<Desktop> desktop)
25✔
217
{
218
    nextdesktop = std::move(desktop);
25✔
219
    // Disable the mouse till the next desktop is shown to avoid e.g. double-switching
220
    disable_mouse = true;
25✔
221
    return nextdesktop.get();
25✔
222
}
223

224
IngameWindow* WindowManager::FindWindowAtPos(const Position& pos) const
9✔
225
{
226
    // Fenster durchgehen ( von hinten nach vorn, da die vordersten ja zuerst geprüft werden müssen !! )
227
    for(const auto& window : helpers::reverse(windows))
9✔
228
    {
229
        // FensterRect für Kollisionsabfrage
230
        Rect window_rect = window->GetDrawRect();
9✔
231

232
        // trifft die Maus auf ein Fenster?
233
        if(IsPointInRect(pos, window_rect))
9✔
234
        {
235
            return window.get();
9✔
236
        }
237
        // Check also if we are in the locked area of a window (e.g. dropdown extends outside of window)
238
        if(window->IsInLockedRegion(pos))
×
239
            return window.get();
×
240
    }
241
    return nullptr;
×
242
}
243

244
IngameWindow* WindowManager::FindNonModalWindow(unsigned id) const
19✔
245
{
246
    auto itWnd = helpers::find_if(
247
      windows, [id](const auto& wnd) { return !wnd->ShouldBeClosed() && !wnd->IsModal() && wnd->GetID() == id; });
46✔
248
    return itWnd == windows.end() ? nullptr : itWnd->get();
19✔
249
}
250

251
/**
252
 *  Verarbeitung des Drückens der Linken Maustaste.
253
 *
254
 *  @param[in] mc Mauskoordinaten Struktur
255
 */
256
void WindowManager::Msg_LeftDown(MouseCoords mc)
11✔
257
{
258
    // ist unser Desktop gültig?
259
    if(!curDesktop)
11✔
260
        return;
×
261

262
    // Sound abspielen
263
    SoundEffectItem* sound = LOADER.GetSoundN("sound", 112);
22✔
264
    if(sound)
11✔
265
        sound->Play(255, false);
11✔
266

267
    // haben wir überhaupt fenster?
268
    if(windows.empty())
11✔
269
    {
270
        // nein, dann Desktop aktivieren
271
        SetActiveWindow(*curDesktop);
11✔
272

273
        // ist der Maus-Klick-Fix aktiv?
274
        if(!disable_mouse)
11✔
275
        {
276
            // nein, Msg_LeftDown aufrufen
277
            curDesktop->Msg_LeftDown(mc);
11✔
278

279
            // und allen unten drunter auch Bescheid sagen
280
            curDesktop->RelayMouseMessage(&Window::Msg_LeftDown, mc);
11✔
281
        }
282

283
        // und raus
284
        return;
11✔
285
    }
286

287
    // ist das zuletzt aktiv gewesene Fenster Modal?
288
    IngameWindow& lastActiveWnd = *windows.back();
×
289
    if(lastActiveWnd.IsModal())
×
290
    {
291
        if(!lastActiveWnd.IsActive())
×
292
            SetActiveWindow(lastActiveWnd);
×
293

294
        // ja es ist modal, ist der Maus-Klick-Fix aktiv?
295
        if(!disable_mouse)
×
296
        {
297
            // nein, Msg_LeftDownaufrufen
298
            lastActiveWnd.Msg_LeftDown(mc);
×
299

300
            // und allen unten drunter auch Bescheid sagen
301
            lastActiveWnd.RelayMouseMessage(&Window::Msg_LeftDown, mc);
×
302

303
            // und noch MouseLeftDown vom Fenster aufrufen
304
            lastActiveWnd.MouseLeftDown(mc);
×
305
        }
306

307
        // und raus
308
        return;
×
309
    }
310

311
    IngameWindow* foundWindow = FindWindowAtPos(mc.GetPos());
×
312

313
    // Haben wir ein Fenster gefunden gehabt?
314
    if(foundWindow)
×
315
    {
316
        SetActiveWindow(*foundWindow);
×
317

318
        // ist der Maus-Klick-Fix aktiv?
319
        if(!disable_mouse)
×
320
        {
321
            // nein, dann Msg_LeftDown aufrufen
322
            foundWindow->Msg_LeftDown(mc);
×
323

324
            // und allen unten drunter auch Bescheid sagen
325
            foundWindow->RelayMouseMessage(&Window::Msg_LeftDown, mc);
×
326

327
            // und noch MouseLeftDown vom Fenster aufrufen
328
            foundWindow->MouseLeftDown(mc);
×
329
        }
330
    } else
331
    {
332
        SetActiveWindow(*curDesktop);
×
333

334
        // ist der Maus-Klick-Fix aktiv?
335
        if(!disable_mouse)
×
336
        {
337
            // nein, dann Msg_LeftDown aufrufen
338
            curDesktop->Msg_LeftDown(mc);
×
339

340
            // und allen unten drunter auch Bescheid sagen
341
            curDesktop->RelayMouseMessage(&Window::Msg_LeftDown, mc);
×
342
        }
343
    }
344
}
345

346
/**
347
 *  Verarbeitung des Loslassens der Linken Maustaste.
348
 *
349
 *  @param[in] mc Mauskoordinaten Struktur
350
 */
351
void WindowManager::Msg_LeftUp(MouseCoords mc)
11✔
352
{
353
    // ist unser Desktop gültig?
354
    if(!curDesktop)
11✔
355
        return;
×
356

357
    // Ggf. Doppelklick untersuche
358
    unsigned time_now = VIDEODRIVER.GetTickCount();
11✔
359
    if(time_now - lastLeftClickTime < DOUBLE_CLICK_INTERVAL && mc.GetPos() == lastLeftClickPos)
11✔
360
    {
361
        mc.dbl_click = true;
1✔
362
    } else
363
    {
364
        // Werte wieder erneut speichern
365
        lastLeftClickPos = mc.GetPos();
10✔
366
        lastLeftClickTime = time_now;
10✔
367
    }
368

369
    // ist der Maus-Klick-Fix aktiv?
370
    if(!disable_mouse)
11✔
371
    {
372
        // ist der Desktop aktiv?
373
        if(curDesktop->IsActive())
10✔
374
        {
375
            // ja, dann Msg_LeftUp aufrufen
376
            curDesktop->Msg_LeftUp(mc);
10✔
377

378
            // und die Fenster darunter auch
379
            curDesktop->RelayMouseMessage(&Window::Msg_LeftUp, mc);
10✔
380
        } else if(!windows.empty())
×
381
        {
382
            // ja, dann Msg_LeftUp aufrufen
383
            IngameWindow& activeWnd = *windows.back();
×
384
            activeWnd.Msg_LeftUp(mc);
×
385

386
            // und den anderen Fenstern auch Bescheid geben
387
            activeWnd.RelayMouseMessage(&Window::Msg_LeftUp, mc);
×
388

389
            // und noch MouseLeftUp vom Fenster aufrufen
390
            activeWnd.MouseLeftUp(mc);
×
391
        }
392
    }
393

394
    // Maus-Klick-Fix deaktivieren
395
    if(disable_mouse && !nextdesktop)
11✔
396
        disable_mouse = false;
1✔
397
}
398

399
/**
400
 *  Verarbeitung des Drückens der Rechten Maustaste.
401
 *
402
 *  @param[in] mc Mauskoordinaten Struktur
403
 */
404
void WindowManager::Msg_RightDown(const MouseCoords& mc)
13✔
405
{
406
    // ist unser Desktop gültig?
407
    if(!curDesktop)
13✔
408
        return;
×
409

410
    // Right-click closes (most) windows, so check that
411
    if(!windows.empty())
13✔
412
    {
413
        IngameWindow* foundWindow = FindWindowAtPos(mc.GetPos());
9✔
414
        if(windows.back()->IsModal())
9✔
415
        {
416
            // We have a modal window -> Activate it
417
            SetActiveWindow(*windows.back());
2✔
418
            // Ignore actions in all other windows
419
            if(foundWindow != GetTopMostWindow())
2✔
420
                return;
×
421
        }
422
        if(foundWindow)
9✔
423
        {
424
            // Close it if requested (unless pinned)
425
            if(foundWindow->getCloseBehavior() == CloseBehavior::Regular)
9✔
426
            {
427
                if(!foundWindow->IsPinned())
7✔
428
                    foundWindow->Close();
6✔
429
            } else
430
            {
431
                SetActiveWindow(*foundWindow);
2✔
432
                foundWindow->Msg_RightDown(mc);
2✔
433
            }
434
            return;
9✔
435
        }
436
    }
437

438
    // ist der Desktop aktiv?
439
    if(curDesktop->IsActive())
4✔
440
    {
441
        // ja, dann Msg_RightDown aufrufen
442
        curDesktop->Msg_RightDown(mc);
4✔
443

444
        // und die Fenster darunter auch
445
        curDesktop->RelayMouseMessage(&Window::Msg_RightDown, mc);
4✔
446
    } else if(!windows.empty())
×
447
    {
448
        // dann Nachricht an Fenster weiterleiten
449
        windows.back()->RelayMouseMessage(&Window::Msg_RightDown, mc);
×
450
    }
451

452
    SetActiveWindow(*curDesktop);
4✔
453

454
    // ja, dann Msg_RightDown aufrufen
455
    curDesktop->Msg_RightDown(mc);
4✔
456

457
    // und die Fenster darunter auch
458
    curDesktop->RelayMouseMessage(&Window::Msg_RightDown, mc);
4✔
459
}
460

461
void WindowManager::Msg_RightUp(const MouseCoords& mc)
2✔
462
{
463
    RelayMouseMessage(&Window::Msg_RightUp, mc);
2✔
464
}
2✔
465

466
/**
467
 *  Verarbeitung Mausrad hoch.
468
 *
469
 *  @param[in] mc Mauskoordinaten Struktur
470
 */
471
void WindowManager::Msg_WheelUp(const MouseCoords& mc)
×
472
{
473
    // ist unser Desktop gültig?
474
    if(!curDesktop)
×
475
        return;
×
476

477
    // haben wir überhaupt fenster?
478
    if(windows.empty())
×
479
    {
480
        SetActiveWindow(*curDesktop);
×
481

482
        // nein, Msg_LeftDown aufrufen
483
        curDesktop->Msg_WheelUp(mc);
×
484

485
        // und allen unten drunter auch Bescheid sagen
486
        curDesktop->RelayMouseMessage(&Window::Msg_WheelUp, mc);
×
487

488
        // und raus
489
        return;
×
490
    }
491

492
    // ist das zuletzt aktiv gewesene Fenster Modal?
493
    IngameWindow& activeWnd = *windows.back();
×
494
    if(activeWnd.IsModal())
×
495
    {
496
        // Msg_LeftDownaufrufen
497
        activeWnd.Msg_WheelUp(mc);
×
498

499
        // und allen unten drunter auch Bescheid sagen
500
        activeWnd.RelayMouseMessage(&Window::Msg_WheelUp, mc);
×
501

502
        // und raus
503
        return;
×
504
    }
505

506
    IngameWindow* foundWindow = FindWindowAtPos(mc.GetPos());
×
507

508
    if(foundWindow)
×
509
    {
510
        SetActiveWindow(*foundWindow);
×
511

512
        // dann Msg_WheelUp aufrufen
513
        foundWindow->Msg_WheelUp(mc);
×
514

515
        // und allen unten drunter auch Bescheid sagen
516
        foundWindow->RelayMouseMessage(&Window::Msg_WheelUp, mc);
×
517
    } else
518
    {
519
        SetActiveWindow(*curDesktop);
×
520

521
        // nein, dann Msg_WheelUpDown aufrufen
522
        curDesktop->Msg_WheelUp(mc);
×
523

524
        // und allen unten drunter auch Bescheid sagen
525
        curDesktop->RelayMouseMessage(&Window::Msg_WheelUp, mc);
×
526
    }
527
}
528

529
/**
530
 *  Verarbeitung Mausrad runter
531
 *
532
 *  @param[in] mc Mauskoordinaten Struktur
533
 */
534
void WindowManager::Msg_WheelDown(const MouseCoords& mc)
×
535
{
536
    if(!curDesktop)
×
537
        return;
×
538
    if(windows.empty())
×
539
    {
540
        SetActiveWindow(*curDesktop);
×
541
        curDesktop->Msg_WheelDown(mc);
×
542
        curDesktop->RelayMouseMessage(&Window::Msg_WheelDown, mc);
×
543
        // und raus
544
        return;
×
545
    }
546
    IngameWindow& activeWnd = *windows.back();
×
547
    if(activeWnd.IsModal())
×
548
    {
549
        activeWnd.Msg_WheelDown(mc);
×
550
        activeWnd.RelayMouseMessage(&Window::Msg_WheelDown, mc);
×
551
        return;
×
552
    }
553
    IngameWindow* foundWindow = FindWindowAtPos(mc.GetPos());
×
554

555
    if(foundWindow)
×
556
    {
557
        SetActiveWindow(*foundWindow);
×
558
        foundWindow->Msg_WheelDown(mc);
×
559
        foundWindow->RelayMouseMessage(&Window::Msg_WheelDown, mc);
×
560
    } else
561
    {
562
        SetActiveWindow(*curDesktop);
×
563
        curDesktop->Msg_WheelDown(mc);
×
564
        curDesktop->RelayMouseMessage(&Window::Msg_WheelDown, mc);
×
565
    }
566
}
567

568
/**
569
 *  Verarbeitung des Verschiebens der Maus.
570
 *
571
 *  @param[in] mc Mauskoordinaten Struktur
572
 */
573
void WindowManager::Msg_MouseMove(const MouseCoords& mc)
42✔
574
{
575
    lastMousePos = mc.pos;
42✔
576

577
    // ist unser Desktop gültig?
578
    if(!curDesktop)
42✔
579
        return;
×
580

581
    // nein, ist unser Desktop aktiv?
582
    if(curDesktop->IsActive())
42✔
583
    {
584
        // ja, dann Msg_MouseMove aufrufen
585
        curDesktop->Msg_MouseMove(mc);
39✔
586

587
        // und alles drunter auch benachrichtigen
588
        curDesktop->RelayMouseMessage(&Window::Msg_MouseMove, mc);
39✔
589
    } else if(!windows.empty())
3✔
590
    {
591
        IngameWindow& activeWnd = *windows.back();
3✔
592
        // und MouseMove vom Fenster aufrufen
593
        activeWnd.MouseMove(mc);
3✔
594

595
        // ja, dann Msg_MouseMove aufrufen
596
        activeWnd.Msg_MouseMove(mc);
3✔
597

598
        // und alles drunter auch benachrichtigen
599
        activeWnd.RelayMouseMessage(&Window::Msg_MouseMove, mc);
3✔
600
    }
601
}
602

603
void WindowManager::Msg_KeyDown(const KeyEvent& ke)
19✔
604
{
605
    if(ke.alt && (ke.kt == KeyType::Return))
19✔
606
    {
607
        // Switch Fullscreen/Windowed
608
        const auto newScreenSize =
609
          !SETTINGS.video.fullscreen ? SETTINGS.video.fullscreenSize : SETTINGS.video.windowedSize; //-V807
×
610
        VIDEODRIVER.ResizeScreen(newScreenSize, !SETTINGS.video.fullscreen);
×
611
        SETTINGS.video.fullscreen = VIDEODRIVER.IsFullscreen();
×
612
    } else if(ke.kt == KeyType::Print)
19✔
613
        TakeScreenshot();
×
614
    else
615
        RelayKeyboardMessage(&Window::Msg_KeyDown, ke);
19✔
616
}
19✔
617

618
/**
619
 *  Handle resize of the window or change of resolution
620
 */
621
void WindowManager::WindowResized()
2✔
622
{
623
    VIDEODRIVER.RenewViewport();
2✔
624
    Msg_ScreenResize(VIDEODRIVER.GetRenderSize());
2✔
625
}
2✔
626

627
/**
628
 *  React to change of the render size
629
 */
630
void WindowManager::Msg_ScreenResize(const Extent& newSize)
7✔
631
{
632
    // Don't handle it if nothing changed
633
    if(newSize == curRenderSize)
7✔
634
        return;
5✔
635

636
    ScreenResizeEvent sr(curRenderSize, elMax(Extent(800, 600), newSize));
5✔
637
    curRenderSize = sr.newSize;
5✔
638

639
    // Don't change fullscreen size (only in menu)
640
    if(!SETTINGS.video.fullscreen)
5✔
641
        SETTINGS.video.windowedSize = VIDEODRIVER.GetWindowSize();
5✔
642

643
    // ist unser Desktop gültig?
644
    if(!curDesktop)
5✔
645
        return;
3✔
646

647
    curDesktop->Msg_ScreenResize(sr);
2✔
648

649
    // IngameWindow verschieben falls nötig, so dass sie komplett sichtbar sind
650
    for(const auto& window : windows)
2✔
651
    {
652
        DrawPoint delta = window->GetPos() + DrawPoint(window->GetSize()) - DrawPoint(sr.newSize);
×
653
        if(delta.x > 0 || delta.y > 0)
×
654
            window->SetPos(window->GetPos() - elMax(delta, DrawPoint(0, 0)));
×
655
    }
656
}
657

658
IngameWindow* WindowManager::GetTopMostWindow() const
123✔
659
{
660
    if(windows.empty())
123✔
661
        return nullptr;
37✔
662
    else
663
        return windows.back().get();
86✔
664
}
665

666
void WindowManager::DoClose(IngameWindow* window)
43✔
667
{
668
    const auto it =
669
      std::find_if(windows.begin(), windows.end(), [window](const auto& it) { return it.get() == window; });
108✔
670

671
    RTTR_Assert(it != windows.end());
43✔
672

673
    SetToolTip(nullptr, "");
43✔
674

675
    // Store if this was the active window
676
    const bool isActiveWnd = window == GetTopMostWindow();
43✔
677

678
    // Remove from list and notify parent, hold onto it till parent is notified
679
    const auto tmpHolder = std::move(*it);
86✔
680
    windows.erase(it);
43✔
681
    if(isActiveWnd)
43✔
682
    {
683
        if(windows.empty())
35✔
684
            SetActiveWindow(*curDesktop);
25✔
685
        else
686
            SetActiveWindow(*windows.back());
10✔
687
    }
688
    curDesktop->Msg_WindowClosed(*tmpHolder);
43✔
689
}
43✔
690

691
/**
692
 *  Closes _ALL_ windows with the given ID
693
 *
694
 *  @param[in] id ID of the window to be closed
695
 */
696
void WindowManager::Close(unsigned id)
3✔
697
{
698
    for(auto& wnd : windows)
5✔
699
    {
700
        if(wnd->GetID() == id && !wnd->ShouldBeClosed())
2✔
701
            wnd->Close();
2✔
702
    }
703
}
3✔
704

705
void WindowManager::CloseNow(IngameWindow* window)
9✔
706
{
707
    if(!window->ShouldBeClosed())
9✔
708
        window->Close();
9✔
709
    DoClose(window);
9✔
710
}
9✔
711

712
/**
713
 *  Actually process the desktop change
714
 */
715
void WindowManager::DoDesktopSwitch()
25✔
716
{
717
    RTTR_Assert(nextdesktop);
25✔
718
    VIDEODRIVER.ClearScreen();
25✔
719

720
    SetToolTip(nullptr, "");
25✔
721

722
    // If we have a current desktop close all windows
723
    if(curDesktop)
25✔
724
        windows.clear();
21✔
725

726
    // Do the switch
727
    curDesktop = std::move(nextdesktop);
25✔
728
    curDesktop->SetActive(true);
25✔
729

730
    for(auto& nextWnd : nextWnds)
26✔
731
        Show(std::move(nextWnd));
1✔
732
    nextWnds.clear();
25✔
733

734
    if(!VIDEODRIVER.IsLeftDown())
25✔
735
        disable_mouse = false;
25✔
736

737
    // Dummy mouse move to init hovering etc
738
    Msg_MouseMove(MouseCoords(VIDEODRIVER.GetMousePos()));
25✔
739
}
25✔
740

741
void WindowManager::CloseMarkedIngameWnds()
76✔
742
{
743
    auto isWndMarkedForClose = [](const auto& wnd) { return wnd->ShouldBeClosed(); };
121✔
744
    auto it = std::find_if(windows.begin(), windows.end(), isWndMarkedForClose);
76✔
745
    while(it != windows.end())
110✔
746
    {
747
        DoClose(it->get());
34✔
748
        it = std::find_if(windows.begin(), windows.end(), isWndMarkedForClose);
34✔
749
    }
750
}
76✔
751

752
template<class T_Windows>
753
void SetActiveWindowImpl(const Window& wnd, Desktop& desktop, T_Windows& windows)
109✔
754
{
755
    auto itWnd = helpers::find_if(windows, [&wnd](const auto& it) { return it.get() == &wnd; });
215✔
756
    if(itWnd != windows.end())
109✔
757
    {
758
        // If we have a modal window, don't make this active unless it is the top most one
759
        if(&wnd != windows.back().get() && helpers::contains_if(windows, [](const auto& it) { return it->IsModal(); }))
85✔
760
        {
761
            return;
8✔
762
        }
763
        desktop.SetActive(false);
61✔
764
        // Move window to the end (itWnd+1 -> itWnd -> end())
765
        std::rotate(itWnd, std::next(itWnd), windows.end());
61✔
766
    } else if(&wnd == &desktop)
40✔
767
        desktop.SetActive(true);
40✔
768
    else
769
        return;
×
770
    for(const auto& curWnd : windows)
194✔
771
        curWnd->SetActive(&wnd == curWnd.get());
93✔
772
}
773

774
void WindowManager::SetActiveWindow(Window& wnd)
109✔
775
{
776
    if(curDesktop)
109✔
777
        SetActiveWindowImpl(wnd, *curDesktop, windows);
109✔
778
    if(nextdesktop) // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move)
109✔
779
        SetActiveWindowImpl(wnd, *nextdesktop, nextWnds);
×
780
}
109✔
781

782
void WindowManager::TakeScreenshot() const
×
783
{
784
    libsiedler2::PixelBufferBGRA buffer(curRenderSize.x, curRenderSize.y);
×
785
    glReadPixels(0, 0, curRenderSize.x, curRenderSize.y, GL_BGRA, GL_UNSIGNED_BYTE, buffer.getPixelPtr());
×
786
    flipVertical(buffer);
×
787
    const bfs::path outFilepath =
788
      RTTRCONFIG.ExpandPath(s25::folders::screenshots) / (s25util::Time::FormatTime("%Y-%m-%d_%H-%i-%s") + ".bmp");
×
789
    try
790
    {
791
        saveBitmap(buffer, outFilepath);
×
792
        LOG.write(_("Screenshot saved to %1%\n")) % outFilepath;
×
793
    } catch(const std::runtime_error& e)
×
794
    {
795
        LOG.write(_("Error writing screenshot: %1%\n")) % e.what();
×
796
    }
797
}
×
798

799
class WindowManager::Tooltip
800
{
801
    static constexpr unsigned BORDER_SIZE = 2;
802
    const ctrlBaseTooltip* showingCtrl;
803
    const glFont* font;
804
    std::vector<std::string> lines;
805
    unsigned width = 0, height = 0;
806
    unsigned short maxWidth;
807

808
public:
809
    Tooltip(const ctrlBaseTooltip* showingCtrl, const std::string& text, unsigned short maxWidth)
×
810
        : showingCtrl(showingCtrl), font(NormalFont), maxWidth(maxWidth)
×
811
    {
812
        setText(text);
×
813
    }
×
814

815
    auto getShowingCtrl() const { return showingCtrl; }
×
816
    auto getWidth() const { return width; }
×
817

818
    void setText(const std::string& text)
×
819
    {
820
        lines = font->GetWrapInfo(text, maxWidth, maxWidth).CreateSingleStrings(text);
×
821
        if(lines.empty())
×
822
            return;
×
823
        width = 0;
×
824
        for(const auto& line : lines)
×
825
            width = std::max(width, font->getWidth(line));
×
826
        width += BORDER_SIZE * 2;
×
827
        height = lines.size() * font->getHeight() + BORDER_SIZE * 2;
×
828
    }
829

830
    void draw(DrawPoint pos) const
×
831
    {
832
        Window::DrawRectangle(Rect(pos, width, height), 0x9F000000);
×
833
        pos += DrawPoint::all(BORDER_SIZE);
×
834
        const auto fontHeight = font->getHeight();
×
835
        for(const auto& line : lines)
×
836
        {
837
            font->Draw(pos, line, FontStyle::TOP, COLOR_YELLOW);
×
838
            pos.y += fontHeight;
×
839
        }
840
    }
×
841
};
842

843
void WindowManager::SetToolTip(const ctrlBaseTooltip* ttw, const std::string& tooltip, bool updateCurrent)
787✔
844
{
845
    // Max width of tooltip
846
    constexpr unsigned short MAX_TOOLTIP_WIDTH = 260;
787✔
847

848
    if(tooltip.empty())
787✔
849
    {
850
        if(curTooltip && (!ttw || curTooltip->getShowingCtrl() == ttw))
787✔
851
            curTooltip.reset();
×
852
    } else if(updateCurrent)
×
853
    {
854
        if(curTooltip && curTooltip->getShowingCtrl() == ttw)
×
855
            curTooltip->setText(tooltip);
×
856
    } else
857
        curTooltip = std::make_unique<Tooltip>(ttw, tooltip, MAX_TOOLTIP_WIDTH);
×
858
}
787✔
859

860
void WindowManager::DrawToolTip()
76✔
861
{
862
    // Tooltip zeichnen
863
    if(curTooltip && lastMousePos.isValid())
76✔
864
    {
865
        constexpr unsigned cursorWidth = 32;
×
866
        constexpr unsigned cursorPadding = 5;
×
867
        // Horizontal space between mouse position and tooltip border
868
        constexpr unsigned rightSpacing = cursorWidth + cursorPadding;
×
869
        constexpr unsigned leftSpacing = cursorPadding;
×
870
        DrawPoint ttPos = DrawPoint(lastMousePos.x + rightSpacing, lastMousePos.y);
×
871
        unsigned right_edge = ttPos.x + curTooltip->getWidth();
×
872

873
        // links neben der Maus, wenn es über den Rand gehen würde
874
        if(right_edge > curRenderSize.x)
×
875
            ttPos.x = lastMousePos.x - leftSpacing - curTooltip->getWidth();
×
876
        curTooltip->draw(ttPos);
×
877
    }
878
}
76✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc