diff --git a/Client/core/CGUI.cpp b/Client/core/CGUI.cpp index 663376601f..486afc1e59 100644 --- a/Client/core/CGUI.cpp +++ b/Client/core/CGUI.cpp @@ -134,6 +134,10 @@ void CLocalGUI::ChangeLocale(const char* szName) m_LastLocaleName = strCanonicalLocale; + // Attempt CEGUI cleanup + if (CGUI* pGUI = CCore::GetSingleton().GetGUI()) + pGUI->Cleanup(); + if (guiWasLoaded) { CreateWindows(guiWasLoaded); diff --git a/Client/core/CLocalization.cpp b/Client/core/CLocalization.cpp index 5f2f271de0..709d49f2dd 100644 --- a/Client/core/CLocalization.cpp +++ b/Client/core/CLocalization.cpp @@ -41,10 +41,7 @@ CLocalization::CLocalization(const SString& strLocale, const SString& strLocaleP CLocalization::~CLocalization() { - for (auto iter : m_LanguageMap) - { - delete iter.second; - } + m_pCurrentLang = nullptr; } // @@ -99,29 +96,32 @@ void CLocalization::SetCurrentLanguage(SString strLocale) CLanguage* CLocalization::GetLanguage(SString strLocale) { strLocale = ValidateLocale(strLocale); - CLanguage* pLanguage = MapFindRef(m_LanguageMap, strLocale); - if (!pLanguage) + auto iter = m_LanguageMap.find(strLocale); + if (iter != m_LanguageMap.end()) + { + return iter->second.get(); + } + + Language Lang = Language::from_name(strLocale); + Lang = Lang ? Lang : Language::from_name("en_US"); + + try + { + std::unique_ptr pLanguage = std::make_unique(m_DictManager.get_dictionary(Lang, MTA_LOCALE_TEXTDOMAIN), Lang.str(), Lang.get_name()); + CLanguage* pLanguagePtr = pLanguage.get(); + m_LanguageMap.emplace(strLocale, std::move(pLanguage)); + return pLanguagePtr; + } + catch (const std::exception& ex) + { + WriteDebugEvent(SString("Localization failed to load dictionary for '%s': %s", strLocale.c_str(), ex.what())); + return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr; + } + catch (...) { - Language Lang = Language::from_name(strLocale); - Lang = Lang ? Lang : Language::from_name("en_US"); - - try - { - pLanguage = new CLanguage(m_DictManager.get_dictionary(Lang, MTA_LOCALE_TEXTDOMAIN), Lang.str(), Lang.get_name()); - MapSet(m_LanguageMap, strLocale, pLanguage); - } - catch (const std::exception& ex) - { - WriteDebugEvent(SString("Localization failed to load dictionary for '%s': %s", strLocale.c_str(), ex.what())); - return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr; - } - catch (...) - { - WriteDebugEvent(SString("Localization failed to load dictionary for '%s': unknown error", strLocale.c_str())); - return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr; - } + WriteDebugEvent(SString("Localization failed to load dictionary for '%s': unknown error", strLocale.c_str())); + return (strLocale != "en_US") ? GetLanguage("en_US") : nullptr; } - return pLanguage; } // diff --git a/Client/core/CLocalization.h b/Client/core/CLocalization.h index 58e49b58dc..6e5b46b579 100644 --- a/Client/core/CLocalization.h +++ b/Client/core/CLocalization.h @@ -13,7 +13,8 @@ using namespace tinygettext; #include #include "CLanguage.h" -#define MTA_LOCALE_DIR "MTA/locale/" +#include +#define MTA_LOCALE_DIR "MTA/locale/" #pragma once @@ -42,7 +43,7 @@ class CLocalization : public CLocalizationInterface static void LogCallback(const std::string& str); private: - DictionaryManager m_DictManager; - std::map m_LanguageMap; - CLanguage* m_pCurrentLang{}; + DictionaryManager m_DictManager; + std::map> m_LanguageMap; + CLanguage* m_pCurrentLang{}; }; diff --git a/Client/gui/CGUI_Impl.cpp b/Client/gui/CGUI_Impl.cpp index c05853ea4a..a2c981632d 100644 --- a/Client/gui/CGUI_Impl.cpp +++ b/Client/gui/CGUI_Impl.cpp @@ -144,6 +144,16 @@ CGUI_Impl::~CGUI_Impl() // DO NOT delete m_pRenderer - it's already deleted by System destructor } +void CGUI_Impl::CreateRootWindow() +{ + if (!m_pWindowManager || !m_pSystem) + return; + + // Create dummy GUI root + m_pTop = reinterpret_cast(m_pWindowManager->createWindow("DefaultWindow", "guiroot")); + m_pSystem->setGUISheet(m_pTop); +} + void CGUI_Impl::SetSkin(const char* szName) { if (m_HasSchemeLoaded) @@ -162,12 +172,8 @@ void CGUI_Impl::SetSkin(const char* szName) CEGUI::System::getSingleton().setDefaultMouseCursor("CGUI-Images", "MouseArrow"); - // Destroy any windows we already have - CEGUI::WindowManager::getSingleton().destroyAllWindows(); - - // Create dummy GUI root - m_pTop = reinterpret_cast(m_pWindowManager->createWindow("DefaultWindow", "guiroot")); - m_pSystem->setGUISheet(m_pTop); + // Clean up CEGUI - this also re-creates the root window + Cleanup(); // Disable single click timeouts m_pSystem->setSingleClickTimeout(100000000.0f); @@ -327,28 +333,31 @@ bool CGUI_Impl::GetGUIInputEnabled() break; case INPUTMODE_NO_BINDS_ON_EDIT: { - CEGUI::Window* pActiveWindow = m_pTop->getActiveChild(); - if (!pActiveWindow || pActiveWindow == m_pTop || !pActiveWindow->isVisible()) - { - return false; - } - if (pActiveWindow->getType() == "CGUI/Editbox") - { - CEGUI::Editbox* pEditBox = reinterpret_cast(pActiveWindow); - return (!pEditBox->isReadOnly() && pEditBox->hasInputFocus()); - } - else if (pActiveWindow->getType() == "CGUI/MultiLineEditbox") - { - CEGUI::MultiLineEditbox* pMultiLineEditBox = reinterpret_cast(pActiveWindow); - return (!pMultiLineEditBox->isReadOnly() && pMultiLineEditBox->hasInputFocus()); - } - else if (pActiveWindow->getType() == CGUIWEBBROWSER_NAME) + if (m_pTop) { - auto pElement = reinterpret_cast(pActiveWindow->getUserData()); - if (pElement->GetType() == CGUI_WEBBROWSER) + CEGUI::Window* pActiveWindow = m_pTop->getActiveChild(); + if (!pActiveWindow || pActiveWindow == m_pTop || !pActiveWindow->isVisible()) + { + return false; + } + if (pActiveWindow->getType() == "CGUI/Editbox") + { + CEGUI::Editbox* pEditBox = reinterpret_cast(pActiveWindow); + return (!pEditBox->isReadOnly() && pEditBox->hasInputFocus()); + } + else if (pActiveWindow->getType() == "CGUI/MultiLineEditbox") + { + CEGUI::MultiLineEditbox* pMultiLineEditBox = reinterpret_cast(pActiveWindow); + return (!pMultiLineEditBox->isReadOnly() && pMultiLineEditBox->hasInputFocus()); + } + else if (pActiveWindow->getType() == CGUIWEBBROWSER_NAME) { - auto pWebBrowser = reinterpret_cast(pElement); - return pWebBrowser->HasInputFocus(); + auto pElement = reinterpret_cast(pActiveWindow->getUserData()); + if (pElement->GetType() == CGUI_WEBBROWSER) + { + auto pWebBrowser = reinterpret_cast(pElement); + return pWebBrowser->HasInputFocus(); + } } } return false; @@ -580,6 +589,9 @@ eCursorType CGUI_Impl::GetCursorType() void CGUI_Impl::AddChild(CGUIElement_Impl* pChild) { + if (!m_pTop) + return; + m_pTop->addChildWindow(pChild->GetWindow()); } @@ -1153,12 +1165,15 @@ bool CGUI_Impl::Event_MouseButtonDown(const CEGUI::EventArgs& Args) pElement->Event_OnMouseButtonDown(); else { - // If there's no element, we're probably dealing with the root element - CEGUI::Window* pActiveWindow = m_pTop->getActiveChild(); - if (m_pTop == wnd && pActiveWindow) + if (m_pTop) { - // Deactivate active window to trigger onClientGUIBlur - pActiveWindow->deactivate(); + // If there's no element, we're probably dealing with the root element + CEGUI::Window* pActiveWindow = m_pTop->getActiveChild(); + if (m_pTop == wnd && pActiveWindow) + { + // Deactivate active window to trigger onClientGUIBlur + pActiveWindow->deactivate(); + } } } @@ -1797,3 +1812,33 @@ CEGUI::Window* CGUI_Impl::GetMasterWindow(CEGUI::Window* wnd) } return wnd; } + +void CGUI_Impl::Cleanup() +{ + try + { + CleanDeadPool(); + + m_pTop = nullptr; + + if (m_pWindowManager) + m_pWindowManager->destroyAllWindows(); + + // Clear redraw structures that may reference old elements + m_RedrawQueue.clear(); + m_RedrawRegistry.clear(); + + // Recreate the root window (destroyed above via destroyAllWindows) + CreateRootWindow(); + } + catch (const std::exception& e) + { + WriteDebugEvent(SString("CGUI_Impl::Cleanup - Exception: %s", e.what())); + m_pTop = nullptr; + } + catch (...) + { + WriteDebugEvent("CGUI_Impl::Cleanup() failed with unknown exception"); + m_pTop = nullptr; + } +} diff --git a/Client/gui/CGUI_Impl.h b/Client/gui/CGUI_Impl.h index 05c30de9f7..6fe4de332d 100644 --- a/Client/gui/CGUI_Impl.h +++ b/Client/gui/CGUI_Impl.h @@ -283,6 +283,9 @@ class CGUI_Impl : public CGUI, public CGUITabList CGUIWindow* LoadLayout(CGUIElement* pParent, const SString& strFilename); bool LoadImageset(const SString& strFilename); + // Cleanup CEGUI active resources (dead pool) + void Cleanup(); + private: friend class CGUIElement_Impl; CGUIButton* _CreateButton(CGUIElement_Impl* pParent = NULL, const char* szCaption = ""); @@ -361,4 +364,6 @@ class CGUI_Impl : public CGUI, public CGUITabList bool m_HasSchemeLoaded; SString m_CurrentSchemeName; CElapsedTime m_RenderOkTimer; + + void CreateRootWindow(); }; diff --git a/Client/sdk/gui/CGUI.h b/Client/sdk/gui/CGUI.h index 217fc004c2..1b7f641226 100644 --- a/Client/sdk/gui/CGUI.h +++ b/Client/sdk/gui/CGUI.h @@ -170,4 +170,6 @@ class CGUI virtual CGUIWindow* LoadLayout(CGUIElement* pParent, const SString& strFilename) = 0; virtual bool LoadImageset(const SString& strFilename) = 0; + + virtual void Cleanup() = 0; };