From 051a4a3c17a4cfd4e6906c374af8e4d6a7e19333 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 16 May 2023 16:07:03 +0300 Subject: [PATCH] Encapsulation of WindowHandler state --- client/CMT.cpp | 3 +- client/CPlayerInterface.cpp | 90 ++++++++++++------------------ client/Client.cpp | 3 +- client/ClientCommandManager.cpp | 10 +--- client/NetPacksLobbyClient.cpp | 2 +- client/gui/WindowHandler.cpp | 64 +++++++++++---------- client/gui/WindowHandler.h | 56 ++++++++++++++----- client/mainmenu/CMainMenu.cpp | 6 +- client/renderSDL/ScreenHandler.cpp | 2 +- client/windows/CHeroWindow.cpp | 19 +++---- 10 files changed, 132 insertions(+), 123 deletions(-) diff --git a/client/CMT.cpp b/client/CMT.cpp index c942499d1..ad10a7d1d 100644 --- a/client/CMT.cpp +++ b/client/CMT.cpp @@ -623,8 +623,7 @@ static void quitApplication() CSH->endGameplay(); } - GH.windows().listInt.clear(); - GH.windows().objsToBlit.clear(); + GH.windows().clear(); CMM.reset(); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index bc7f8a9ab..2aa74ca70 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -169,15 +169,16 @@ void CPlayerInterface::initGameInterface(std::shared_ptr ENV, std:: void CPlayerInterface::playerStartsTurn(PlayerColor player) { EVENT_HANDLER_CALLED_BY_CLIENT; - if (!vstd::contains (GH.windows().listInt, adventureInt)) + + if(GH.windows().findInts().empty()) { // after map load - remove all active windows and replace them with adventure map - GH.windows().popInts ((int)GH.windows().listInt.size()); - GH.windows().pushInt (adventureInt); + GH.windows().clear(); + GH.windows().pushInt(adventureInt); } // remove all dialogs that do not expect query answer - while (GH.windows().listInt.front() != adventureInt && !dynamic_cast(GH.windows().listInt.front().get())) + while (GH.windows().topInt() != adventureInt && !dynamic_cast(GH.windows().topInt().get())) GH.windows().popInts(1); if (player != playerID && LOCPLINT == this) @@ -538,17 +539,15 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town) castleInt->heroes->update(); castleInt->redraw(); } - for (auto isa : GH.windows().listInt) + + for (auto ki : GH.windows().findInts()) { - CKingdomInterface *ki = dynamic_cast(isa.get()); - if (ki) - { - ki->townChanged(town); - ki->updateGarrisons(); - ki->redraw(); - } + ki->townChanged(town); + ki->updateGarrisons(); + ki->redraw(); } } + void CPlayerInterface::heroVisitsTown(const CGHeroInstance* hero, const CGTownInstance * town) { EVENT_HANDLER_CALLED_BY_CLIENT; @@ -590,17 +589,13 @@ void CPlayerInterface::garrisonsChanged(std::vector ob adventureInt->onTownChanged(town); } - for (auto & elem : GH.windows().listInt) - { - CGarrisonHolder *cgh = dynamic_cast(elem.get()); - if (cgh) - cgh->updateGarrisons(); + for (auto cgh : GH.windows().findInts()) + cgh->updateGarrisons(); - if (CTradeWindow *cmw = dynamic_cast(elem.get())) - { - if (vstd::contains(objs, cmw->hero)) - cmw->garrisonChanged(); - } + for (auto cmw : GH.windows().findInts()) + { + if (vstd::contains(objs, cmw->hero)) + cmw->garrisonChanged(); } GH.windows().totalRedraw(); @@ -1016,7 +1011,7 @@ void CPlayerInterface::showInfoDialog(EInfoWindowMode type, const std::string &t waitWhileDialog(); //Fix for mantis #98 adventureInt->showInfoBoxMessage(components, text, timer); - if (makingTurn && GH.windows().listInt.size() && LOCPLINT == this) + if (makingTurn && GH.windows().count() > 0 && LOCPLINT == this) CCS->soundh->playSound(static_cast(soundID)); return; } @@ -1057,7 +1052,7 @@ void CPlayerInterface::showInfoDialog(const std::string &text, const std::vector } std::shared_ptr temp = CInfoWindow::create(text, playerID, components); - if (makingTurn && GH.windows().listInt.size() && LOCPLINT == this) + if (makingTurn && GH.windows().count() > 0 && LOCPLINT == this) { CCS->soundh->playSound(static_cast(soundID)); showingDialog->set(true); @@ -1206,14 +1201,11 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town ) else if(castleInterface) castleInterface->creaturesChangedEventHandler(); - for(auto isa : GH.windows().listInt) - { - CKingdomInterface *ki = dynamic_cast(isa.get()); - if (ki && townObj) + if (townObj) + for (auto ki : GH.windows().findInts()) ki->townChanged(townObj); - } } - else if(town && GH.windows().listInt.size() && (town->ID == Obj::CREATURE_GENERATOR1 + else if(town && GH.windows().count() > 0 && (town->ID == Obj::CREATURE_GENERATOR1 || town->ID == Obj::CREATURE_GENERATOR4 || town->ID == Obj::WAR_MACHINE_FACTORY)) { CRecruitmentWindow *crw = dynamic_cast(GH.windows().topInt().get()); @@ -1594,7 +1586,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul if(adventureInt) { GH.terminate_cond->setn(true); - GH.windows().popInts(GH.windows().listInt.size()); + GH.windows().popInts(GH.windows().count()); adventureInt.reset(); } } @@ -1809,12 +1801,9 @@ void CPlayerInterface::artifactRemoved(const ArtifactLocation &al) EVENT_HANDLER_CALLED_BY_CLIENT; auto hero = std::visit(HeroObjectRetriever(), al.artHolder); adventureInt->onHeroChanged(hero); - for(auto isa : GH.windows().listInt) - { - auto artWin = dynamic_cast(isa.get()); - if (artWin) - artWin->artifactRemoved(al); - } + + for(auto artWin : GH.windows().findInts()) + artWin->artifactRemoved(al); waitWhileDialog(); } @@ -1834,12 +1823,9 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact redraw = false; } - for(auto isa : GH.windows().listInt) - { - auto artWin = dynamic_cast(isa.get()); - if (artWin) - artWin->artifactMoved(src, dst, redraw); - } + for(auto artWin : GH.windows().findInts()) + artWin->artifactMoved(src, dst, redraw); + waitWhileDialog(); } @@ -1853,12 +1839,9 @@ void CPlayerInterface::artifactAssembled(const ArtifactLocation &al) EVENT_HANDLER_CALLED_BY_CLIENT; auto hero = std::visit(HeroObjectRetriever(), al.artHolder); adventureInt->onHeroChanged(hero); - for(auto isa : GH.windows().listInt) - { - auto artWin = dynamic_cast(isa.get()); - if (artWin) - artWin->artifactAssembled(al); - } + + for(auto artWin : GH.windows().findInts()) + artWin->artifactAssembled(al); } void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) @@ -1866,12 +1849,9 @@ void CPlayerInterface::artifactDisassembled(const ArtifactLocation &al) EVENT_HANDLER_CALLED_BY_CLIENT; auto hero = std::visit(HeroObjectRetriever(), al.artHolder); adventureInt->onHeroChanged(hero); - for(auto isa : GH.windows().listInt) - { - auto artWin = dynamic_cast(isa.get()); - if (artWin) - artWin->artifactDisassembled(al); - } + + for(auto artWin : GH.windows().findInts()) + artWin->artifactDisassembled(al); } void CPlayerInterface::waitForAllDialogs(bool unlockPim) diff --git a/client/Client.cpp b/client/Client.cpp index 9bcb93624..5003361f0 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -770,8 +770,7 @@ void CClient::removeGUI() if(GH.windows().topInt()) GH.windows().topInt()->deactivate(); adventureInt.reset(); - GH.windows().listInt.clear(); - GH.windows().objsToBlit.clear(); + GH.windows().clear(); GH.statusbar.reset(); logGlobal->info("Removed GUI."); diff --git a/client/ClientCommandManager.cpp b/client/ClientCommandManager.cpp index 6d12e7db0..aec9b8a3d 100644 --- a/client/ClientCommandManager.cpp +++ b/client/ClientCommandManager.cpp @@ -196,14 +196,8 @@ void ClientCommandManager::handleNotDialogCommand() void ClientCommandManager::handleGuiCommand() { - for(const auto & child : GH.windows().listInt) - { - const auto childPtr = child.get(); - if(const CIntObject * obj = dynamic_cast(childPtr)) - printInfoAboutInterfaceObject(obj, 0); - else - printCommandMessage(std::string(typeid(childPtr).name()) + "\n"); - } + for(const auto & child : GH.windows().findInts()) + printInfoAboutInterfaceObject(child.get(), 0); } void ClientCommandManager::handleConvertTextCommand() diff --git a/client/NetPacksLobbyClient.cpp b/client/NetPacksLobbyClient.cpp index 4f2134823..5d8b37a6c 100644 --- a/client/NetPacksLobbyClient.cpp +++ b/client/NetPacksLobbyClient.cpp @@ -57,7 +57,7 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyClientDisconnected(LobbyClient void ApplyOnLobbyScreenNetPackVisitor::visitLobbyClientDisconnected(LobbyClientDisconnected & pack) { - if(GH.windows().listInt.size()) + if(GH.windows().count() > 0) GH.windows().popInts(1); } diff --git a/client/gui/WindowHandler.cpp b/client/gui/WindowHandler.cpp index f3b86b1bd..ef8eace14 100644 --- a/client/gui/WindowHandler.cpp +++ b/client/gui/WindowHandler.cpp @@ -21,30 +21,29 @@ void WindowHandler::popInt(std::shared_ptr top) { - assert(listInt.front() == top); + assert(windowsStack.back() == top); top->deactivate(); disposed.push_back(top); - listInt.pop_front(); - objsToBlit -= top; - if(!listInt.empty()) - listInt.front()->activate(); + windowsStack.pop_back(); + if(!windowsStack.empty()) + windowsStack.back()->activate(); + totalRedraw(); } void WindowHandler::pushInt(std::shared_ptr newInt) { assert(newInt); - assert(!vstd::contains(listInt, newInt)); // do not add same object twice + assert(!vstd::contains(windowsStack, newInt)); // do not add same object twice //a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway) screenBuf = screen2; - if(!listInt.empty()) - listInt.front()->deactivate(); - listInt.push_front(newInt); + if(!windowsStack.empty()) + windowsStack.back()->deactivate(); + windowsStack.push_back(newInt); CCS->curh->set(Cursor::Map::POINTER); newInt->activate(); - objsToBlit.push_back(newInt); totalRedraw(); } @@ -53,36 +52,35 @@ void WindowHandler::popInts(int howMany) if(!howMany) return; //senseless but who knows... - assert(listInt.size() >= howMany); - listInt.front()->deactivate(); + assert(windowsStack.size() >= howMany); + windowsStack.back()->deactivate(); for(int i = 0; i < howMany; i++) { - objsToBlit -= listInt.front(); - disposed.push_back(listInt.front()); - listInt.pop_front(); + disposed.push_back(windowsStack.back()); + windowsStack.pop_back(); } - if(!listInt.empty()) + if(!windowsStack.empty()) { - listInt.front()->activate(); + windowsStack.back()->activate(); totalRedraw(); } GH.fakeMouseMove(); } -std::shared_ptr WindowHandler::topInt() +std::shared_ptr WindowHandler::topInt() const { - if(listInt.empty()) - return std::shared_ptr(); - else - return listInt.front(); + if(windowsStack.empty()) + return nullptr; + + return windowsStack.back(); } void WindowHandler::totalRedraw() { CSDL_Ext::fillSurface(screen2, Colors::BLACK); - for(auto & elem : objsToBlit) + for(auto & elem : windowsStack) elem->showAll(screen2); CSDL_Ext::blitAt(screen2, 0, 0, screen); } @@ -90,22 +88,21 @@ void WindowHandler::totalRedraw() void WindowHandler::simpleRedraw() { //update only top interface and draw background - if(objsToBlit.size() > 1) + if(windowsStack.size() > 1) CSDL_Ext::blitAt(screen2, 0, 0, screen); //blit background - if(!objsToBlit.empty()) - objsToBlit.back()->show(screen); //blit active interface/window + if(!windowsStack.empty()) + windowsStack.back()->show(screen); //blit active interface/window } void WindowHandler::onScreenResize() { - for(const auto & entry : listInt) + for(const auto & entry : windowsStack) { auto intObject = std::dynamic_pointer_cast(entry); if(intObject) intObject->onScreenResize(); } - totalRedraw(); } @@ -113,3 +110,14 @@ void WindowHandler::onFrameRendered() { disposed.clear(); } + +size_t WindowHandler::count() const +{ + return windowsStack.size(); +} + +void WindowHandler::clear() +{ + windowsStack.clear(); + disposed.clear(); +} diff --git a/client/gui/WindowHandler.h b/client/gui/WindowHandler.h index bc9d930fa..38443cfe3 100644 --- a/client/gui/WindowHandler.h +++ b/client/gui/WindowHandler.h @@ -13,15 +13,14 @@ class IShowActivatable; class WindowHandler { -public: - /// list of interfaces - front=foreground; back = background (includes adventure map, window interfaces, all kind of active dialogs, and so on) - std::list> listInt; + /// list of interfaces. front = bottom-most (background), back = top-most (foreground) + /// (includes adventure map, window interfaces, all kind of active dialogs, and so on) + std::vector> windowsStack; + /// Temporary list of recently popped windows std::vector> disposed; - // objs to blit - std::vector> objsToBlit; - +public: /// forces total redraw (using showAll), sets a flag, method gets called at the end of the rendering void totalRedraw(); @@ -33,12 +32,10 @@ public: /// deactivate old top interface, activates this one and pushes to the top void pushInt(std::shared_ptr newInt); + + /// creates window of class T and pushes it to the top template - void pushIntT(Args && ... args) - { - auto newInt = std::make_shared(std::forward(args)...); - pushInt(newInt); - } + void pushIntT(Args && ... args); /// pops one or more interfaces - deactivates top, deletes and removes given number of interfaces, activates new front void popInts(int howMany); @@ -47,8 +44,41 @@ public: void popInt(std::shared_ptr top); /// returns top interface - std::shared_ptr topInt(); - + std::shared_ptr topInt() const; + /// should be called after frame has been rendered to screen void onFrameRendered(); + + /// returns current number of windows in the stack + size_t count() const; + + /// erases all currently existing windows from the stacl + void clear(); + + /// returns all existing windows of selected type + template + std::vector> findInts() const; }; + +template +void WindowHandler::pushIntT(Args && ... args) +{ + auto newInt = std::make_shared(std::forward(args)...); + pushInt(newInt); +} + +template +std::vector> WindowHandler::findInts() const +{ + std::vector> result; + + for (auto const & window : windowsStack) + { + std::shared_ptr casted = std::dynamic_pointer_cast(window); + + if (casted) + result.push_back(casted); + } + + return result; +} diff --git a/client/mainmenu/CMainMenu.cpp b/client/mainmenu/CMainMenu.cpp index a6574a73a..051136f0f 100644 --- a/client/mainmenu/CMainMenu.cpp +++ b/client/mainmenu/CMainMenu.cpp @@ -325,7 +325,7 @@ void CMainMenu::update() if(CMM != this->shared_from_this()) //don't update if you are not a main interface return; - if(GH.windows().listInt.empty()) + if(GH.windows().count() == 0) { GH.windows().pushInt(CMM); GH.windows().pushInt(menu); @@ -523,7 +523,7 @@ void CSimpleJoinScreen::leaveScreen() textTitle->setText("Closing..."); CSH->state = EClientState::CONNECTION_CANCELLED; } - else if(GH.windows().listInt.size() && GH.windows().listInt.front().get() == this) + else if(GH.windows().topInt().get() == this) { close(); } @@ -553,7 +553,7 @@ void CSimpleJoinScreen::connectThread(const std::string & addr, ui16 port) else CSH->justConnectToServer(addr, port); - if(GH.windows().listInt.size() && GH.windows().listInt.front().get() == this) + if(GH.windows().topInt().get() == this) { close(); } diff --git a/client/renderSDL/ScreenHandler.cpp b/client/renderSDL/ScreenHandler.cpp index 1dc7bbeac..d1e62cff9 100644 --- a/client/renderSDL/ScreenHandler.cpp +++ b/client/renderSDL/ScreenHandler.cpp @@ -271,7 +271,7 @@ void ScreenHandler::initializeScreenBuffers() throw std::runtime_error("Unable to copy surface\n"); } - if (GH.windows().listInt.size() > 1) + if (GH.windows().count() > 1) screenBuf = screen2; else screenBuf = screen; diff --git a/client/windows/CHeroWindow.cpp b/client/windows/CHeroWindow.cpp index fae90dea5..b7a713509 100644 --- a/client/windows/CHeroWindow.cpp +++ b/client/windows/CHeroWindow.cpp @@ -314,18 +314,17 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded) //if we have exchange window with this curHero open bool noDismiss=false; - for(auto isa : GH.windows().listInt) - { - if(CExchangeWindow * cew = dynamic_cast(isa.get())) - { - for(int g=0; g < cew->heroInst.size(); ++g) - if(cew->heroInst[g] == curHero) - noDismiss = true; - } - if(dynamic_cast(isa.get())) - noDismiss = true; + for(auto cew : GH.windows().findInts()) + { + for(int g=0; g < cew->heroInst.size(); ++g) + if(cew->heroInst[g] == curHero) + noDismiss = true; } + + for(auto ki : GH.windows().findInts()) + noDismiss = true; + //if player only have one hero and no towns if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1) noDismiss = true;