#include "windowmanagerimp.hpp" #include #include #include #include #include #include #include #include #include #include #include #include // For BT_NO_PROFILE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/inputmanager.hpp" #include "../mwbase/luamanager.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/statemanager.hpp" #include "../mwbase/world.hpp" #include "../mwrender/vismask.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" #include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/npcstats.hpp" #include "../mwrender/postprocessor.hpp" #include "alchemywindow.hpp" #include "backgroundimage.hpp" #include "bookpage.hpp" #include "bookwindow.hpp" #include "companionwindow.hpp" #include "confirmationdialog.hpp" #include "console.hpp" #include "container.hpp" #include "controllers.hpp" #include "countdialog.hpp" #include "cursor.hpp" #include "debugwindow.hpp" #include "dialogue.hpp" #include "enchantingdialog.hpp" #include "exposedwindow.hpp" #include "hud.hpp" #include "inventorywindow.hpp" #include "itemchargeview.hpp" #include "itemview.hpp" #include "itemwidget.hpp" #include "jailscreen.hpp" #include "journalviewmodel.hpp" #include "journalwindow.hpp" #include "keyboardnavigation.hpp" #include "levelupdialog.hpp" #include "loadingscreen.hpp" #include "mainmenu.hpp" #include "merchantrepair.hpp" #include "postprocessorhud.hpp" #include "quickkeysmenu.hpp" #include "recharge.hpp" #include "repair.hpp" #include "resourceskin.hpp" #include "screenfader.hpp" #include "scrollwindow.hpp" #include "settingswindow.hpp" #include "spellbuyingwindow.hpp" #include "spellview.hpp" #include "spellwindow.hpp" #include "statswindow.hpp" #include "tradewindow.hpp" #include "trainingwindow.hpp" #include "travelwindow.hpp" #include "ustring.hpp" #include "videowidget.hpp" #include "waitdialog.hpp" namespace MWGui { WindowManager::WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders) : mOldUpdateMask(0) , mOldCullMask(0) , mStore(nullptr) , mResourceSystem(resourceSystem) , mWorkQueue(workQueue) , mViewer(viewer) , mConsoleOnlyScripts(consoleOnlyScripts) , mCurrentModals() , mHud(nullptr) , mMap(nullptr) , mStatsWindow(nullptr) , mConsole(nullptr) , mDialogueWindow(nullptr) , mInventoryWindow(nullptr) , mScrollWindow(nullptr) , mBookWindow(nullptr) , mCountDialog(nullptr) , mTradeWindow(nullptr) , mSettingsWindow(nullptr) , mConfirmationDialog(nullptr) , mSpellWindow(nullptr) , mQuickKeysMenu(nullptr) , mLoadingScreen(nullptr) , mWaitDialog(nullptr) , mVideoBackground(nullptr) , mVideoWidget(nullptr) , mWerewolfFader(nullptr) , mBlindnessFader(nullptr) , mHitFader(nullptr) , mScreenFader(nullptr) , mDebugWindow(nullptr) , mPostProcessorHud(nullptr) , mJailScreen(nullptr) , mContainerWindow(nullptr) , mTranslationDataStorage(translationDataStorage) , mInputBlocker(nullptr) , mCrosshairEnabled(Settings::Manager::getBool("crosshair", "HUD")) , mSubtitlesEnabled(Settings::Manager::getBool("subtitles", "GUI")) , mHitFaderEnabled(Settings::Manager::getBool("hit fader", "GUI")) , mWerewolfOverlayEnabled(Settings::Manager::getBool("werewolf overlay", "GUI")) , mHudEnabled(true) , mCursorVisible(true) , mCursorActive(true) , mPlayerBounty(-1) , mGuiModes() , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) , mAllowed(GW_ALL) , mRestAllowed(true) , mShowOwned(0) , mEncoding(encoding) , mVersionDescription(versionDescription) , mWindowVisible(true) { int w, h; SDL_GetWindowSize(window, &w, &h); int dw, dh; SDL_GL_GetDrawableSize(window, &dw, &dh); mScalingFactor = std::clamp(Settings::Manager::getFloat("scaling factor", "GUI"), 0.5f, 8.f) * (dw / w); mGuiPlatform = std::make_unique(viewer, guiRoot, resourceSystem->getImageManager(), resourceSystem->getVFS(), mScalingFactor, "mygui", logpath / "MyGUI.log"); mGui = std::make_unique(); mGui->initialise(""); createTextures(); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); // Load fonts mFontLoader = std::make_unique(encoding, resourceSystem->getVFS(), mScalingFactor); // Register own widgets with MyGUI MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Widget"); MyGUI::FactoryManager::getInstance().registerFactory("Layer"); MyGUI::FactoryManager::getInstance().registerFactory("Layer"); BookPage::registerMyGUIComponents(); PostProcessorHud::registerMyGUIComponents(); ItemView::registerComponents(); ItemChargeView::registerComponents(); ItemWidget::registerComponents(); SpellView::registerComponents(); Gui::registerAllWidgets(); LuaUi::registerAllWidgets(); MyGUI::FactoryManager::getInstance().registerFactory("Controller"); MyGUI::FactoryManager::getInstance().registerFactory( "Resource", "ResourceImageSetPointer"); MyGUI::FactoryManager::getInstance().registerFactory( "Resource", "AutoSizedResourceSkin"); MyGUI::ResourceManager::getInstance().load("core.xml"); bool keyboardNav = Settings::Manager::getBool("keyboard navigation", "GUI"); mKeyboardNavigation = std::make_unique(); mKeyboardNavigation->setEnabled(keyboardNav); Gui::ImageButton::setDefaultNeedKeyFocus(keyboardNav); auto loadingScreen = std::make_unique(mResourceSystem, mViewer); mLoadingScreen = loadingScreen.get(); mWindows.push_back(std::move(loadingScreen)); // set up the hardware cursor manager mCursorManager = std::make_unique(); MyGUI::PointerManager::getInstance().eventChangeMousePointer += MyGUI::newDelegate(this, &WindowManager::onCursorChange); MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); // Create all cursors in advance createCursors(); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); mCursorManager->setEnabled(true); // hide mygui's pointer MyGUI::PointerManager::getInstance().setVisible(false); mVideoBackground = MyGUI::Gui::getInstance().createWidgetReal( "ImageBox", 0, 0, 1, 1, MyGUI::Align::Default, "Video"); mVideoBackground->setImageTexture("black"); mVideoBackground->setVisible(false); mVideoBackground->setNeedMouseFocus(true); mVideoBackground->setNeedKeyFocus(true); mVideoWidget = mVideoBackground->createWidgetReal("ImageBox", 0, 0, 1, 1, MyGUI::Align::Default); mVideoWidget->setNeedMouseFocus(true); mVideoWidget->setNeedKeyFocus(true); mVideoWidget->setVFS(resourceSystem->getVFS()); // Removes default MyGUI system clipboard implementation, which supports windows only MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardChanged += MyGUI::newDelegate(this, &WindowManager::onClipboardChanged); MyGUI::ClipboardManager::getInstance().eventClipboardRequested += MyGUI::newDelegate(this, &WindowManager::onClipboardRequested); mShowOwned = Settings::Manager::getInt("show owned", "Game"); mVideoWrapper = std::make_unique(window, viewer); mVideoWrapper->setGammaContrast( Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video")); if (useShaders) mGuiPlatform->getRenderManagerPtr()->enableShaders(mResourceSystem->getSceneManager()->getShaderManager()); mStatsWatcher = std::make_unique(); } void WindowManager::initUI() { // Get size info from the Gui object int w = MyGUI::RenderManager::getInstance().getViewSize().width; int h = MyGUI::RenderManager::getInstance().getViewSize().height; mTextColours.loadColours(); mDragAndDrop = std::make_unique(); auto recharge = std::make_unique(); mGuiModeStates[GM_Recharge] = GuiModeState(recharge.get()); mWindows.push_back(std::move(recharge)); auto menu = std::make_unique(w, h, mResourceSystem->getVFS(), mVersionDescription); mGuiModeStates[GM_MainMenu] = GuiModeState(menu.get()); mWindows.push_back(std::move(menu)); mLocalMapRender = std::make_unique(mViewer->getSceneData()->asGroup()); auto map = std::make_unique(mCustomMarkers, mDragAndDrop.get(), mLocalMapRender.get(), mWorkQueue); mMap = map.get(); mWindows.push_back(std::move(map)); mMap->renderGlobalMap(); trackWindow(mMap, "map"); auto statsWindow = std::make_unique(mDragAndDrop.get()); mStatsWindow = statsWindow.get(); mWindows.push_back(std::move(statsWindow)); trackWindow(mStatsWindow, "stats"); auto inventoryWindow = std::make_unique( mDragAndDrop.get(), mViewer->getSceneData()->asGroup(), mResourceSystem); mInventoryWindow = inventoryWindow.get(); mWindows.push_back(std::move(inventoryWindow)); auto spellWindow = std::make_unique(mDragAndDrop.get()); mSpellWindow = spellWindow.get(); mWindows.push_back(std::move(spellWindow)); trackWindow(mSpellWindow, "spells"); mGuiModeStates[GM_Inventory] = GuiModeState({ mMap, mInventoryWindow, mSpellWindow, mStatsWindow }); mGuiModeStates[GM_None] = GuiModeState({ mMap, mInventoryWindow, mSpellWindow, mStatsWindow }); auto tradeWindow = std::make_unique(); mTradeWindow = tradeWindow.get(); mWindows.push_back(std::move(tradeWindow)); trackWindow(mTradeWindow, "barter"); mGuiModeStates[GM_Barter] = GuiModeState({ mInventoryWindow, mTradeWindow }); auto console = std::make_unique(w, h, mConsoleOnlyScripts); mConsole = console.get(); mWindows.push_back(std::move(console)); trackWindow(mConsole, "console"); bool questList = mResourceSystem->getVFS()->exists("textures/tx_menubook_options_over.dds"); auto journal = JournalWindow::create(JournalViewModel::create(), questList, mEncoding); mGuiModeStates[GM_Journal] = GuiModeState(journal.get()); mGuiModeStates[GM_Journal].mCloseSound = ESM::RefId::stringRefId("book close"); mGuiModeStates[GM_Journal].mOpenSound = ESM::RefId::stringRefId("book open"); mWindows.push_back(std::move(journal)); mMessageBoxManager = std::make_unique( mStore->get().find("fMessageTimePerChar")->mValue.getFloat()); auto spellBuyingWindow = std::make_unique(); mGuiModeStates[GM_SpellBuying] = GuiModeState(spellBuyingWindow.get()); mWindows.push_back(std::move(spellBuyingWindow)); auto travelWindow = std::make_unique(); mGuiModeStates[GM_Travel] = GuiModeState(travelWindow.get()); mWindows.push_back(std::move(travelWindow)); auto dialogueWindow = std::make_unique(); mDialogueWindow = dialogueWindow.get(); mWindows.push_back(std::move(dialogueWindow)); trackWindow(mDialogueWindow, "dialogue"); mGuiModeStates[GM_Dialogue] = GuiModeState(mDialogueWindow); mTradeWindow->eventTradeDone += MyGUI::newDelegate(mDialogueWindow, &DialogueWindow::onTradeComplete); auto containerWindow = std::make_unique(mDragAndDrop.get()); mContainerWindow = containerWindow.get(); mWindows.push_back(std::move(containerWindow)); trackWindow(mContainerWindow, "container"); mGuiModeStates[GM_Container] = GuiModeState({ mContainerWindow, mInventoryWindow }); auto hud = std::make_unique(mCustomMarkers, mDragAndDrop.get(), mLocalMapRender.get()); mHud = hud.get(); mWindows.push_back(std::move(hud)); mToolTips = std::make_unique(); auto scrollWindow = std::make_unique(); mScrollWindow = scrollWindow.get(); mWindows.push_back(std::move(scrollWindow)); mGuiModeStates[GM_Scroll] = GuiModeState(mScrollWindow); mGuiModeStates[GM_Scroll].mOpenSound = ESM::RefId::stringRefId("scroll"); mGuiModeStates[GM_Scroll].mCloseSound = ESM::RefId::stringRefId("scroll"); auto bookWindow = std::make_unique(); mBookWindow = bookWindow.get(); mWindows.push_back(std::move(bookWindow)); mGuiModeStates[GM_Book] = GuiModeState(mBookWindow); mGuiModeStates[GM_Book].mOpenSound = ESM::RefId::stringRefId("book open"); mGuiModeStates[GM_Book].mCloseSound = ESM::RefId::stringRefId("book close"); auto countDialog = std::make_unique(); mCountDialog = countDialog.get(); mWindows.push_back(std::move(countDialog)); auto settingsWindow = std::make_unique(); mSettingsWindow = settingsWindow.get(); mWindows.push_back(std::move(settingsWindow)); trackWindow(mSettingsWindow, "settings"); mGuiModeStates[GM_Settings] = GuiModeState(mSettingsWindow); auto confirmationDialog = std::make_unique(); mConfirmationDialog = confirmationDialog.get(); mWindows.push_back(std::move(confirmationDialog)); auto alchemyWindow = std::make_unique(); trackWindow(alchemyWindow.get(), "alchemy"); mGuiModeStates[GM_Alchemy] = GuiModeState(alchemyWindow.get()); mWindows.push_back(std::move(alchemyWindow)); auto quickKeysMenu = std::make_unique(); mQuickKeysMenu = quickKeysMenu.get(); mWindows.push_back(std::move(quickKeysMenu)); mGuiModeStates[GM_QuickKeysMenu] = GuiModeState(mQuickKeysMenu); auto levelupDialog = std::make_unique(); mGuiModeStates[GM_Levelup] = GuiModeState(levelupDialog.get()); mWindows.push_back(std::move(levelupDialog)); auto waitDialog = std::make_unique(); mWaitDialog = waitDialog.get(); mWindows.push_back(std::move(waitDialog)); mGuiModeStates[GM_Rest] = GuiModeState({ mWaitDialog->getProgressBar(), mWaitDialog }); auto spellCreationDialog = std::make_unique(); mGuiModeStates[GM_SpellCreation] = GuiModeState(spellCreationDialog.get()); mWindows.push_back(std::move(spellCreationDialog)); auto enchantingDialog = std::make_unique(); mGuiModeStates[GM_Enchanting] = GuiModeState(enchantingDialog.get()); mWindows.push_back(std::move(enchantingDialog)); auto trainingWindow = std::make_unique(); mGuiModeStates[GM_Training] = GuiModeState({ trainingWindow->getProgressBar(), trainingWindow.get() }); mWindows.push_back(std::move(trainingWindow)); auto merchantRepair = std::make_unique(); mGuiModeStates[GM_MerchantRepair] = GuiModeState(merchantRepair.get()); mWindows.push_back(std::move(merchantRepair)); auto repair = std::make_unique(); mGuiModeStates[GM_Repair] = GuiModeState(repair.get()); mWindows.push_back(std::move(repair)); mSoulgemDialog = std::make_unique(mMessageBoxManager.get()); auto companionWindow = std::make_unique(mDragAndDrop.get(), mMessageBoxManager.get()); trackWindow(companionWindow.get(), "companion"); mGuiModeStates[GM_Companion] = GuiModeState({ mInventoryWindow, companionWindow.get() }); mWindows.push_back(std::move(companionWindow)); auto jailScreen = std::make_unique(); mJailScreen = jailScreen.get(); mWindows.push_back(std::move(jailScreen)); mGuiModeStates[GM_Jail] = GuiModeState(mJailScreen); std::string werewolfFaderTex = "textures\\werewolfoverlay.dds"; if (mResourceSystem->getVFS()->exists(werewolfFaderTex)) { auto werewolfFader = std::make_unique(werewolfFaderTex); mWerewolfFader = werewolfFader.get(); mWindows.push_back(std::move(werewolfFader)); } auto blindnessFader = std::make_unique("black"); mBlindnessFader = blindnessFader.get(); mWindows.push_back(std::move(blindnessFader)); // fall back to player_hit_01.dds if bm_player_hit_01.dds is not available std::string hitFaderTexture = "textures\\bm_player_hit_01.dds"; const std::string hitFaderLayout = "openmw_screen_fader_hit.layout"; MyGUI::FloatCoord hitFaderCoord(0, 0, 1, 1); if (!mResourceSystem->getVFS()->exists(hitFaderTexture)) { hitFaderTexture = "textures\\player_hit_01.dds"; hitFaderCoord = MyGUI::FloatCoord(0.2, 0.25, 0.6, 0.5); } auto hitFader = std::make_unique(hitFaderTexture, hitFaderLayout, hitFaderCoord); mHitFader = hitFader.get(); mWindows.push_back(std::move(hitFader)); auto screenFader = std::make_unique("black"); mScreenFader = screenFader.get(); mWindows.push_back(std::move(screenFader)); auto debugWindow = std::make_unique(); mDebugWindow = debugWindow.get(); mWindows.push_back(std::move(debugWindow)); auto postProcessorHud = std::make_unique(); mPostProcessorHud = postProcessorHud.get(); mWindows.push_back(std::move(postProcessorHud)); trackWindow(mPostProcessorHud, "postprocessor"); mInputBlocker = MyGUI::Gui::getInstance().createWidget( "", 0, 0, w, h, MyGUI::Align::Stretch, "InputBlocker"); mHud->setVisible(true); mCharGen = std::make_unique(mViewer->getSceneData()->asGroup(), mResourceSystem); updatePinnedWindows(); // Set up visibility updateVisible(); mStatsWatcher->addListener(mHud); mStatsWatcher->addListener(mStatsWindow); mStatsWatcher->addListener(mCharGen.get()); } int WindowManager::getFontHeight() const { return mFontLoader->getFontHeight(); } void WindowManager::setNewGame(bool newgame) { if (newgame) { disallowAll(); mStatsWatcher->removeListener(mCharGen.get()); mCharGen = std::make_unique(mViewer->getSceneData()->asGroup(), mResourceSystem); mStatsWatcher->addListener(mCharGen.get()); } else allow(GW_ALL); mStatsWatcher->forceUpdate(); } WindowManager::~WindowManager() { try { LuaUi::clearUserInterface(); mStatsWatcher.reset(); MyGUI::LanguageManager::getInstance().eventRequestTag.clear(); MyGUI::PointerManager::getInstance().eventChangeMousePointer.clear(); MyGUI::InputManager::getInstance().eventChangeKeyFocus.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardChanged.clear(); MyGUI::ClipboardManager::getInstance().eventClipboardRequested.clear(); mWindows.clear(); mMessageBoxManager.reset(); mToolTips.reset(); mCharGen.reset(); mKeyboardNavigation.reset(); cleanupGarbage(); mFontLoader.reset(); mGui->shutdown(); mGuiPlatform->shutdown(); } catch (const MyGUI::Exception& e) { Log(Debug::Error) << "Error in the destructor: " << e.what(); } } void WindowManager::setStore(const MWWorld::ESMStore& store) { mStore = &store; } void WindowManager::cleanupGarbage() { // Delete any dialogs which are no longer in use mGarbageDialogs.clear(); } void WindowManager::enableScene(bool enable) { unsigned int disablemask = MWRender::Mask_GUI | MWRender::Mask_PreCompile; if (!enable && getCullMask() != disablemask) { mOldUpdateMask = mViewer->getUpdateVisitor()->getTraversalMask(); mOldCullMask = getCullMask(); mViewer->getUpdateVisitor()->setTraversalMask(disablemask); setCullMask(disablemask); } else if (enable && getCullMask() == disablemask) { mViewer->getUpdateVisitor()->setTraversalMask(mOldUpdateMask); setCullMask(mOldCullMask); } } void WindowManager::updateConsoleObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr) { mConsole->updateSelectedObjectPtr(currentPtr, newPtr); } void WindowManager::updateVisible() { bool loading = (getMode() == GM_Loading || getMode() == GM_LoadingWallpaper); bool mainmenucover = containsMode(GM_MainMenu) && MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame; enableScene(!loading && !mainmenucover); if (!mMap) return; // UI not created yet mHud->setVisible(mHudEnabled && !loading); mToolTips->setVisible(mHudEnabled && !loading); bool gameMode = !isGuiMode(); MWBase::Environment::get().getInputManager()->changeInputMode(!gameMode); mInputBlocker->setVisible(gameMode); if (loading) setCursorVisible(mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); else setCursorVisible(!gameMode); if (gameMode) setKeyFocusWidget(nullptr); // Icons of forced hidden windows are displayed setMinimapVisibility((mAllowed & GW_Map) && (!mMap->pinned() || (mForceHidden & GW_Map))); setWeaponVisibility( (mAllowed & GW_Inventory) && (!mInventoryWindow->pinned() || (mForceHidden & GW_Inventory))); setSpellVisibility((mAllowed & GW_Magic) && (!mSpellWindow->pinned() || (mForceHidden & GW_Magic))); setHMSVisibility((mAllowed & GW_Stats) && (!mStatsWindow->pinned() || (mForceHidden & GW_Stats))); mInventoryWindow->setGuiMode(getMode()); // If in game mode (or interactive messagebox), show the pinned windows if (mGuiModes.empty()) { mMap->setVisible(mMap->pinned() && !isConsoleMode() && !(mForceHidden & GW_Map) && (mAllowed & GW_Map)); mStatsWindow->setVisible( mStatsWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Stats) && (mAllowed & GW_Stats)); mInventoryWindow->setVisible(mInventoryWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Inventory) && (mAllowed & GW_Inventory)); mSpellWindow->setVisible( mSpellWindow->pinned() && !isConsoleMode() && !(mForceHidden & GW_Magic) && (mAllowed & GW_Magic)); return; } else if (getMode() != GM_Inventory) { mMap->setVisible(false); mStatsWindow->setVisible(false); mSpellWindow->setVisible(false); mHud->setDrowningBarVisible(false); mInventoryWindow->setVisible( getMode() == GM_Container || getMode() == GM_Barter || getMode() == GM_Companion); } GuiMode mode = mGuiModes.back(); mInventoryWindow->setTrading(mode == GM_Barter); if (getMode() == GM_Inventory) { // For the inventory mode, compute the effective set of windows to show. // This is controlled both by what windows the // user has opened/closed (the 'shown' variable) and by what // windows we are allowed to show (the 'allowed' var.) int eff = mShown & mAllowed & ~mForceHidden; mMap->setVisible(eff & GW_Map); mInventoryWindow->setVisible(eff & GW_Inventory); mSpellWindow->setVisible(eff & GW_Magic); mStatsWindow->setVisible(eff & GW_Stats); } switch (mode) { // FIXME: refactor chargen windows to use modes properly (or not use them at all) case GM_Name: case GM_Race: case GM_Class: case GM_ClassPick: case GM_ClassCreate: case GM_Birth: case GM_ClassGenerate: case GM_Review: mCharGen->spawnDialog(mode); break; default: break; } } void WindowManager::setDrowningTimeLeft(float time, float maxTime) { mHud->setDrowningTimeLeft(time, maxTime); } void WindowManager::removeDialog(std::unique_ptr&& dialog) { if (!dialog) return; dialog->setVisible(false); mGarbageDialogs.push_back(std::move(dialog)); } void WindowManager::exitCurrentGuiMode() { if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop) { mDragAndDrop->finish(); return; } GuiModeState& state = mGuiModeStates[mGuiModes.back()]; for (const auto& window : state.mWindows) { if (!window->exit()) { // unable to exit window, but give access to main menu if (!MyGUI::InputManager::getInstance().isModalAny() && getMode() != GM_MainMenu) pushGuiMode(GM_MainMenu); return; } } popGuiMode(); } void WindowManager::interactiveMessageBox( std::string_view message, const std::vector& buttons, bool block) { mMessageBoxManager->createInteractiveMessageBox(message, buttons); updateVisible(); if (block) { Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit()); while (mMessageBoxManager->readPressedButton(false) == -1 && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { const double dt = std::chrono::duration_cast>(frameRateLimiter.getLastFrameDuration()) .count(); mKeyboardNavigation->onFrame(); mMessageBoxManager->onFrame(dt); MWBase::Environment::get().getInputManager()->update(dt, true, false); if (!mWindowVisible) std::this_thread::sleep_for(std::chrono::milliseconds(5)); else { mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); } // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); frameRateLimiter.limit(); } } } void WindowManager::messageBox(std::string_view message, enum MWGui::ShowInDialogueMode showInDialogueMode) { if (getMode() == GM_Dialogue && showInDialogueMode != MWGui::ShowInDialogueMode_Never) { mDialogueWindow->addMessageBox(MyGUI::LanguageManager::getInstance().replaceTags(toUString(message))); } else if (showInDialogueMode != MWGui::ShowInDialogueMode_Only) { mMessageBoxManager->createMessageBox(message); } } void WindowManager::scheduleMessageBox(std::string message, enum MWGui::ShowInDialogueMode showInDialogueMode) { mScheduledMessageBoxes.lock()->emplace_back(std::move(message), showInDialogueMode); } void WindowManager::staticMessageBox(std::string_view message) { mMessageBoxManager->createMessageBox(message, true); } void WindowManager::removeStaticMessageBox() { mMessageBoxManager->removeStaticMessageBox(); } int WindowManager::readPressedButton() { return mMessageBoxManager->readPressedButton(); } std::string_view WindowManager::getGameSettingString(std::string_view id, std::string_view default_) { const ESM::GameSetting* setting = mStore->get().search(id); if (setting && setting->mValue.getType() == ESM::VT_String) return setting->mValue.getString(); return default_; } void WindowManager::updateMap() { if (!mLocalMapRender) return; MWWorld::ConstPtr player = MWMechanics::getPlayer(); osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3(); osg::Quat playerOrientation(-player.getRefData().getPosition().rot[2], osg::Vec3(0, 0, 1)); osg::Vec3f playerdirection; int x, y; float u, v; mLocalMapRender->updatePlayer(playerPosition, playerOrientation, u, v, x, y, playerdirection); if (!player.getCell()->isExterior()) { setActiveMap(x, y, true); } // else: need to know the current grid center, call setActiveMap from changeCell mMap->setPlayerDir(playerdirection.x(), playerdirection.y()); mMap->setPlayerPos(x, y, u, v); mHud->setPlayerDir(playerdirection.x(), playerdirection.y()); mHud->setPlayerPos(x, y, u, v); } void WindowManager::update(float frameDuration) { handleScheduledMessageBoxes(); bool gameRunning = MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_NoGame; if (gameRunning) updateMap(); if (!mGuiModes.empty()) { GuiModeState& state = mGuiModeStates[mGuiModes.back()]; for (WindowBase* window : state.mWindows) window->onFrame(frameDuration); } else { // update pinned windows if visible for (WindowBase* window : mGuiModeStates[GM_Inventory].mWindows) if (window->isVisible()) window->onFrame(frameDuration); } // Make sure message boxes are always in front // This is an awful workaround for a series of awfully interwoven issues that couldn't be worked around // in a better way because of an impressive number of even more awfully interwoven issues. if (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox() && mCurrentModals.back() != mMessageBoxManager->getInteractiveMessageBox()) { std::vector::iterator found = std::find( mCurrentModals.begin(), mCurrentModals.end(), mMessageBoxManager->getInteractiveMessageBox()); if (found != mCurrentModals.end()) { WindowModal* msgbox = *found; std::swap(*found, mCurrentModals.back()); MyGUI::InputManager::getInstance().addWidgetModal(msgbox->mMainWidget); mKeyboardNavigation->setModalWindow(msgbox->mMainWidget); mKeyboardNavigation->setDefaultFocus(msgbox->mMainWidget, msgbox->getDefaultKeyFocus()); } } if (!mCurrentModals.empty()) mCurrentModals.back()->onFrame(frameDuration); mKeyboardNavigation->onFrame(); if (mMessageBoxManager) mMessageBoxManager->onFrame(frameDuration); mToolTips->onFrame(frameDuration); if (mLocalMapRender) mLocalMapRender->cleanupCameras(); mDebugWindow->onFrame(frameDuration); if (!gameRunning) return; // We should display message about crime only once per frame, even if there are several crimes. // Otherwise we will get message spam when stealing several items via Take All button. const MWWorld::Ptr player = MWMechanics::getPlayer(); const MWWorld::Class& playerCls = player.getClass(); int currentBounty = playerCls.getNpcStats(player).getBounty(); if (currentBounty != mPlayerBounty) { if (mPlayerBounty >= 0 && currentBounty > mPlayerBounty) messageBox("#{sCrimeMessage}"); mPlayerBounty = currentBounty; } MWBase::LuaManager::ActorControls* playerControls = MWBase::Environment::get().getLuaManager()->getActorControls(player); bool triedToMove = playerControls && (playerControls->mMovement != 0 || playerControls->mSideMovement != 0 || playerControls->mJump); if (triedToMove && playerCls.getEncumbrance(player) > playerCls.getCapacity(player)) { const auto& msgboxs = mMessageBoxManager->getActiveMessageBoxes(); auto it = std::find_if(msgboxs.begin(), msgboxs.end(), [](const std::unique_ptr& msgbox) { return (msgbox->getMessage() == "#{sNotifyMessage59}"); }); // if an overencumbered messagebox is already present, reset its expiry timer, // otherwise create a new one. if (it != msgboxs.end()) (*it)->mCurrentTime = 0; else messageBox("#{sNotifyMessage59}"); } mDragAndDrop->onFrame(); mHud->onFrame(frameDuration); mPostProcessorHud->onFrame(frameDuration); if (mCharGen) mCharGen->onFrame(frameDuration); updateActivatedQuickKey(); mStatsWatcher->update(); cleanupGarbage(); } void WindowManager::changeCell(const MWWorld::CellStore* cell) { mMap->requestMapRender(cell); std::string name{ MWBase::Environment::get().getWorld()->getCellName(cell) }; mMap->setCellName(name); mHud->setCellName(name); auto cellCommon = cell->getCell(); if (cellCommon->isExterior()) { if (!cellCommon->getEditorName().empty()) mMap->addVisitedLocation(name, cellCommon->getGridX(), cellCommon->getGridY()); mMap->cellExplored(cellCommon->getGridX(), cellCommon->getGridY()); setActiveMap(cellCommon->getGridX(), cellCommon->getGridY(), false); } else { mMap->setCellPrefix(std::string(cellCommon->getEditorName())); mHud->setCellPrefix(std::string(cellCommon->getEditorName())); osg::Vec3f worldPos; if (!MWBase::Environment::get().getWorld()->findInteriorPositionInWorldSpace(cell, worldPos)) worldPos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition(); else MWBase::Environment::get().getWorld()->getPlayer().setLastKnownExteriorPosition(worldPos); mMap->setGlobalMapPlayerPosition(worldPos.x(), worldPos.y()); setActiveMap(0, 0, true); } } void WindowManager::setActiveMap(int x, int y, bool interior) { mMap->setActiveCell(x, y, interior); mHud->setActiveCell(x, y, interior); } void WindowManager::setDrowningBarVisibility(bool visible) { mHud->setDrowningBarVisible(visible); } void WindowManager::setHMSVisibility(bool visible) { mHud->setHmsVisible(visible); } void WindowManager::setMinimapVisibility(bool visible) { mHud->setMinimapVisible(visible); } bool WindowManager::toggleFogOfWar() { mMap->toggleFogOfWar(); return mHud->toggleFogOfWar(); } void WindowManager::setFocusObject(const MWWorld::Ptr& focus) { mToolTips->setFocusObject(focus); if (mHud && (mShowOwned == 2 || mShowOwned == 3)) { bool owned = mToolTips->checkOwned(); mHud->setCrosshairOwned(owned); } } void WindowManager::setFocusObjectScreenCoords(float min_x, float min_y, float max_x, float max_y) { mToolTips->setFocusObjectScreenCoords(min_x, min_y, max_x, max_y); } bool WindowManager::toggleFullHelp() { return mToolTips->toggleFullHelp(); } bool WindowManager::getFullHelp() const { return mToolTips->getFullHelp(); } void WindowManager::setWeaponVisibility(bool visible) { mHud->setWeapVisible(visible); } void WindowManager::setSpellVisibility(bool visible) { mHud->setSpellVisible(visible); mHud->setEffectVisible(visible); } void WindowManager::setSneakVisibility(bool visible) { mHud->setSneakVisible(visible); } void WindowManager::setDragDrop(bool dragDrop) { mToolTips->setEnabled(!dragDrop); MWBase::Environment::get().getInputManager()->setDragDrop(dragDrop); } void WindowManager::setCursorVisible(bool visible) { mCursorVisible = visible; } void WindowManager::setCursorActive(bool active) { mCursorActive = active; } void WindowManager::onRetrieveTag(const MyGUI::UString& _tag, MyGUI::UString& _result) { std::string tag(_tag); std::string MyGuiPrefix = "setting="; size_t MyGuiPrefixLength = MyGuiPrefix.length(); std::string tokenToFind = "sCell="; size_t tokenLength = tokenToFind.length(); if (tag.compare(0, MyGuiPrefixLength, MyGuiPrefix) == 0) { tag = tag.substr(MyGuiPrefixLength, tag.length()); size_t comma_pos = tag.find(','); std::string settingSection = tag.substr(0, comma_pos); std::string settingTag = tag.substr(comma_pos + 1, tag.length()); _result = Settings::Manager::getString(settingTag, settingSection); } else if (tag.compare(0, tokenLength, tokenToFind) == 0) { _result = mTranslationDataStorage.translateCellName(tag.substr(tokenLength)); _result = MyGUI::TextIterator::toTagsString(_result); } else if (Gui::replaceTag(tag, _result)) { return; } else { std::vector split; Misc::StringUtils::split(tag, split, ":"); l10n::Manager& l10nManager = *MWBase::Environment::get().getL10nManager(); // If a key has a "Context:KeyName" format, use YAML to translate data if (split.size() == 2) { _result = l10nManager.getContext(split[0])->formatMessage(split[1], {}, {}); return; } // If not, treat is as GMST name from legacy localization if (!mStore) { Log(Debug::Error) << "Error: WindowManager::onRetrieveTag: no Store set up yet, can not replace '" << tag << "'"; _result = tag; return; } const ESM::GameSetting* setting = mStore->get().search(tag); if (setting && setting->mValue.getType() == ESM::VT_String) _result = setting->mValue.getString(); else _result = tag; } } void WindowManager::processChangedSettings(const Settings::CategorySettingVector& changed) { mToolTips->setDelay(Settings::Manager::getFloat("tooltip delay", "GUI")); bool changeRes = false; for (const auto& setting : changed) { if (setting.first == "HUD" && setting.second == "crosshair") mCrosshairEnabled = Settings::Manager::getBool("crosshair", "HUD"); else if (setting.first == "GUI" && setting.second == "subtitles") mSubtitlesEnabled = Settings::Manager::getBool("subtitles", "GUI"); else if (setting.first == "GUI" && setting.second == "menu transparency") setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); else if (setting.first == "Video" && (setting.second == "resolution x" || setting.second == "resolution y" || setting.second == "window mode" || setting.second == "window border")) changeRes = true; else if (setting.first == "Video" && setting.second == "vsync") mVideoWrapper->setSyncToVBlank(Settings::Manager::getBool("vsync", "Video")); else if (setting.first == "Video" && (setting.second == "gamma" || setting.second == "contrast")) mVideoWrapper->setGammaContrast( Settings::Manager::getFloat("gamma", "Video"), Settings::Manager::getFloat("contrast", "Video")); } if (changeRes) { mVideoWrapper->setVideoMode(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"), static_cast(Settings::Manager::getInt("window mode", "Video")), Settings::Manager::getBool("window border", "Video")); } } void WindowManager::windowResized(int x, int y) { Settings::Manager::setInt("resolution x", "Video", x); Settings::Manager::setInt("resolution y", "Video", y); // We only want to process changes to window-size related settings. Settings::CategorySettingVector filter = { { "Video", "resolution x" }, { "Video", "resolution y" } }; // If the HUD has not been initialised, the World singleton will not be available. if (mHud) { MWBase::Environment::get().getWorld()->processChangedSettings(Settings::Manager::getPendingChanges(filter)); } Settings::Manager::resetPendingChanges(filter); mGuiPlatform->getRenderManagerPtr()->setViewSize(x, y); // scaled size const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); x = viewSize.width; y = viewSize.height; sizeVideo(x, y); if (!mHud) return; // UI not initialized yet for (std::map::iterator it = mTrackedWindows.begin(); it != mTrackedWindows.end(); ++it) { std::string settingName = it->second; if (Settings::Manager::getBool(settingName + " maximized", "Windows")) settingName += " maximized"; MyGUI::IntPoint pos(static_cast(Settings::Manager::getFloat(settingName + " x", "Windows") * x), static_cast(Settings::Manager::getFloat(settingName + " y", "Windows") * y)); MyGUI::IntSize size(static_cast(Settings::Manager::getFloat(settingName + " w", "Windows") * x), static_cast(Settings::Manager::getFloat(settingName + " h", "Windows") * y)); it->first->setPosition(pos); it->first->setSize(size); } for (const auto& window : mWindows) window->onResChange(x, y); // TODO: check if any windows are now off-screen and move them back if so } bool WindowManager::isWindowVisible() { return mWindowVisible; } void WindowManager::windowVisibilityChange(bool visible) { mWindowVisible = visible; } void WindowManager::windowClosed() { MWBase::Environment::get().getStateManager()->requestQuit(); } void WindowManager::onCursorChange(const std::string& name) { mCursorManager->cursorChanged(name); } void WindowManager::pushGuiMode(GuiMode mode) { pushGuiMode(mode, MWWorld::Ptr()); } void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg) { pushGuiMode(mode, arg, false); } void WindowManager::forceLootMode(const MWWorld::Ptr& ptr) { pushGuiMode(MWGui::GM_Container, ptr, true); } void WindowManager::pushGuiMode(GuiMode mode, const MWWorld::Ptr& arg, bool force) { if (mode == GM_Inventory && mAllowed == GW_None) return; if (mGuiModes.empty() || mGuiModes.back() != mode) { // If this mode already exists somewhere in the stack, just bring it to the front. if (std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end()) { mGuiModes.erase(std::find(mGuiModes.begin(), mGuiModes.end(), mode)); } if (!mGuiModes.empty()) { mKeyboardNavigation->saveFocus(mGuiModes.back()); mGuiModeStates[mGuiModes.back()].update(false); } mGuiModes.push_back(mode); mGuiModeStates[mode].update(true); playSound(mGuiModeStates[mode].mOpenSound); } if (force) mContainerWindow->treatNextOpenAsLoot(); for (WindowBase* window : mGuiModeStates[mode].mWindows) window->setPtr(arg); mKeyboardNavigation->restoreFocus(mode); updateVisible(); } void WindowManager::setCullMask(uint32_t mask) { mViewer->getCamera()->setCullMask(mask); // We could check whether stereo is enabled here, but these methods are // trivial and have no effect in mono or multiview so just call them regardless. mViewer->getCamera()->setCullMaskLeft(mask); mViewer->getCamera()->setCullMaskRight(mask); } uint32_t WindowManager::getCullMask() { return mViewer->getCamera()->getCullMask(); } void WindowManager::popGuiMode(bool noSound) { if (mDragAndDrop && mDragAndDrop->mIsOnDragAndDrop) { mDragAndDrop->finish(); } if (!mGuiModes.empty()) { const GuiMode mode = mGuiModes.back(); mKeyboardNavigation->saveFocus(mode); mGuiModes.pop_back(); mGuiModeStates[mode].update(false); if (!noSound) playSound(mGuiModeStates[mode].mCloseSound); } if (!mGuiModes.empty()) { const GuiMode mode = mGuiModes.back(); mGuiModeStates[mode].update(true); mKeyboardNavigation->restoreFocus(mode); } updateVisible(); // To make sure that console window get focus again if (mConsole && mConsole->isVisible()) mConsole->onOpen(); } void WindowManager::removeGuiMode(GuiMode mode, bool noSound) { if (!mGuiModes.empty() && mGuiModes.back() == mode) { popGuiMode(noSound); return; } std::vector::iterator it = mGuiModes.begin(); while (it != mGuiModes.end()) { if (*it == mode) it = mGuiModes.erase(it); else ++it; } updateVisible(); } void WindowManager::goToJail(int days) { pushGuiMode(MWGui::GM_Jail); mJailScreen->goToJail(days); } void WindowManager::setSelectedSpell(const ESM::RefId& spellId, int successChancePercent) { mSelectedSpell = spellId; mSelectedEnchantItem = MWWorld::Ptr(); mHud->setSelectedSpell(spellId, successChancePercent); const ESM::Spell* spell = mStore->get().find(spellId); mSpellWindow->setTitle(spell->mName); } void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) { mSelectedEnchantItem = item; mSelectedSpell = ESM::RefId::sEmpty; const ESM::Enchantment* ench = mStore->get().find(item.getClass().getEnchantment(item)); int chargePercent = static_cast(item.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100); mHud->setSelectedEnchantItem(item, chargePercent); mSpellWindow->setTitle(item.getClass().getName(item)); } const MWWorld::Ptr& WindowManager::getSelectedEnchantItem() const { return mSelectedEnchantItem; } void WindowManager::setSelectedWeapon(const MWWorld::Ptr& item) { mSelectedWeapon = item; int durabilityPercent = 100; if (item.getClass().hasItemHealth(item)) { durabilityPercent = static_cast(item.getClass().getItemNormalizedHealth(item) * 100); } mHud->setSelectedWeapon(item, durabilityPercent); mInventoryWindow->setTitle(item.getClass().getName(item)); } const MWWorld::Ptr& WindowManager::getSelectedWeapon() const { return mSelectedWeapon; } void WindowManager::unsetSelectedSpell() { mSelectedSpell = ESM::RefId::sEmpty; mSelectedEnchantItem = MWWorld::Ptr(); mHud->unsetSelectedSpell(); MWWorld::Player* player = &MWBase::Environment::get().getWorld()->getPlayer(); if (player->getDrawState() == MWMechanics::DrawState::Spell) player->setDrawState(MWMechanics::DrawState::Nothing); mSpellWindow->setTitle("#{sNone}"); } void WindowManager::unsetSelectedWeapon() { mSelectedWeapon = MWWorld::Ptr(); mHud->unsetSelectedWeapon(); mInventoryWindow->setTitle("#{sSkillHandtohand}"); } void WindowManager::getMousePosition(int& x, int& y) { const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); x = pos.left; y = pos.top; } void WindowManager::getMousePosition(float& x, float& y) { const MyGUI::IntPoint& pos = MyGUI::InputManager::getInstance().getMousePosition(); x = static_cast(pos.left); y = static_cast(pos.top); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); x /= viewSize.width; y /= viewSize.height; } bool WindowManager::getWorldMouseOver() { return mHud->getWorldMouseOver(); } float WindowManager::getScalingFactor() const { return mScalingFactor; } void WindowManager::executeInConsole(const std::string& path) { mConsole->executeFile(path); } MWGui::InventoryWindow* WindowManager::getInventoryWindow() { return mInventoryWindow; } MWGui::CountDialog* WindowManager::getCountDialog() { return mCountDialog; } MWGui::ConfirmationDialog* WindowManager::getConfirmationDialog() { return mConfirmationDialog; } MWGui::TradeWindow* WindowManager::getTradeWindow() { return mTradeWindow; } MWGui::PostProcessorHud* WindowManager::getPostProcessorHud() { return mPostProcessorHud; } void WindowManager::useItem(const MWWorld::Ptr& item, bool bypassBeastRestrictions) { if (mInventoryWindow) mInventoryWindow->useItem(item, bypassBeastRestrictions); } bool WindowManager::isAllowed(GuiWindow wnd) const { return (mAllowed & wnd) != 0; } void WindowManager::allow(GuiWindow wnd) { mAllowed = (GuiWindow)(mAllowed | wnd); if (wnd & GW_Inventory) { mBookWindow->setInventoryAllowed(true); mScrollWindow->setInventoryAllowed(true); } updateVisible(); } void WindowManager::disallowAll() { mAllowed = GW_None; mRestAllowed = false; mBookWindow->setInventoryAllowed(false); mScrollWindow->setInventoryAllowed(false); updateVisible(); } void WindowManager::toggleVisible(GuiWindow wnd) { if (getMode() != GM_Inventory) return; std::string settingName; switch (wnd) { case GW_Inventory: settingName = "inventory"; break; case GW_Map: settingName = "map"; break; case GW_Magic: settingName = "spells"; break; case GW_Stats: settingName = "stats"; break; default: break; } if (!settingName.empty()) { settingName += " hidden"; bool hidden = Settings::Manager::getBool(settingName, "Windows"); Settings::Manager::setBool(settingName, "Windows", !hidden); } mShown = (GuiWindow)(mShown ^ wnd); updateVisible(); } void WindowManager::forceHide(GuiWindow wnd) { mForceHidden = (GuiWindow)(mForceHidden | wnd); updateVisible(); } void WindowManager::unsetForceHide(GuiWindow wnd) { mForceHidden = (GuiWindow)(mForceHidden & ~wnd); updateVisible(); } bool WindowManager::isGuiMode() const { return !mGuiModes.empty() || isConsoleMode() || (mPostProcessorHud && mPostProcessorHud->isVisible()) || (mMessageBoxManager && mMessageBoxManager->isInteractiveMessageBox()); } bool WindowManager::isConsoleMode() const { return mConsole && mConsole->isVisible(); } bool WindowManager::isPostProcessorHudVisible() const { return mPostProcessorHud->isVisible(); } MWGui::GuiMode WindowManager::getMode() const { if (mGuiModes.empty()) return GM_None; return mGuiModes.back(); } void WindowManager::disallowMouse() { mInputBlocker->setVisible(true); } void WindowManager::allowMouse() { mInputBlocker->setVisible(!isGuiMode()); } void WindowManager::notifyInputActionBound() { mSettingsWindow->updateControlsBox(); allowMouse(); } bool WindowManager::containsMode(GuiMode mode) const { if (mGuiModes.empty()) return false; return std::find(mGuiModes.begin(), mGuiModes.end(), mode) != mGuiModes.end(); } void WindowManager::showCrosshair(bool show) { if (mHud) mHud->setCrosshairVisible(show && mCrosshairEnabled); } void WindowManager::updateActivatedQuickKey() { mQuickKeysMenu->updateActivatedQuickKey(); } void WindowManager::activateQuickKey(int index) { mQuickKeysMenu->activateQuickKey(index); } bool WindowManager::getSubtitlesEnabled() { return mSubtitlesEnabled; } bool WindowManager::toggleHud() { mHudEnabled = !mHudEnabled; updateVisible(); mMessageBoxManager->setVisible(mHudEnabled); return mHudEnabled; } bool WindowManager::getRestEnabled() { // Enable rest dialogue if character creation finished if (mRestAllowed == false && MWBase::Environment::get().getWorld()->getGlobalFloat("chargenstate") == -1) mRestAllowed = true; return mRestAllowed; } bool WindowManager::getPlayerSleeping() { return mWaitDialog->getSleeping(); } void WindowManager::wakeUpPlayer() { mWaitDialog->wakeUp(); } void WindowManager::addVisitedLocation(const std::string& name, int x, int y) { mMap->addVisitedLocation(name, x, y); } const Translation::Storage& WindowManager::getTranslationDataStorage() const { return mTranslationDataStorage; } void WindowManager::changePointer(const std::string& name) { MyGUI::PointerManager::getInstance().setPointer(name); onCursorChange(name); } void WindowManager::showSoulgemDialog(MWWorld::Ptr item) { mSoulgemDialog->show(item); updateVisible(); } void WindowManager::updatePlayer() { mInventoryWindow->updatePlayer(); const MWWorld::Ptr player = MWMechanics::getPlayer(); if (player.getClass().getNpcStats(player).isWerewolf()) { setWerewolfOverlay(true); forceHide((GuiWindow)(MWGui::GW_Inventory | MWGui::GW_Magic)); } } // Remove this wrapper once onKeyFocusChanged call is rendered unnecessary void WindowManager::setKeyFocusWidget(MyGUI::Widget* widget) { MyGUI::InputManager::getInstance().setKeyFocusWidget(widget); onKeyFocusChanged(widget); } void WindowManager::onKeyFocusChanged(MyGUI::Widget* widget) { if (widget && widget->castType(false)) SDL_StartTextInput(); else SDL_StopTextInput(); } void WindowManager::setEnemy(const MWWorld::Ptr& enemy) { mHud->setEnemy(enemy); } int WindowManager::getMessagesCount() const { int count = 0; if (mMessageBoxManager) count = mMessageBoxManager->getMessagesCount(); return count; } Loading::Listener* WindowManager::getLoadingScreen() { return mLoadingScreen; } bool WindowManager::getCursorVisible() { return mCursorVisible && mCursorActive; } void WindowManager::trackWindow(Layout* layout, const std::string& name) { std::string settingName = name; MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); bool isMaximized = Settings::Manager::getBool(name + " maximized", "Windows"); if (isMaximized) settingName += " maximized"; MyGUI::IntPoint pos( static_cast(Settings::Manager::getFloat(settingName + " x", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(settingName + " y", "Windows") * viewSize.height)); MyGUI::IntSize size( static_cast(Settings::Manager::getFloat(settingName + " w", "Windows") * viewSize.width), static_cast(Settings::Manager::getFloat(settingName + " h", "Windows") * viewSize.height)); layout->mMainWidget->setPosition(pos); layout->mMainWidget->setSize(size); MyGUI::Window* window = layout->mMainWidget->castType(); window->eventWindowChangeCoord += MyGUI::newDelegate(this, &WindowManager::onWindowChangeCoord); mTrackedWindows[window] = name; } void WindowManager::toggleMaximized(Layout* layout) { MyGUI::Window* window = layout->mMainWidget->castType(); std::string setting = mTrackedWindows[window]; if (setting.empty()) return; bool maximized = !Settings::Manager::getBool(setting + " maximized", "Windows"); if (maximized) setting += " maximized"; MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); float x = Settings::Manager::getFloat(setting + " x", "Windows") * float(viewSize.width); float y = Settings::Manager::getFloat(setting + " y", "Windows") * float(viewSize.height); float w = Settings::Manager::getFloat(setting + " w", "Windows") * float(viewSize.width); float h = Settings::Manager::getFloat(setting + " h", "Windows") * float(viewSize.height); window->setCoord(x, y, w, h); Settings::Manager::setBool(mTrackedWindows[window] + " maximized", "Windows", maximized); } void WindowManager::onWindowChangeCoord(MyGUI::Window* _sender) { std::string setting = mTrackedWindows[_sender]; MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); float x = _sender->getPosition().left / float(viewSize.width); float y = _sender->getPosition().top / float(viewSize.height); float w = _sender->getSize().width / float(viewSize.width); float h = _sender->getSize().height / float(viewSize.height); Settings::Manager::setFloat(setting + " x", "Windows", x); Settings::Manager::setFloat(setting + " y", "Windows", y); Settings::Manager::setFloat(setting + " w", "Windows", w); Settings::Manager::setFloat(setting + " h", "Windows", h); bool maximized = Settings::Manager::getBool(setting + " maximized", "Windows"); if (maximized) Settings::Manager::setBool(setting + " maximized", "Windows", false); } void WindowManager::clear() { mPlayerBounty = -1; for (const auto& window : mWindows) window->clear(); if (mLocalMapRender) mLocalMapRender->clear(); mMessageBoxManager->clear(); mToolTips->clear(); mSelectedSpell = ESM::RefId::sEmpty; mCustomMarkers.clear(); mForceHidden = GW_None; mRestAllowed = true; while (!mGuiModes.empty()) popGuiMode(); updateVisible(); } void WindowManager::write(ESM::ESMWriter& writer, Loading::Listener& progress) { mMap->write(writer, progress); mQuickKeysMenu->write(writer); if (!mSelectedSpell.empty()) { writer.startRecord(ESM::REC_ASPL); writer.writeHNString("ID__", mSelectedSpell.getRefIdString()); writer.endRecord(ESM::REC_ASPL); } for (CustomMarkerCollection::ContainerType::const_iterator it = mCustomMarkers.begin(); it != mCustomMarkers.end(); ++it) { writer.startRecord(ESM::REC_MARK); it->second.save(writer); writer.endRecord(ESM::REC_MARK); } } void WindowManager::readRecord(ESM::ESMReader& reader, uint32_t type) { if (type == ESM::REC_GMAP) mMap->readRecord(reader, type); else if (type == ESM::REC_KEYS) mQuickKeysMenu->readRecord(reader, type); else if (type == ESM::REC_ASPL) { reader.getSubNameIs("ID__"); ESM::RefId spell = reader.getRefId(); if (mStore->get().search(spell)) mSelectedSpell = spell; } else if (type == ESM::REC_MARK) { ESM::CustomMarker marker; marker.load(reader); mCustomMarkers.addMarker(marker, false); } } int WindowManager::countSavedGameRecords() const { return 1 // Global map + 1 // QuickKeysMenu + mCustomMarkers.size() + (!mSelectedSpell.empty() ? 1 : 0); } bool WindowManager::isSavingAllowed() const { return !MyGUI::InputManager::getInstance().isModalAny() && !isConsoleMode() // TODO: remove this, once we have properly serialized the state of open windows && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest))); } void WindowManager::playVideo(std::string_view name, bool allowSkipping, bool overrideSounds) { mVideoWidget->playVideo("video\\" + std::string{ name }); mVideoWidget->eventKeyButtonPressed.clear(); mVideoBackground->eventKeyButtonPressed.clear(); if (allowSkipping) { mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed); } enableScene(false); MyGUI::IntSize screenSize = MyGUI::RenderManager::getInstance().getViewSize(); sizeVideo(screenSize.width, screenSize.height); MyGUI::Widget* oldKeyFocus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); setKeyFocusWidget(mVideoWidget); mVideoBackground->setVisible(true); bool cursorWasVisible = mCursorVisible; setCursorVisible(false); if (overrideSounds && mVideoWidget->hasAudioStream()) MWBase::Environment::get().getSoundManager()->pauseSounds( MWSound::VideoPlayback, ~MWSound::Type::Movie & MWSound::Type::Mask); Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit()); while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest()) { const double dt = std::chrono::duration_cast>(frameRateLimiter.getLastFrameDuration()) .count(); MWBase::Environment::get().getInputManager()->update(dt, true, false); if (!mWindowVisible) { mVideoWidget->pause(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); } else { if (mVideoWidget->isPaused()) mVideoWidget->resume(); mViewer->eventTraversal(); mViewer->updateTraversal(); mViewer->renderingTraversals(); } // at the time this function is called we are in the middle of a frame, // so out of order calls are necessary to get a correct frameNumber for the next frame. // refer to the advance() and frame() order in Engine::go() mViewer->advance(mViewer->getFrameStamp()->getSimulationTime()); frameRateLimiter.limit(); } mVideoWidget->stop(); MWBase::Environment::get().getSoundManager()->resumeSounds(MWSound::VideoPlayback); setKeyFocusWidget(oldKeyFocus); setCursorVisible(cursorWasVisible); // Restore normal rendering updateVisible(); mVideoBackground->setVisible(false); } void WindowManager::sizeVideo(int screenWidth, int screenHeight) { // Use black bars to correct aspect ratio bool stretch = Settings::Manager::getBool("stretch menu background", "GUI"); mVideoBackground->setSize(screenWidth, screenHeight); mVideoWidget->autoResize(stretch); } void WindowManager::exitCurrentModal() { if (!mCurrentModals.empty()) { WindowModal* window = mCurrentModals.back(); if (!window->exit()) return; window->setVisible(false); } } void WindowManager::addCurrentModal(WindowModal* input) { if (mCurrentModals.empty()) mKeyboardNavigation->saveFocus(getMode()); mCurrentModals.push_back(input); mKeyboardNavigation->restoreFocus(-1); mKeyboardNavigation->setModalWindow(input->mMainWidget); mKeyboardNavigation->setDefaultFocus(input->mMainWidget, input->getDefaultKeyFocus()); } void WindowManager::removeCurrentModal(WindowModal* input) { if (!mCurrentModals.empty()) { if (input == mCurrentModals.back()) { mCurrentModals.pop_back(); mKeyboardNavigation->saveFocus(-1); } else { auto found = std::find(mCurrentModals.begin(), mCurrentModals.end(), input); if (found != mCurrentModals.end()) mCurrentModals.erase(found); else Log(Debug::Warning) << "Warning: can't find modal window " << input; } } if (mCurrentModals.empty()) { mKeyboardNavigation->setModalWindow(nullptr); mKeyboardNavigation->restoreFocus(getMode()); } else mKeyboardNavigation->setModalWindow(mCurrentModals.back()->mMainWidget); } void WindowManager::onVideoKeyPressed(MyGUI::Widget* _sender, MyGUI::KeyCode _key, MyGUI::Char _char) { if (_key == MyGUI::KeyCode::Escape) mVideoWidget->stop(); } void WindowManager::updatePinnedWindows() { mInventoryWindow->setPinned(Settings::Manager::getBool("inventory pin", "Windows")); if (Settings::Manager::getBool("inventory hidden", "Windows")) mShown = (GuiWindow)(mShown ^ GW_Inventory); mMap->setPinned(Settings::Manager::getBool("map pin", "Windows")); if (Settings::Manager::getBool("map hidden", "Windows")) mShown = (GuiWindow)(mShown ^ GW_Map); mSpellWindow->setPinned(Settings::Manager::getBool("spells pin", "Windows")); if (Settings::Manager::getBool("spells hidden", "Windows")) mShown = (GuiWindow)(mShown ^ GW_Magic); mStatsWindow->setPinned(Settings::Manager::getBool("stats pin", "Windows")); if (Settings::Manager::getBool("stats hidden", "Windows")) mShown = (GuiWindow)(mShown ^ GW_Stats); } void WindowManager::pinWindow(GuiWindow window) { switch (window) { case GW_Inventory: mInventoryWindow->setPinned(true); break; case GW_Map: mMap->setPinned(true); break; case GW_Magic: mSpellWindow->setPinned(true); break; case GW_Stats: mStatsWindow->setPinned(true); break; default: break; } updateVisible(); } void WindowManager::fadeScreenIn(const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeOut(time, delay); } void WindowManager::fadeScreenOut(const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeIn(time, delay); } void WindowManager::fadeScreenTo(const int percent, const float time, bool clearQueue, float delay) { if (clearQueue) mScreenFader->clearQueue(); mScreenFader->fadeTo(percent, time, delay); } void WindowManager::setBlindness(const int percent) { mBlindnessFader->notifyAlphaChanged(percent / 100.f); } void WindowManager::activateHitOverlay(bool interrupt) { if (!mHitFaderEnabled) return; if (!interrupt && !mHitFader->isEmpty()) return; mHitFader->clearQueue(); mHitFader->fadeTo(100, 0.0f); mHitFader->fadeTo(0, 0.5f); } void WindowManager::setWerewolfOverlay(bool set) { if (!mWerewolfOverlayEnabled) return; if (mWerewolfFader) mWerewolfFader->notifyAlphaChanged(set ? 1.0f : 0.0f); } void WindowManager::onClipboardChanged(const std::string& _type, const std::string& _data) { if (_type == "Text") SDL_SetClipboardText(MyGUI::TextIterator::getOnlyText(MyGUI::UString(_data)).asUTF8().c_str()); } void WindowManager::onClipboardRequested(const std::string& _type, std::string& _data) { if (_type != "Text") return; char* text = nullptr; text = SDL_GetClipboardText(); if (text) _data = MyGUI::TextIterator::toTagsString(text); SDL_free(text); } void WindowManager::toggleConsole() { bool visible = mConsole->isVisible(); if (!visible && !mGuiModes.empty()) mKeyboardNavigation->saveFocus(mGuiModes.back()); mConsole->setVisible(!visible); if (visible && !mGuiModes.empty()) mKeyboardNavigation->restoreFocus(mGuiModes.back()); updateVisible(); } void WindowManager::toggleDebugWindow() { mDebugWindow->setVisible(!mDebugWindow->isVisible()); } void WindowManager::togglePostProcessorHud() { if (!MWBase::Environment::get().getWorld()->getPostProcessor()->isEnabled()) { messageBox("Postprocessor is not enabled."); return; } bool visible = mPostProcessorHud->isVisible(); if (!visible && !mGuiModes.empty()) mKeyboardNavigation->saveFocus(mGuiModes.back()); mPostProcessorHud->setVisible(!visible); if (visible && !mGuiModes.empty()) mKeyboardNavigation->restoreFocus(mGuiModes.back()); updateVisible(); } void WindowManager::cycleSpell(bool next) { if (!isGuiMode()) mSpellWindow->cycle(next); } void WindowManager::cycleWeapon(bool next) { if (!isGuiMode()) mInventoryWindow->cycle(next); } void WindowManager::playSound(const ESM::RefId& soundId, float volume, float pitch) { if (soundId.empty()) return; MWBase::Environment::get().getSoundManager()->playSound( soundId, volume, pitch, MWSound::Type::Sfx, MWSound::PlayMode::NoEnvNoScaling); } void WindowManager::updateSpellWindow() { if (mSpellWindow) mSpellWindow->updateSpells(); } void WindowManager::setConsoleSelectedObject(const MWWorld::Ptr& object) { mConsole->setSelectedObject(object); } MWWorld::Ptr WindowManager::getConsoleSelectedObject() const { return mConsole->getSelectedObject(); } void WindowManager::printToConsole(const std::string& msg, std::string_view color) { mConsole->print(msg, color); } void WindowManager::setConsoleMode(const std::string& mode) { mConsole->setConsoleMode(mode); } void WindowManager::createCursors() { MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator(); while (enumerator.next()) { MyGUI::IResource* resource = enumerator.current().second; ResourceImageSetPointerFix* imgSetPointer = resource->castType(false); if (!imgSetPointer) continue; std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0, 0).texture; osg::ref_ptr image = mResourceSystem->getImageManager()->getImage(tex_name); if (image.valid()) { // everything looks good, send it to the cursor manager Uint8 hotspot_x = imgSetPointer->getHotSpot().left; Uint8 hotspot_y = imgSetPointer->getHotSpot().top; int rotation = imgSetPointer->getRotation(); MyGUI::IntSize pointerSize = imgSetPointer->getSize(); mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, image, hotspot_x, hotspot_y, pointerSize.width, pointerSize.height); } } } void WindowManager::createTextures() { { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("white"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x = 0; x < 8; ++x) for (int y = 0; y < 8; ++y) { *(data++) = 255; *(data++) = 255; *(data++) = 255; } tex->unlock(); } { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("black"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x = 0; x < 8; ++x) for (int y = 0; y < 8; ++y) { *(data++) = 0; *(data++) = 0; *(data++) = 0; } tex->unlock(); } { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture("transparent"); tex->createManual(8, 8, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8); setMenuTransparency(Settings::Manager::getFloat("menu transparency", "GUI")); } } void WindowManager::setMenuTransparency(float value) { MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().getTexture("transparent"); unsigned char* data = reinterpret_cast(tex->lock(MyGUI::TextureUsage::Write)); for (int x = 0; x < 8; ++x) for (int y = 0; y < 8; ++y) { *(data++) = 255; *(data++) = 255; *(data++) = 255; *(data++) = static_cast(value * 255); } tex->unlock(); } void WindowManager::addCell(MWWorld::CellStore* cell) { mLocalMapRender->addCell(cell); } void WindowManager::removeCell(MWWorld::CellStore* cell) { mLocalMapRender->removeCell(cell); } void WindowManager::writeFog(MWWorld::CellStore* cell) { mLocalMapRender->saveFogOfWar(cell); } const MWGui::TextColours& WindowManager::getTextColours() { return mTextColours; } bool WindowManager::injectKeyPress(MyGUI::KeyCode key, unsigned int text, bool repeat) { if (!mKeyboardNavigation->injectKeyPress(key, text, repeat)) { MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); bool widgetActive = MyGUI::InputManager::getInstance().injectKeyPress(key, text); if (!widgetActive || !focus) return false; // FIXME: MyGUI doesn't allow widgets to state if a given key was actually used, so make a guess if (focus->getTypeName().find("Button") != std::string::npos) { switch (key.getValue()) { case MyGUI::KeyCode::ArrowDown: case MyGUI::KeyCode::ArrowUp: case MyGUI::KeyCode::ArrowLeft: case MyGUI::KeyCode::ArrowRight: case MyGUI::KeyCode::Return: case MyGUI::KeyCode::NumpadEnter: case MyGUI::KeyCode::Space: return true; default: return false; } } return false; } else return true; } bool WindowManager::injectKeyRelease(MyGUI::KeyCode key) { return MyGUI::InputManager::getInstance().injectKeyRelease(key); } void WindowManager::GuiModeState::update(bool visible) { for (const auto& window : mWindows) window->setVisible(visible); } void WindowManager::watchActor(const MWWorld::Ptr& ptr) { mStatsWatcher->watchActor(ptr); } MWWorld::Ptr WindowManager::getWatchedActor() const { return mStatsWatcher->getWatchedActor(); } const std::string& WindowManager::getVersionDescription() const { return mVersionDescription; } void WindowManager::handleScheduledMessageBoxes() { const auto scheduledMessageBoxes = mScheduledMessageBoxes.lock(); for (const ScheduledMessageBox& v : *scheduledMessageBoxes) messageBox(v.mMessage, v.mShowInDialogueMode); scheduledMessageBoxes->clear(); } void WindowManager::onDeleteCustomData(const MWWorld::Ptr& ptr) { for (const auto& window : mWindows) window->onDeleteCustomData(ptr); } void WindowManager::asyncPrepareSaveMap() { mMap->asyncPrepareSaveMap(); } }