/* * GeneralOptionsTab.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "GeneralOptionsTab.h" #include "CGameInfo.h" #include "CPlayerInterface.h" #include "CServerHandler.h" #include "media/IMusicPlayer.h" #include "media/ISoundPlayer.h" #include "render/IScreenHandler.h" #include "windows/GUIClasses.h" #include "../../eventsSDL/InputHandler.h" #include "../../gui/CGuiHandler.h" #include "../../gui/WindowHandler.h" #include "../../widgets/Buttons.h" #include "../../widgets/Images.h" #include "../../widgets/Slider.h" #include "../../widgets/TextControls.h" #include "../../../lib/texts/CGeneralTextHandler.h" #include "../../../lib/filesystem/ResourcePath.h" static void setIntSetting(std::string group, std::string field, int value) { Settings entry = settings.write[group][field]; entry->Float() = value; } static void setBoolSetting(std::string group, std::string field, bool value) { Settings entry = settings.write[group][field]; entry->Bool() = value; } static std::string scalingToEntryString( int scaling) { return std::to_string(scaling) + '%'; } static std::string scalingToLabelString( int scaling) { std::string string = CGI->generaltexth->translate("vcmi.systemOptions.scalingButton.hover"); boost::replace_all(string, "%p", std::to_string(scaling)); return string; } static std::string longTouchToEntryString( int duration) { std::string string = CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.entry"); boost::replace_all(string, "%d", std::to_string(duration)); return string; } static std::string longTouchToLabelString( int duration) { std::string string = CGI->generaltexth->translate("vcmi.systemOptions.longTouchButton.hover"); boost::replace_all(string, "%d", std::to_string(duration)); return string; } static std::string resolutionToEntryString( int w, int h) { std::string string = "%wx%h"; boost::replace_all(string, "%w", std::to_string(w)); boost::replace_all(string, "%h", std::to_string(h)); return string; } static std::string resolutionToLabelString( int w, int h) { std::string string = CGI->generaltexth->translate("vcmi.systemOptions.resolutionButton.hover"); boost::replace_all(string, "%w", std::to_string(w)); boost::replace_all(string, "%h", std::to_string(h)); return string; } GeneralOptionsTab::GeneralOptionsTab() : InterfaceObjectConfigurable(), onFullscreenChanged(settings.listen["video"]["fullscreen"]) { OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; setRedrawParent(true); addConditional("touchscreen", GH.input().hasTouchInputDevice()); #ifdef VCMI_MOBILE addConditional("mobile", true); addConditional("desktop", false); #else addConditional("mobile", false); addConditional("desktop", true); #endif const JsonNode config(JsonPath::builtin("config/widgets/settings/generalOptionsTab.json")); addCallback("spellbookAnimationChanged", [](bool value) { setBoolSetting("video", "spellbookAnimation", value); }); addCallback("setMusic", [this](int value) { setIntSetting("general", "music", value); widget("musicSlider")->redraw(); auto targetLabel = widget("musicValueLabel"); if (targetLabel) targetLabel->setText(std::to_string(value) + "%"); }); addCallback("setVolume", [this](int value) { setIntSetting("general", "sound", value); widget("soundVolumeSlider")->redraw(); auto targetLabel = widget("soundValueLabel"); if (targetLabel) targetLabel->setText(std::to_string(value) + "%"); }); //settings that do not belong to base game: addCallback("fullscreenBorderlessChanged", [this](bool value) { setFullscreenMode(value, false); }); addCallback("fullscreenExclusiveChanged", [this](bool value) { setFullscreenMode(value, true); }); addCallback("setGameResolution", [this](int dummyValue) { selectGameResolution(); }); addCallback("setGameScaling", [this](int dummyValue) { selectGameScaling(); }); addCallback("setLongTouchDuration", [this](int dummyValue) { selectLongTouchDuration(); }); addCallback("framerateChanged", [](bool value) { setBoolSetting("video", "showfps", value); }); addCallback("hapticFeedbackChanged", [](bool value) { setBoolSetting("general", "hapticFeedback", value); }); addCallback("enableUiEnhancementsChanged", [](bool value) { setBoolSetting("general", "enableUiEnhancements", value); }); addCallback("enableLargeSpellbookChanged", [this](bool value) { setBoolSetting("gameTweaks", "enableLargeSpellbook", value); std::shared_ptr spellbookAnimationCheckbox = widget("spellbookAnimationCheckbox"); if(value) spellbookAnimationCheckbox->disable(); else spellbookAnimationCheckbox->enable(); redraw(); }); addCallback("audioMuteFocusChanged", [](bool value) { setBoolSetting("general", "audioMuteFocus", value); }); //moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content addCallback("availableCreaturesAsDwellingChanged", [=](int value) { setBoolSetting("gameTweaks", "availableCreaturesAsDwellingLabel", value > 0); }); addCallback("compactTownCreatureInfoChanged", [](bool value) { setBoolSetting("gameTweaks", "compactTownCreatureInfo", value); }); build(config); const auto & currentResolution = settings["video"]["resolution"]; std::shared_ptr scalingLabel = widget("scalingLabel"); scalingLabel->setText(scalingToLabelString(currentResolution["scaling"].Integer())); std::shared_ptr longTouchLabel = widget("longTouchLabel"); if (longTouchLabel) longTouchLabel->setText(longTouchToLabelString(settings["general"]["longTouchTimeMilliseconds"].Integer())); std::shared_ptr spellbookAnimationCheckbox = widget("spellbookAnimationCheckbox"); spellbookAnimationCheckbox->setSelected(settings["video"]["spellbookAnimation"].Bool()); if(settings["gameTweaks"]["enableLargeSpellbook"].Bool()) spellbookAnimationCheckbox->disable(); else spellbookAnimationCheckbox->enable(); std::shared_ptr fullscreenBorderlessCheckbox = widget("fullscreenBorderlessCheckbox"); if (fullscreenBorderlessCheckbox) fullscreenBorderlessCheckbox->setSelected(settings["video"]["fullscreen"].Bool() && !settings["video"]["realFullscreen"].Bool()); std::shared_ptr fullscreenExclusiveCheckbox = widget("fullscreenExclusiveCheckbox"); if (fullscreenExclusiveCheckbox) fullscreenExclusiveCheckbox->setSelected(settings["video"]["fullscreen"].Bool() && settings["video"]["realFullscreen"].Bool()); std::shared_ptr framerateCheckbox = widget("framerateCheckbox"); framerateCheckbox->setSelected(settings["video"]["showfps"].Bool()); std::shared_ptr hapticFeedbackCheckbox = widget("hapticFeedbackCheckbox"); if (hapticFeedbackCheckbox) hapticFeedbackCheckbox->setSelected(settings["general"]["hapticFeedback"].Bool()); std::shared_ptr enableUiEnhancementsCheckbox = widget("enableUiEnhancementsCheckbox"); if (enableUiEnhancementsCheckbox) enableUiEnhancementsCheckbox->setSelected(settings["general"]["enableUiEnhancements"].Bool()); std::shared_ptr enableLargeSpellbookCheckbox = widget("enableLargeSpellbookCheckbox"); if (enableLargeSpellbookCheckbox) enableLargeSpellbookCheckbox->setSelected(settings["gameTweaks"]["enableLargeSpellbook"].Bool()); std::shared_ptr audioMuteFocusCheckbox = widget("audioMuteFocusCheckbox"); if (audioMuteFocusCheckbox) audioMuteFocusCheckbox->setSelected(settings["general"]["audioMuteFocus"].Bool()); std::shared_ptr musicSlider = widget("musicSlider"); musicSlider->scrollTo(CCS->musich->getVolume()); std::shared_ptr volumeSlider = widget("soundVolumeSlider"); volumeSlider->scrollTo(CCS->soundh->getVolume()); std::shared_ptr creatureGrowthAsDwellingPicker = widget("availableCreaturesAsDwellingPicker"); creatureGrowthAsDwellingPicker->setSelected(settings["gameTweaks"]["availableCreaturesAsDwellingLabel"].Bool()); std::shared_ptr compactTownCreatureInfo = widget("compactTownCreatureInfoCheckbox"); compactTownCreatureInfo->setSelected(settings["gameTweaks"]["compactTownCreatureInfo"].Bool()); std::shared_ptr musicVolumeLabel = widget("musicValueLabel"); musicVolumeLabel->setText(std::to_string(CCS->musich->getVolume()) + "%"); std::shared_ptr soundVolumeLabel = widget("soundValueLabel"); soundVolumeLabel->setText(std::to_string(CCS->soundh->getVolume()) + "%"); updateResolutionSelector(); } void GeneralOptionsTab::updateResolutionSelector() { std::shared_ptr resolutionButton = widget("resolutionButton"); std::shared_ptr resolutionLabel = widget("resolutionLabel"); if (resolutionButton) { if (settings["video"]["fullscreen"].Bool() && !settings["video"]["realFullscreen"].Bool()) resolutionButton->disable(); else resolutionButton->enable(); } if (resolutionLabel) { Point resolution = GH.screenHandler().getRenderResolution(); resolutionLabel->setText(resolutionToLabelString(resolution.x, resolution.y)); } } void GeneralOptionsTab::selectGameResolution() { supportedResolutions = GH.screenHandler().getSupportedResolutions(); std::vector items; size_t currentResolutionIndex = 0; size_t i = 0; for(const auto & it : supportedResolutions) { auto resolutionStr = resolutionToEntryString(it.x, it.y); if(widget("resolutionLabel")->getText() == resolutionToLabelString(it.x, it.y)) currentResolutionIndex = i; items.push_back(std::move(resolutionStr)); ++i; } GH.windows().createAndPushWindow(items, nullptr, CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.hover"), CGI->generaltexth->translate("vcmi.systemOptions.resolutionMenu.help"), [this](int index) { setGameResolution(index); }, currentResolutionIndex); } void GeneralOptionsTab::setGameResolution(int index) { assert(index >= 0 && index < supportedResolutions.size()); if ( index < 0 || index >= supportedResolutions.size() ) return; Point resolution = supportedResolutions[index]; Settings gameRes = settings.write["video"]["resolution"]; gameRes["width"].Float() = resolution.x; gameRes["height"].Float() = resolution.y; widget("resolutionLabel")->setText(resolutionToLabelString(resolution.x, resolution.y)); GH.dispatchMainThread([](){ GH.onScreenResize(true); }); } void GeneralOptionsTab::setFullscreenMode(bool on, bool exclusive) { if (on == settings["video"]["fullscreen"].Bool() && exclusive == settings["video"]["realFullscreen"].Bool()) return; setBoolSetting("video", "realFullscreen", exclusive); setBoolSetting("video", "fullscreen", on); std::shared_ptr fullscreenExclusiveCheckbox = widget("fullscreenExclusiveCheckbox"); std::shared_ptr fullscreenBorderlessCheckbox = widget("fullscreenBorderlessCheckbox"); if (fullscreenBorderlessCheckbox) fullscreenBorderlessCheckbox->setSelectedSilent(on && !exclusive); if (fullscreenExclusiveCheckbox) fullscreenExclusiveCheckbox->setSelectedSilent(on && exclusive); updateResolutionSelector(); GH.dispatchMainThread([](){ GH.onScreenResize(true); }); } void GeneralOptionsTab::selectGameScaling() { supportedScaling.clear(); // generate list of all possible scaling values, with 10% step // also add one value over maximum, so if player can use scaling up to 123.456% he will be able to select 130% // and let screen handler clamp that value to actual maximum auto [minimalScaling, maximalScaling] = GH.screenHandler().getSupportedScalingRange(); for (int i = 0; i <= maximalScaling + 10 - 1; i += 10) { if (i >= minimalScaling) supportedScaling.push_back(i); } std::vector items; size_t currentIndex = 0; size_t i = 0; for(const auto & it : supportedScaling) { auto resolutionStr = scalingToEntryString(it); if(widget("scalingLabel")->getText() == scalingToLabelString(it)) currentIndex = i; items.push_back(std::move(resolutionStr)); ++i; } GH.windows().createAndPushWindow( items, nullptr, CGI->generaltexth->translate("vcmi.systemOptions.scalingMenu.hover"), CGI->generaltexth->translate("vcmi.systemOptions.scalingMenu.help"), [this](int index) { setGameScaling(index); }, currentIndex ); } void GeneralOptionsTab::setGameScaling(int index) { assert(index >= 0 && index < supportedScaling.size()); if ( index < 0 || index >= supportedScaling.size() ) return; int scaling = supportedScaling[index]; Settings gameRes = settings.write["video"]["resolution"]; gameRes["scaling"].Float() = scaling; widget("scalingLabel")->setText(scalingToLabelString(scaling)); GH.dispatchMainThread([](){ GH.onScreenResize(true); }); } void GeneralOptionsTab::selectLongTouchDuration() { longTouchDurations = { 500, 750, 1000, 1250, 1500, 1750, 2000 }; std::vector items; size_t currentIndex = 0; size_t i = 0; for(const auto & it : longTouchDurations) { auto resolutionStr = longTouchToEntryString(it); if(widget("longTouchLabel")->getText() == longTouchToLabelString(it)) currentIndex = i; items.push_back(std::move(resolutionStr)); ++i; } GH.windows().createAndPushWindow( items, nullptr, CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.hover"), CGI->generaltexth->translate("vcmi.systemOptions.longTouchMenu.help"), [this](int index) { setLongTouchDuration(index); }, currentIndex ); } void GeneralOptionsTab::setLongTouchDuration(int index) { assert(index >= 0 && index < longTouchDurations.size()); if ( index < 0 || index >= longTouchDurations.size() ) return; int scaling = longTouchDurations[index]; Settings longTouchTime = settings.write["general"]["longTouchTimeMilliseconds"]; longTouchTime->Float() = scaling; widget("longTouchLabel")->setText(longTouchToLabelString(scaling)); }