diff --git a/libs/s25main/ingameWindows/IngameWindow.h b/libs/s25main/ingameWindows/IngameWindow.h index 37e1760426..214d0fd88d 100644 --- a/libs/s25main/ingameWindows/IngameWindow.h +++ b/libs/s25main/ingameWindows/IngameWindow.h @@ -55,14 +55,14 @@ class IngameWindow : public Window glArchivItem_Bitmap* background, bool modal = false, CloseBehavior closeBehavior = CloseBehavior::Regular, Window* parent = nullptr); - /// setzt den Hintergrund. + /// Set background image void SetBackground(glArchivItem_Bitmap* background) { this->background = background; } - /// liefert den Hintergrund. + /// Get background image glArchivItem_Bitmap* GetBackground() const { return background; } - /// setzt den Fenstertitel. + /// Set window title void SetTitle(const std::string& title) { this->title_ = title; } - /// liefert den Fenstertitel. + /// Get window title const std::string& GetTitle() const { return title_; } void Resize(const Extent& newSize) override; @@ -78,14 +78,14 @@ class IngameWindow : public Window /// Set the position for the window after adjusting newPos so the window is in the visible area void SetPos(DrawPoint newPos, bool saveRestorePos = true); - /// merkt das Fenster zum Schließen vor. + /// Queue the window for closing, will be done in next draw cycle virtual void Close(); - /// soll das Fenster geschlossen werden. + /// Return if the window will be closes bool ShouldBeClosed() const { return closeme; } - /// minimiert das Fenster. + /// Minimize window (only title bar and bottom border remains) void SetMinimized(bool minimized = true); - /// ist das Fenster minimiert? + /// Return whether the window is minimized bool IsMinimized() const { return isMinimized_; } void SetPinned(bool pinned = true); @@ -109,12 +109,12 @@ class IngameWindow : public Window /// Called when not minimized after the frame and background have been drawn virtual void DrawContent() {} - /// Verschiebt Fenster in die Bildschirmmitte + /// Move window to center of screen void MoveToCenter(); - /// Verschiebt Fenster neben die Maus + /// Move window next to current cursor position void MoveNextToMouse(); - /// Weiterleitung von Nachrichten erlaubt oder nicht? + /// Return if events (mouse move...) should be passed to controls of the window bool IsMessageRelayAllowed() const override; void SaveOpenStatus(bool isOpen) const; diff --git a/libs/s25main/ingameWindows/iwSkipGFs.cpp b/libs/s25main/ingameWindows/iwSkipGFs.cpp index 29c4f2e120..d0ce94dfd0 100644 --- a/libs/s25main/ingameWindows/iwSkipGFs.cpp +++ b/libs/s25main/ingameWindows/iwSkipGFs.cpp @@ -5,39 +5,91 @@ #include "iwSkipGFs.h" #include "Loader.h" #include "controls/ctrlEdit.h" +#include "helpers/Range.h" #include "network/GameClient.h" #include "gameData/const_gui_ids.h" #include "s25util/StringConversion.h" #include "s25util/colors.h" +namespace { +enum +{ + ID_lblToGf, + ID_edtToGf, + ID_btToGf, + ID_lblByGf, + ID_edtByGf, + ID_btByGf, + ID_btJumpPresetStart, // Must be last, one per jump preset +}; +constexpr std::array jumpPresets = { + 100, + 1000, + 5000, + 10000, +}; +} // namespace + iwSkipGFs::iwSkipGFs(GameWorldView& gwv) - : IngameWindow(CGI_SKIPGFS, IngameWindow::posLastOrCenter, Extent(300, 110), _("Skip GameFrames"), + : IngameWindow(CGI_SKIPGFS, IngameWindow::posLastOrCenter, Extent(300, 120), _("Skip GameFrames"), LOADER.GetImageN("resource", 41)), gwv(gwv) { - // Text vor Editfeld - AddText(0, DrawPoint(50, 36), _("to GameFrame:"), COLOR_YELLOW, FontStyle{}, NormalFont); + constexpr auto lblWidth = 135; + const auto edtOffset = contentOffset.x + lblWidth; + constexpr auto spacing = 5; + constexpr Extent edtSize(100, 20); + Extent btSize(GetIwSize().x - edtOffset - edtSize.x - spacing, edtSize.y); - // Editfeld zum Eingeben des Ziel-GF - ctrlEdit* edit = AddEdit(1, DrawPoint(126, 32), Extent(120, 20), TextureColor::Grey, NormalFont); + DrawPoint curPos(edtOffset, 28); + ctrlEdit* edit = AddEdit(ID_edtToGf, curPos, edtSize, TextureColor::Grey, NormalFont); edit->SetFocus(); + curPos.x -= spacing; + AddText(ID_lblToGf, curPos + DrawPoint(0, 4), _("to GameFrame:"), COLOR_YELLOW, FontStyle::RIGHT, NormalFont); + curPos.x += edtSize.x + spacing * 2; + AddTextButton(ID_btToGf, curPos, btSize, TextureColor::Green2, "->|", NormalFont); + + curPos.x = edtOffset; + curPos.y += edtSize.y + 5; + + AddEdit(ID_edtByGf, curPos, edtSize, TextureColor::Grey, NormalFont); + curPos.x -= spacing; + AddText(ID_lblByGf, curPos + DrawPoint(0, 4), _("by GameFrames:"), COLOR_YELLOW, FontStyle::RIGHT, NormalFont); + curPos.x += edtSize.x + spacing * 2; + AddTextButton(ID_btByGf, curPos, btSize, TextureColor::Green2, "-->", NormalFont); - // OK-Button - AddTextButton(2, DrawPoint(110, 65), Extent(80, 22), TextureColor::Green2, _("OK"), NormalFont); + curPos.x = contentOffset.x + spacing; + curPos.y += edtSize.y + 5; + const auto availWidth = GetIwSize().x - spacing * 2; + btSize.x = (availWidth - spacing * (jumpPresets.size() - 1)) / jumpPresets.size(); + for(auto i : jumpPresets) + { + AddTextButton(ID_btJumpPresetStart + i, curPos, btSize, TextureColor::Green1, "+" + std::to_string(i), + NormalFont); + curPos.x += btSize.x + spacing; + } } -void iwSkipGFs::SkipGFs() +void iwSkipGFs::SkipGFs(const unsigned edtCtrlId) { - int gf = s25util::fromStringClassicDef(GetCtrl(1)->GetText(), 0); - GAMECLIENT.SkipGF(gf, gwv); + int targetGF = s25util::fromStringClassicDef(GetCtrl(edtCtrlId)->GetText(), 0); + if(edtCtrlId == ID_edtByGf) + targetGF += GAMECLIENT.GetGFNumber(); + GAMECLIENT.SkipGF(targetGF, gwv); } -void iwSkipGFs::Msg_ButtonClick(const unsigned /*ctrl_id*/) +void iwSkipGFs::Msg_ButtonClick(const unsigned ctrlId) { - SkipGFs(); + if(ctrlId < ID_btJumpPresetStart) + SkipGFs((ctrlId == ID_btByGf) ? ID_edtByGf : ID_edtToGf); + else + { + const unsigned targetGF = GAMECLIENT.GetGFNumber() + ctrlId - ID_btJumpPresetStart; + GAMECLIENT.SkipGF(targetGF, gwv); + } } -void iwSkipGFs::Msg_EditEnter(const unsigned /*ctrl_id*/) +void iwSkipGFs::Msg_EditEnter(const unsigned ctrlId) { - SkipGFs(); + SkipGFs(ctrlId); } diff --git a/libs/s25main/ingameWindows/iwSkipGFs.h b/libs/s25main/ingameWindows/iwSkipGFs.h index e7be3d31f5..b395e1afdb 100644 --- a/libs/s25main/ingameWindows/iwSkipGFs.h +++ b/libs/s25main/ingameWindows/iwSkipGFs.h @@ -16,9 +16,8 @@ class iwSkipGFs : public IngameWindow private: GameWorldView& gwv; - /// Teilt dem GameClient den Wert mit - void SkipGFs(); + void SkipGFs(unsigned edtCtrlId); - void Msg_ButtonClick(unsigned ctrl_id) override; - void Msg_EditEnter(unsigned ctrl_id) override; + void Msg_ButtonClick(unsigned ctrlId) override; + void Msg_EditEnter(unsigned ctrlId) override; }; diff --git a/libs/s25main/network/GameClient.cpp b/libs/s25main/network/GameClient.cpp index 57692232a1..64772e155e 100644 --- a/libs/s25main/network/GameClient.cpp +++ b/libs/s25main/network/GameClient.cpp @@ -1381,7 +1381,7 @@ void GameClient::ExecuteGameFrame() // Check remaining time until next GF if(framesinfo.frameTime >= framesinfo.gf_length) { - // This can happen, if we don't call this method in intervalls less than gf_length or gf_length has changed + // This can happen, if we don't call this method in intervals less than gf_length or gf_length has changed // TODO: Run multiple GFs per call. // For now just make sure it is less than gf_length by skipping some simulation time, // until we are only a bit less than 1 GF behind diff --git a/tests/s25Main/UI/CMakeLists.txt b/tests/s25Main/UI/CMakeLists.txt index 410286a9f2..fed11e8fab 100644 --- a/tests/s25Main/UI/CMakeLists.txt +++ b/tests/s25Main/UI/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2005 - 2021 Settlers Freaks +# Copyright (C) 2005 - 2025 Settlers Freaks # # SPDX-License-Identifier: GPL-2.0-or-later @@ -7,6 +7,6 @@ add_subdirectory(uiHelper) add_testcase(NAME UI - LIBS s25Main testUIHelper rttr::vld + LIBS s25Main testUIHelper rttr::vld testWorldFixtures COST 45 ) diff --git a/tests/s25Main/UI/testWindows.cpp b/tests/s25Main/UI/testWindows.cpp index 1cff989ed0..4136231055 100644 --- a/tests/s25Main/UI/testWindows.cpp +++ b/tests/s25Main/UI/testWindows.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org) +// Copyright (C) 2005 - 2025 Settlers Freaks (sf-team at siedler25.org) // // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,10 +10,16 @@ #include "controls/ctrlGroup.h" #include "controls/ctrlImage.h" #include "controls/ctrlMultiline.h" +#include "controls/ctrlTextButton.h" #include "desktops/Desktop.h" #include "ingameWindows/iwAddons.h" +#include "ingameWindows/iwSkipGFs.h" #include "ingameWindows/iwVictory.h" #include "uiHelper/uiHelpers.hpp" +#include "worldFixtures/CreateEmptyWorld.h" +#include "worldFixtures/WorldFixture.h" +#include "world/GameWorldView.h" +#include "world/GameWorldViewer.h" #include #include #include @@ -21,6 +27,8 @@ //-V:MOCK_METHOD:813 //-V:MOCK_EXPECT:807 +using SmallWorldFixture = WorldFixture; + BOOST_FIXTURE_TEST_SUITE(Windows, uiHelper::Fixture) BOOST_AUTO_TEST_CASE(Victory) @@ -78,6 +86,19 @@ BOOST_AUTO_TEST_CASE(AddonWindow) } } +BOOST_FIXTURE_TEST_CASE(JumpWindow, SmallWorldFixture) +{ + // Test if it is constructible only, accesses GameClient for buttons + GameWorldViewer gwv(0, world); + GameWorldView view(gwv, Position(0, 0), Extent(100, 100)); + iwSkipGFs wnd(view); + // At least 4 buttons for "jump by x" and at least 1 extra for "jump to" + const auto bts = wnd.GetCtrls(); + BOOST_TEST(bts.size() > 4); + const auto numIncBts = helpers::count_if(bts, [](const ctrlTextButton* bt) { return bt->GetText().at(0) == '+'; }); + BOOST_TEST(numIncBts >= 4); +} + namespace { MOCK_BASE_CLASS(TestWindow, Window) {