diff --git a/Common/UI/TabHolder.cpp b/Common/UI/TabHolder.cpp index f7da78ad5e..32129466d9 100644 --- a/Common/UI/TabHolder.cpp +++ b/Common/UI/TabHolder.cpp @@ -138,6 +138,10 @@ bool TabHolder::EnsureTab(int index) { void TabHolder::SetInitialTab(int tab) { if (tab < 0 || tab >= (int)tabs_.size()) { + // The tab may no longer exist (the search tab is hidden on small screens, for example). + if (currentTab_ < (int)tabs_.size()) { + EnsureTab(currentTab_); + } return; } EnsureTab(tab); diff --git a/Core/Config.cpp b/Core/Config.cpp index 45525140f0..ab7a631017 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1666,6 +1666,9 @@ void Config::PostLoadCleanup() { // Three frames in flight showed no benefit over two, so the option is gone. Squash old values. iInflightFrames = std::clamp(iInflightFrames, 1, 2); + + // The touch layout editor divides by this, and loops forever drawing the grid if it's 0. + iTouchSnapGridSize = std::clamp(iTouchSnapGridSize, 2, 256); } void Config::PreSaveCleanup() { diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index dbbf3761f7..30c7b2368a 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -867,7 +867,8 @@ void AskToEditCurrentServer(int requestToken, ScreenManager *screenManager) { }); return; } - static std::string editText = g_Config.sProAdhocServer; + static std::string editText; + editText = g_Config.sProAdhocServer; TextEditPopupScreen *popupScreen = new TextEditPopupScreen(&editText, editText, n->T("Ad hoc server address"), 256); if (System_GetPropertyBool(SYSPROP_KEYBOARD_IS_SOFT)) { popupScreen->SetAlignTop(true); diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 0c491b8905..7523041d8d 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -308,20 +308,16 @@ void ControlMappingScreen::OnAutoConfigure(UI::EventParams ¶ms) { } auto km = GetI18NCategory(I18NCat::KEYMAPPING); auto di = GetI18NCategory(I18NCat::DIALOG); - UI::ListPopupScreen *autoConfList = new UI::ListPopupScreen(km->T("Autoconfigure for device"), items, -1); + // Uses a callback, since in portrait the settings are in a popup, which would get dialogFinished instead of us. + UI::ListPopupScreen *autoConfList = new UI::ListPopupScreen(km->T("Autoconfigure for device"), items, -1, [items](int index) { + KeyMap::AutoConfForPad(items[index]); + }); autoConfList->SetNotification(NoticeLevel::WARN, di->T("This will overwrite the existing configuration")); if (params.v) autoConfList->SetPopupOrigin(params.v); screenManager()->push(autoConfList); } -void ControlMappingScreen::dialogFinished(const Screen *dialog, DialogResult result) { - if (result == DR_OK && !strcmp(dialog->tag(), "listpopup")) { - UI::ListPopupScreen *popup = (UI::ListPopupScreen *)dialog; - KeyMap::AutoConfForPad(popup->GetChoiceString()); - } -} - void KeyMappingNewKeyDialog::CreatePopupContents(UI::ViewGroup *parent) { using namespace UI; @@ -351,8 +347,11 @@ bool KeyMappingNewKeyDialog::key(const KeyInput &key) { return true; } - if (pspBtn_ == VIRTKEY_SPEED_ANALOG && !UI::IsEscapeKey(key)) { - // Only map analog values to this mapping. + if (pspBtn_ == VIRTKEY_SPEED_ANALOG) { + // Only map analog values to this mapping. Escape cancels. + if (UI::IsEscapeKey(key)) { + TriggerFinish(DR_CANCEL); + } return true; } diff --git a/UI/ControlMappingScreen.h b/UI/ControlMappingScreen.h index 6c7a4579ae..ab4744ec80 100644 --- a/UI/ControlMappingScreen.h +++ b/UI/ControlMappingScreen.h @@ -57,8 +57,6 @@ protected: private: void OnAutoConfigure(UI::EventParams ¶ms); - void dialogFinished(const Screen *dialog, DialogResult result) override; - std::vector mappers_; int keyMapGeneration_ = -1; diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index f402e03f25..08c479d8d0 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -136,14 +136,18 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { ConfigCustomButton *cfg = nullptr; bool *show = nullptr; - memset(array, 0, sizeof(array)); cfg = &g_Config.CustomButton[id_]; // This screen is reachable from the main menu, so neither GamepadEmu nor the layout screen // need have sanitized these yet - and everything below indexes the tables with them. CustomKeyData::Sanitize(*cfg); show = &touch.touchCustom[id_].show; - for (int i = 0; i < ARRAY_SIZE(g_customKeyList); i++) - array[i] = (0x01 == ((g_Config.CustomButton[id_].key >> i) & 0x01)); + // Only load once, since the views can be recreated (on rotation, for example) before the array is saved. + if (!arrayLoaded_) { + for (int i = 0; i < ARRAY_SIZE(g_customKeyList); i++) { + array[i] = (0x01 == ((g_Config.CustomButton[id_].key >> i) & 0x01)); + } + arrayLoaded_ = true; + } // TODO: Less hacky layout work const Bounds layoutBounds = GetLayoutBounds(*screenManager()->getUIContext()); @@ -205,8 +209,9 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { choice = new Choice(mc->T(KeyMap::GetPspButtonNameCharPointer(g_customKeyList[i].c)), new LinearLayoutParams(1.0f)); } - ChoiceEventHandler *choiceEventHandler = new ChoiceEventHandler(checkbox); - choice->OnClick.Handle(choiceEventHandler, &ChoiceEventHandler::onChoiceClick); + choice->OnClick.Add([checkbox](UI::EventParams &) { + checkbox->Toggle(); + }); choice->SetCentered(true); @@ -241,7 +246,3 @@ void CustomButtonMappingScreen::onFinish(DialogResult result) { saveArray(); g_Config.Save("CustomButtonMappingScreen::onFinish"); } - -void CustomButtonMappingScreen::ChoiceEventHandler::onChoiceClick(UI::EventParams &e){ - checkbox_->Toggle(); -}; diff --git a/UI/CustomButtonMappingScreen.h b/UI/CustomButtonMappingScreen.h index 9f6f2b5a00..159866f07a 100644 --- a/UI/CustomButtonMappingScreen.h +++ b/UI/CustomButtonMappingScreen.h @@ -46,15 +46,8 @@ private: void saveArray(); bool array[ARRAY_SIZE(CustomKeyData::g_customKeyList)]{}; + bool arrayLoaded_ = false; int id_; - class ChoiceEventHandler{ - public: - ChoiceEventHandler(UI::CheckBox *checkbox) : checkbox_(checkbox) {} - void onChoiceClick(UI::EventParams &e); - private: - UI::CheckBox *checkbox_; - }; - DeviceOrientation deviceOrientation_; }; diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index 3d0d1c5e21..bb3b1587a5 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -160,14 +160,11 @@ bool CwCheatScreen::WantsTextInput() const { bool CwCheatScreen::TryLoadCheatInfo() { std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); - std::string gameID; if (!info->Ready(GameInfoFlags::PARAM_SFO)) { return false; } - gameID = info->GetParamSFO().GetValueString("DISC_ID"); - if ((info->id.empty() || !info->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/")) { - gameID = g_paramSFO.GenerateFakeID(gamePath_); - } + // The cache has already replaced the ID of homebrew with a generated one. + const std::string &gameID = info->id; if (!engine_ || gameID != gameID_) { gameID_ = gameID; @@ -205,6 +202,9 @@ bool CwCheatScreen::key(const KeyInput &input) { void CwCheatScreen::dialogFinished(const Screen *dialog, DialogResult result) { if (auto downloadScreen = dynamic_cast(dialog)) { + if (result != DR_OK || downloadScreen->ChosenUrl().empty()) { + return; + } if (downloadRequest_) { ERROR_LOG(Log::CwCheats, "Download already in progress, ignoring new download request."); return; @@ -429,7 +429,7 @@ void CwCheatScreen::ImportAndReport(const Path &cheatFile) { int cheatCount = 0; if (!ImportCheats(cheatFile, &cheatCount)) { // Show an error message? - if (File::Exists(cheatFile)) { + if (!File::Exists(cheatFile)) { auto er = GetI18NCategory(I18NCat::ERRORS); errorMessage_ = ApplySafeSubstitutions(er->T("File not found: %1"), ""); errorLevel_ = NoticeLevel::WARN; @@ -474,14 +474,14 @@ void CwCheatScreen::OnImportCheat(UI::EventParams ¶ms) { } bool CwCheatScreen::ImportCheats(const Path &cheatFile, int *cheatsFound) { - FILE *in = File::OpenCFile(cheatFile, "rt"); - if (!in) { - WARN_LOG(Log::CwCheats, "Unable to open %s\n", cheatFile.c_str()); + if (gameID_.length() != 9 || !engine_) { + WARN_LOG(Log::CwCheats, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameID_.c_str()); return false; } - if (gameID_.length() != 9 || !engine_) { - WARN_LOG(Log::CwCheats, "CWCHEAT: Incorrect ID(%s) - can't import cheats.", gameID_.c_str()); + FILE *in = File::OpenCFile(cheatFile, "rt"); + if (!in) { + WARN_LOG(Log::CwCheats, "Unable to open %s\n", cheatFile.c_str()); return false; } diff --git a/UI/DeveloperToolsScreen.cpp b/UI/DeveloperToolsScreen.cpp index d2fb09a7ad..abead2fb46 100644 --- a/UI/DeveloperToolsScreen.cpp +++ b/UI/DeveloperToolsScreen.cpp @@ -350,8 +350,7 @@ void DeveloperToolsScreen::CreateMIPSTracerTab(UI::LinearLayout *list) { return true; }); - MIPSTracerPath_ = mipsTracer.get_logging_path(); - MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_)); + list->Add(new InfoItem(dev->T("Current log file"), mipsTracer.get_logging_path())); PopupSliderChoice* storage_capacity = list->Add( new PopupSliderChoice( @@ -425,7 +424,8 @@ void DeveloperToolsScreen::CreateUITab(UI::LinearLayout *list) { list->Add(new ItemHeader(si->T("Slider test"))); list->Add(new Slider(&testSliderValue_, 0, 100, 1, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); - static const char *positions[] = {"Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None"}; + // Same encoding as in GameSettingsScreen: None is -1. + static const char *positions[] = { "None", "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right" }; list->Add(new ItemHeader(si->T("Notification tests"))); list->Add(new Choice(si->T("Error")))->OnClick.Add([&](UI::EventParams &) { @@ -476,8 +476,8 @@ void DeveloperToolsScreen::CreateUITab(UI::LinearLayout *list) { }); list->Add(new ItemHeader(ac->T("Notifications"))); - list->Add(new PopupMultiChoice(&g_Config.iNotificationPos, sy->T("Notification screen position"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager())); - list->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager())); + list->Add(new PopupMultiChoice(&g_Config.iNotificationPos, sy->T("Notification screen position"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager())); + list->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, -1, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager())); #ifdef _DEBUG list->Add(new CheckBox(&pretendIngame_, ac->T("Pretend to be in-game (for testing)"))); @@ -770,8 +770,8 @@ void DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) { BrowseFileType::ANY, [this](std::string_view value, int) { mipsTracer.set_logging_path(std::string(value)); - MIPSTracerPath_ = value; - MIPSTracerPath->SetRightText(MIPSTracerPath_); + // The views may have been recreated while the dialog was open, so don't keep a pointer to the path view. + RecreateViews(); } ); } diff --git a/UI/DeveloperToolsScreen.h b/UI/DeveloperToolsScreen.h index 0eb1a025ee..be5720f9f4 100644 --- a/UI/DeveloperToolsScreen.h +++ b/UI/DeveloperToolsScreen.h @@ -53,8 +53,6 @@ private: HasIni hasTexturesIni_ = HasIni::MAYBE; bool MIPSTracerEnabled_ = false; - std::string MIPSTracerPath_ = ""; - UI::InfoItem* MIPSTracerPath = nullptr; int testSliderValue_ = 0; bool pretendIngame_ = false; diff --git a/UI/DisplayLayoutScreen.cpp b/UI/DisplayLayoutScreen.cpp index 836c83ad30..208b1e4af9 100644 --- a/UI/DisplayLayoutScreen.cpp +++ b/UI/DisplayLayoutScreen.cpp @@ -268,7 +268,7 @@ void DisplayLayoutScreen::CreateViews() { PopupSliderChoiceFloat *aspectRatio = new PopupSliderChoiceFloat(&config.fDisplayAspectRatio, 0.1f, 2.0f, 1.0f, gr->T("Aspect Ratio"), screenManager()); rightColumn->Add(aspectRatio); - aspectRatio->SetEnabledFunc([config]() { + aspectRatio->SetEnabledFunc([&config]() { return !config.bDisplayStretch && !config.bDisplayIntegerScale; }); aspectRatio->SetHasDropShadow(false); @@ -322,9 +322,11 @@ void DisplayLayoutScreen::CreateViews() { rightColumn->Add(new Spacer(12.0f)); Choice *center = new Choice(di->T("Reset")); - center->OnClick.Add([&config, portrait](UI::EventParams &) { + center->OnClick.Add([this, &config, portrait](UI::EventParams &) { // Hm, not really ideal to have to use strings here. config.ResetToDefault(portrait ? "DisplayLayout.Portrait" : "DisplayLayout.Landscape"); + // Which checkboxes are shown depends on the rotation. + RecreateViews(); }); rightColumn->Add(center); @@ -514,6 +516,7 @@ void PostProcScreen::CreateViews() { std::vector items; int selected = -1; const std::string selectedName = id_ >= (int)g_Config.vPostShaderNames.size() ? "Off" : g_Config.vPostShaderNames[id_]; + indexTranslation_.clear(); for (int i = 0; i < (int)shaders_.size(); i++) { if (!shaders_[i].visible) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 0468fd7b64..6acc975c81 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -257,6 +257,11 @@ void GameScreen::RefreshInstalledUpdate() { hasInstalledUpdate_ = FindInstalledGameUpdate(info_->id, &installedUpdate_); } +// Both panes use isHomebrew_, and which is created first depends on orientation. +void GameScreen::BeforeCreateViews() { + isHomebrew_ = info_ && info_->region == GameRegion::HOMEBREW; +} + void GameScreen::CreateContentViews(UI::ViewGroup *parent) { if (!info_) { // Shouldn't happen @@ -579,8 +584,6 @@ void GameScreen::CreateSettingsViews(UI::ViewGroup *rightColumn) { }); } - isHomebrew_ = info_ && info_->region == GameRegion::HOMEBREW; - const bool packing = GamePacker::IsPacking(gamePath_); if (fileTypeSupportCRC && !isHomebrew_ && !Reporting::HasCRC(gamePath_) && !packing) { rightColumnItems->Add(new Choice(ga->T("Calculate CRC"), ImageID("I_CHECKMARK")))->OnClick.Add([this](UI::EventParams &) { @@ -799,10 +802,8 @@ void GameScreen::OnDeleteConfig(UI::EventParams &e) { void GameScreen::OnGameSettings(UI::EventParams &e) { std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); if (info_ && info_->Ready(GameInfoFlags::PARAM_SFO)) { - std::string discID = info_->GetParamSFO().GetValueString("DISC_ID"); - if ((discID.empty() || !info_->disc_total) && gamePath_.FilePathContainsNoCase("PSP/GAME/")) - discID = g_paramSFO.GenerateFakeID(gamePath_); - screenManager()->push(new GameSettingsScreen(gamePath_, discID, true)); + // The cache has already replaced the ID of homebrew with a generated one. + screenManager()->push(new GameSettingsScreen(gamePath_, info_->id, true)); } } diff --git a/UI/GameScreen.h b/UI/GameScreen.h index c2b667dc85..9c819c5e09 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -48,6 +48,7 @@ public: bool GameRemoved() const { return gameRemoved_; } protected: + void BeforeCreateViews() override; void CreateContentViews(UI::ViewGroup *parent) override; void CreateSettingsViews(UI::ViewGroup *parent) override; void CreateContextMenu(UI::ViewGroup *parent) override; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 32390e8061..87baaed7fa 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -68,7 +68,6 @@ #include "UI/AdhocServerScreen.h" #include "Common/File/FileUtil.h" -#include "Common/File/AndroidContentURI.h" #include "Common/StringUtils.h" #include "Core/Config.h" #include "Core/ConfigValues.h" @@ -125,6 +124,13 @@ GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, g_Config.LoadGameConfig(gameID_); } + ReadConfigTemporaries(); +} + +// Also called after restoring defaults, so the old values aren't written back over them. +void GameSettingsScreen::ReadConfigTemporaries() { + // Written back on exit, so it must be read even if the System tab is never opened. + enableReports_ = Reporting::IsEnabled(); iAlternateSpeedPercent1_ = g_Config.iFpsLimit1 < 0 ? -1 : (g_Config.iFpsLimit1 * 100) / 60; iAlternateSpeedPercent2_ = g_Config.iFpsLimit2 < 0 ? -1 : (g_Config.iFpsLimit2 * 100) / 60; iAlternateSpeedPercentAnalog_ = (g_Config.iAnalogFpsLimit * 100) / 60; @@ -160,6 +166,9 @@ GameSettingsScreen::~GameSettingsScreen() { void GameSettingsScreen::PreCreateViews() { ReloadAllPostShaderInfo(screenManager()->getDrawContext()); ReloadAllThemeInfo(); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + CheckMemstickInstallState(); +#endif } static bool UsingHardwareTextureScaling() { @@ -188,30 +197,50 @@ static std::string *GPUDeviceNameSetting() { return nullptr; } -static bool PathToVisualUsbPath(Path path, std::string &outPath) { - switch (path.Type()) { - case PathType::NATIVE: - if (path.StartsWith(g_Config.memStickDirectory)) { - return g_Config.memStickDirectory.ComputePathTo(path, outPath); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +// Works out where the memstick is (installed.txt next to the exe means My Documents or a custom +// folder), and which of the two checkboxes can be changed. +void GameSettingsScreen::CheckMemstickInstallState() { + const Path &PPSSPPpath = File::GetExeDirectory(); + const Path installedFile = PPSSPPpath / "installed.txt"; + const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; + + installed_ = File::Exists(installedFile); + otherinstalled_ = false; + memstickMyDocEnabled_ = !PSP_IsInited(); + memstickOtherEnabled_ = false; + if (!installed_ && myDocsExists) { + // Disable the setting if we can't create & delete a file next to the exe. + if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) { + if (!(File::Delete(PPSSPPpath / "installedTEMP.txt"))) + memstickMyDocEnabled_ = false; + else + memstickOtherEnabled_ = !PSP_IsInited(); + } else + memstickMyDocEnabled_ = false; + } else { + if (installed_ && myDocsExists) { + FILE *testInstalled = File::OpenCFile(installedFile, "rt"); + if (testInstalled) { + char temp[2048]; + char *tempStr = fgets(temp, sizeof(temp), testInstalled); + // Skip UTF-8 encoding bytes if there are any. There are 3 of them. + if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { + tempStr += 3; + } + memstickOtherEnabled_ = !PSP_IsInited(); + if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) { + installed_ = false; + otherinstalled_ = true; + } + fclose(testInstalled); + } + } else if (!myDocsExists) { + memstickMyDocEnabled_ = false; } - break; - case PathType::CONTENT_URI: -#if PPSSPP_PLATFORM(ANDROID) - { - // Try to parse something sensible out of the content URI. - AndroidContentURI uri(path.ToString()); - outPath = uri.RootPath(); - if (startsWith(outPath, "primary:")) { - outPath = "/" + outPath.substr(8); - } - return true; } -#endif - default: - break; - } - return false; } +#endif void GameSettingsScreen::CreateTabs() { using namespace UI; @@ -355,8 +384,6 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) msaaChoice->SetChoiceIcon(i, ImageID("I_WARNING")); } } - } else { - g_Config.iMultiSampleLevel = 0; } #if PPSSPP_PLATFORM(ANDROID) @@ -444,15 +471,24 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) altSpeed1->SetFormat("%i%%"); altSpeed1->SetZeroLabel(gr->T("Unlimited")); altSpeed1->SetNegativeDisable(gr->T("Disabled")); + altSpeed1->OnChange.Add([this](UI::EventParams &) { + g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100; + }); PopupSliderChoice *altSpeed2 = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercent2_, 0, 1000, UI::NO_DEFAULT_INT, gr->T("Alternative Speed 2", "Alternative speed 2 (in %, 0 = unlimited)"), 5, screenManager(), gr->T("%, 0:unlimited"))); altSpeed2->SetFormat("%i%%"); altSpeed2->SetZeroLabel(gr->T("Unlimited")); altSpeed2->SetNegativeDisable(gr->T("Disabled")); + altSpeed2->OnChange.Add([this](UI::EventParams &) { + g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100; + }); if (analogSpeedMapped_) { PopupSliderChoice *analogSpeed = graphicsSettings->Add(new PopupSliderChoice(&iAlternateSpeedPercentAnalog_, 1, 1000, UI::NO_DEFAULT_INT, gr->T("Analog alternative speed", "Analog alternative speed (in %)"), 5, screenManager(), "%")); - altSpeed2->SetFormat("%i%%"); + analogSpeed->SetFormat("%i%%"); + analogSpeed->OnChange.Add([this](UI::EventParams &) { + g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100; + }); } graphicsSettings->Add(new ItemHeader(gr->T("Speed Hacks", "Speed Hacks (can cause rendering errors!)"))); @@ -964,13 +1000,34 @@ MacAddressChooser::MacAddressChooser(RequesterToken token, Path gamePath, std::s }); } +// The nickname is shown on both the Networking and System tabs. +UI::View *GameSettingsScreen::AddNicknameChoice(UI::ViewGroup *parent) { + using namespace UI; + + auto sy = GetI18NCategory(I18NCat::SYSTEM); + PopupTextInputChoice *nickname = parent->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Nickname"), "", 32, screenManager())); + if (g_Config.sNickName == "PPSSPP") { + nickname->SetIconRight(ImageID("I_WARNING")); + } + nickname->OnChange.Add([this](UI::EventParams &e) { + // Copy to infrastructure name if valid and not already set. + if (g_Config.sInfrastructureUsername.empty()) { + if (g_Config.sNickName == SanitizeString(g_Config.sNickName, StringRestriction::AlphaNumDashUnderscore, 3, 16)) { + g_Config.sInfrastructureUsername = g_Config.sNickName; + } + } + // Updates the warning icon, and the username notice on the Networking tab. + RecreateViews(); + }); + return nickname; +} + void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSettings) { using namespace UI; auto n = GetI18NCategory(I18NCat::NETWORKING); auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); auto di = GetI18NCategory(I18NCat::DIALOG); - auto sy = GetI18NCategory(I18NCat::SYSTEM); networkingSettings->Add(new ItemHeader(ms->T("Networking"))); @@ -1028,28 +1085,20 @@ void GameSettingsScreen::CreateNetworkingSettings(UI::ViewGroup *networkingSetti relayModePopup->SetEnabled(!PSP_IsInited()); networkingSettings->Add(new SettingHint(n->T("PacketRelayHint", "Available on servers that provide 'aemu_postoffice' packet relay, like socom.cc. Disable this for LAN or VPN play. Can be more reliable, but sometimes slower."), relayModePopup)); - PopupTextInputChoice *nickname = networkingSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Nickname"), "", 32, screenManager())); - if (g_Config.sNickName == "PPSSPP") { - nickname->SetIconRight(ImageID("I_WARNING")); - } - nickname->OnChange.Add([this](UI::EventParams &e) { - RecreateViews(); - }); - networkingSettings->Add(new SettingHint(n->T("Try to pick a unique nickname for ad hoc play"), nullptr)); + View *nicknameChoice = AddNicknameChoice(networkingSettings); + networkingSettings->Add(new SettingHint(n->T("Try to pick a unique nickname for ad hoc play"), nicknameChoice)); networkingSettings->Add(new CheckBox(&g_Config.bEnableAdhocServer, n->T("Enable built-in ad hoc server")))->OnClick.Add(requestLocalNetwork); CheckBox *relay_mode_enet = networkingSettings->Add(new CheckBox(&g_Config.bAdhocUseUdp, n->T("Connect to adhoc server using UDP (ENet) instead of TCP"))); relay_mode_enet->SetEnabled(!PSP_IsInited()); - relay_mode_enet->OnClick.Add([this](UI::EventParams &e) { - RecreateViews(); - }); - networkingSettings->Add(new SettingHint(n->T("Try switching between TCP/UDP if you suffer frequent adhoc server disconnects. Some network environments favor TCP and some UDP."), nullptr)); + networkingSettings->Add(new SettingHint(n->T("Try switching between TCP/UDP if you suffer frequent adhoc server disconnects. Some network environments favor TCP and some UDP."), relay_mode_enet)); CheckBox *relay_mode_enet_pdp_reliable = networkingSettings->Add(new CheckBox(&g_Config.bAdhocRelayModeEnetPdpReliable, n->T("Reliable datagram packet delivery on packet relay in UDP mode"))); - relay_mode_enet_pdp_reliable->SetEnabled(!PSP_IsInited() && g_Config.bAdhocUseUdp); - networkingSettings->Add(new SettingHint(n->T("When disabled, packet loss on datagram adhoc sockets will be exposed to game's netcode. Keep this enabled if you don't know what that means."), nullptr)); - + relay_mode_enet_pdp_reliable->SetEnabledFunc([] { + return !PSP_IsInited() && g_Config.bAdhocUseUdp; + }); + networkingSettings->Add(new SettingHint(n->T("When disabled, packet loss on datagram adhoc sockets will be exposed to game's netcode. Keep this enabled if you don't know what that means."), relay_mode_enet_pdp_reliable)); networkingSettings->Add(new ItemHeader(n->T("Infrastructure"))); if (g_Config.sInfrastructureUsername.empty()) { @@ -1298,74 +1347,31 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { #if PPSSPP_PLATFORM(ANDROID) if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) != DEVICE_TYPE_VR) { - memstickDisplay_ = g_Config.memStickDirectory.ToVisualString(); - auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder"), I18NCat::NONE)); + auto memstickPath = systemSettings->Add(new ChoiceWithCallbackValueDisplay(sy->T("Memory Stick folder"), []() { + return g_Config.memStickDirectory.ToVisualString(); + })); memstickPath->SetEnabled(!PSP_IsInited()); memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen); - - // Display USB path for convenience. - std::string usbPath; - if (PathToVisualUsbPath(g_Config.memStickDirectory, usbPath)) { - if (usbPath.empty()) { - // Probably it's just the root. So let's add PSP to make it clear. - usbPath = "/PSP"; - } - } } #elif defined(_WIN32) #if PPSSPP_PLATFORM(UWP) - memstickDisplay_ = g_Config.memStickDirectory.ToVisualString(); - auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&memstickDisplay_, sy->T("Memory Stick folder"), I18NCat::NONE)); + auto memstickPath = systemSettings->Add(new ChoiceWithCallbackValueDisplay(sy->T("Memory Stick folder"), []() { + return g_Config.memStickDirectory.ToVisualString(); + })); memstickPath->SetEnabled(!PSP_IsInited()); memstickPath->OnClick.Handle(this, &GameSettingsScreen::OnShowMemstickScreen); #else - SavePathInMyDocumentChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Memory Stick in My Documents"))); - SavePathInMyDocumentChoice->SetEnabled(!PSP_IsInited()); - SavePathInMyDocumentChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickMyDoc); - SavePathInOtherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Memory Stick in installed.txt"))); - SavePathInOtherChoice->SetEnabled(false); - SavePathInOtherChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickOther); - const bool myDocsExists = W32Util::UserDocumentsPath().size() != 0; + CheckBox *myDocsChoice = systemSettings->Add(new CheckBox(&installed_, sy->T("Memory Stick in My Documents"))); + myDocsChoice->SetEnabledPtr(&memstickMyDocEnabled_); + myDocsChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickMyDoc); + CheckBox *otherChoice = systemSettings->Add(new CheckBox(&otherinstalled_, sy->T("Memory Stick in installed.txt"))); + otherChoice->SetEnabledPtr(&memstickOtherEnabled_); + otherChoice->OnClick.Handle(this, &GameSettingsScreen::OnMemoryStickOther); if (!System_GetPropertyBool(SYSPROP_CAN_GET_FREE_SPACE_FAST)) { // It might be slow, so use an option. systemSettings->Add(new CheckBox(&g_Config.bReportAccurateFreeStorageSpace, sy->T("Report free storage space accurately"))); } - - const Path &PPSSPPpath = File::GetExeDirectory(); - const Path installedFile = PPSSPPpath / "installed.txt"; - installed_ = File::Exists(installedFile); - otherinstalled_ = false; - if (!installed_ && myDocsExists) { - if (File::CreateEmptyFile(PPSSPPpath / "installedTEMP.txt")) { - // Disable the setting whether cannot create & delete file - if (!(File::Delete(PPSSPPpath / "installedTEMP.txt"))) - SavePathInMyDocumentChoice->SetEnabled(false); - else - SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); - } else - SavePathInMyDocumentChoice->SetEnabled(false); - } else { - if (installed_ && myDocsExists) { - FILE *testInstalled = File::OpenCFile(installedFile, "rt"); - if (testInstalled) { - char temp[2048]; - char *tempStr = fgets(temp, sizeof(temp), testInstalled); - // Skip UTF-8 encoding bytes if there are any. There are 3 of them. - if (tempStr && strncmp(tempStr, "\xEF\xBB\xBF", 3) == 0) { - tempStr += 3; - } - SavePathInOtherChoice->SetEnabled(!PSP_IsInited()); - if (tempStr && strlen(tempStr) != 0 && strcmp(tempStr, "\n") != 0) { - installed_ = false; - otherinstalled_ = true; - } - fclose(testInstalled); - } - } else if (!myDocsExists) { - SavePathInMyDocumentChoice->SetEnabled(false); - } - } #endif #endif systemSettings->Add(new CheckBox(&g_Config.bMemStickInserted, sy->T("Memory Stick inserted"))); @@ -1373,9 +1379,6 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { sizeChoice->SetFormat("%d GB"); systemSettings->Add(new ItemHeader(sy->T("Help the PPSSPP team"))); - if (!enableReportsSet_) - enableReports_ = Reporting::IsEnabled(); - enableReportsSet_ = true; CheckBox *enableReportsCheckbox; enableReportsCheckbox = new CheckBox(&enableReports_, sy->T("Enable Compatibility Server Reports")); enableReportsCheckbox->SetEnabledFunc([]() { return Reporting::IsSupported(); }); @@ -1479,14 +1482,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { systemSettings->Add(new PopupMultiChoice(&g_Config.iLanguage, psps->T("Game language"), defaultLanguages, -1, ARRAY_SIZE(defaultLanguages), I18NCat::PSPSETTINGS, screenManager())); static const char *models[] = { "PSP-1000", "PSP-2000/3000" }; systemSettings->Add(new PopupMultiChoice(&g_Config.iPSPModel, sy->T("PSP Model"), models, 0, ARRAY_SIZE(models), I18NCat::SYSTEM, screenManager()))->SetEnabled(!PSP_IsInited()); - systemSettings->Add(new PopupTextInputChoice(GetRequesterToken(), &g_Config.sNickName, sy->T("Nickname"), "", 32, screenManager()))->OnChange.Add([](UI::EventParams &e) { - // Copy to infrastructure name if valid and not already set. - if (g_Config.sInfrastructureUsername.empty()) { - if (g_Config.sNickName == SanitizeString(g_Config.sNickName, StringRestriction::AlphaNumDashUnderscore, 3, 16)) { - g_Config.sInfrastructureUsername = g_Config.sNickName; - } - } - }); + UI::View *nicknameChoice = AddNicknameChoice(systemSettings); systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Daylight savings"))); static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY" }; systemSettings->Add(new PopupMultiChoice(&g_Config.iDateFormat, sy->T("Date Format"), dateFormat, 0, ARRAY_SIZE(dateFormat), I18NCat::SYSTEM, screenManager())); @@ -1607,7 +1603,7 @@ void GameSettingsScreen::OnMemoryStickOther(UI::EventParams &e) { } else { File::Delete(PPSSPPpath / "installed.txt"); - SavePathInMyDocumentChoice->SetEnabled(true); + memstickMyDocEnabled_ = true; otherinstalled_ = false; installed_ = false; g_Config.memStickDirectory = PPSSPPpath / "memstick"; @@ -1697,9 +1693,9 @@ void GameSettingsScreen::dialogFinished(const Screen *dialog, DialogResult resul } if (result == DialogResult::DR_OK) { - g_Config.iFpsLimit1 = iAlternateSpeedPercent1_ < 0 ? -1 : (iAlternateSpeedPercent1_ * 60) / 100; - g_Config.iFpsLimit2 = iAlternateSpeedPercent2_ < 0 ? -1 : (iAlternateSpeedPercent2_ * 60) / 100; - g_Config.iAnalogFpsLimit = (iAlternateSpeedPercentAnalog_ * 60) / 100; + if (equals(dialog->tag(), "RestoreSettingsScreen")) { + ReadConfigTemporaries(); + } recreate = true; } @@ -1823,6 +1819,7 @@ void GameSettingsScreen::OnAudioDevice(UI::EventParams &e) { void GameSettingsScreen::CallbackRestoreDefaults(bool yes) { if (yes) { g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS); + ReadConfigTemporaries(); } System_Notify(SystemNotification::UI); } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index e99a8d9c2d..7c15ca0a44 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -57,18 +57,18 @@ private: void CreateToolsSettings(UI::ViewGroup *tools); void CreateSystemSettings(UI::ViewGroup *systemSettings); void CreateVRSettings(UI::ViewGroup *vrSettings); + UI::View *AddNicknameChoice(UI::ViewGroup *parent); std::string gameID_; -#ifdef _WIN32 - UI::CheckBox *SavePathInMyDocumentChoice = nullptr; - UI::CheckBox *SavePathInOtherChoice = nullptr; - // Used to enable/disable the above two options. +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + void CheckMemstickInstallState(); + bool installed_ = false; bool otherinstalled_ = false; + bool memstickMyDocEnabled_ = false; + bool memstickOtherEnabled_ = false; #endif - std::string memstickDisplay_; - // Global settings handlers void OnChangeBackground(UI::EventParams &e); void OnRestoreDefaultSettings(UI::EventParams &e); @@ -86,6 +86,7 @@ private: void OnSustainedPerformanceModeChange(UI::EventParams &e); void TriggerRestartOrDo(std::function callback); + void ReadConfigTemporaries(); // Temporaries to convert setting types, cache enabled, etc. int iAlternateSpeedPercent1_ = 0; @@ -93,7 +94,6 @@ private: int iAlternateSpeedPercentAnalog_ = 0; int prevInflightFrames_ = -1; bool enableReports_ = false; - bool enableReportsSet_ = false; bool analogSpeedMapped_ = false; // edit the game-specific settings and restore the global settings after exiting diff --git a/UI/GamepadEmu.cpp b/UI/GamepadEmu.cpp index d49edbfdf3..79833e70d4 100644 --- a/UI/GamepadEmu.cpp +++ b/UI/GamepadEmu.cpp @@ -95,11 +95,12 @@ GamepadComponent::GamepadComponent(std::string_view key, UI::LayoutParams *layou static void rotateTouchHelper(float &dx, float &dy) { // rotates dx and dy depending on rotation count // modified from controlmapper.cpp - if (!g_Config.displayLayoutLandscape.bRotateControlsWithScreen) { + const DisplayLayoutConfig &layout = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); + if (!layout.bRotateControlsWithScreen) { return; } int rotations = 0; - switch (g_Config.displayLayoutLandscape.iInternalScreenRotation) { + switch (layout.iInternalScreenRotation) { case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break; case ROTATION_LOCKED_VERTICAL: rotations = 3; break; case ROTATION_LOCKED_VERTICAL180: rotations = 1; break; @@ -459,8 +460,9 @@ void PSPDpad::Draw(UIContext &dc) { return; // from control mapper.cpp int rotations = 0; - if (g_Config.displayLayoutLandscape.bRotateControlsWithScreen) { - switch (g_Config.displayLayoutLandscape.iInternalScreenRotation) { + const DisplayLayoutConfig &layout = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation()); + if (layout.bRotateControlsWithScreen) { + switch (layout.iInternalScreenRotation) { case ROTATION_LOCKED_HORIZONTAL180: rotations = 2; break; case ROTATION_LOCKED_VERTICAL: rotations = 3; break; case ROTATION_LOCKED_VERTICAL180: rotations = 1; break; diff --git a/UI/InstallZipScreen.cpp b/UI/InstallZipScreen.cpp index 4a2c94c6c6..66c859edc6 100644 --- a/UI/InstallZipScreen.cpp +++ b/UI/InstallZipScreen.cpp @@ -78,11 +78,6 @@ void InstallZipScreen::CreateSettingsViews(UI::ViewGroup *parent) { std::string shortFilename = zipPath_.GetFilename(); bool showDeleteCheckbox = false; - returnToHomebrew_ = false; - installChoice_ = nullptr; - playChoice_ = nullptr; - doneView_ = nullptr; - existingSaveView_ = nullptr; std::vector destOptions; @@ -144,12 +139,6 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) { std::string shortFilename = zipPath_.GetFilename(); - returnToHomebrew_ = false; - installChoice_ = nullptr; - playChoice_ = nullptr; - doneView_ = nullptr; - existingSaveView_ = nullptr; - overwriteWarning_ = nullptr; destFolders_.clear(); std::vector destOptions; @@ -320,8 +309,13 @@ void InstallZipScreen::CreateContentViews(UI::ViewGroup *parent) { } void InstallZipScreen::BeforeCreateViews() { - File::FileInfo fileInfo; - File::GetFileInfo(zipPath_, &fileInfo); + // The two panes are created in a different order depending on orientation, so neither can reset the other's views. + returnToHomebrew_ = false; + installChoice_ = nullptr; + playChoice_ = nullptr; + doneView_ = nullptr; + existingSaveView_ = nullptr; + overwriteWarning_ = nullptr; } bool InstallZipScreen::key(const KeyInput &key) { diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index 3b13b21dff..f00e6245b4 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -442,6 +442,9 @@ void MemStickScreen::Browse(UI::EventParams ¶ms) { nw->T("Error"), // from the networking category of strings. ms->T("MissingFileBrowserError", "This device is missing a file/folder browser. If you disabled the 'Files' app, please enable it and try again."), di->T("More info"), di->T("Cancel"), [](bool ok) { + if (!ok) { + return; + } System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/docs/troubleshooting/cant-pick-folder/"); } )); @@ -450,7 +453,8 @@ void MemStickScreen::Browse(UI::EventParams ¶ms) { } void MemStickScreen::dialogFinished(const Screen *dialog, DialogResult result) { - if (result == DialogResult::DR_OK) { + // Other popups, like the missing file browser error, also finish with DR_OK. + if (result == DialogResult::DR_OK && !strcmp(dialog->tag(), "ConfirmMemstickMove")) { INFO_LOG(Log::System, "Confirmation screen done - moving on."); // There's a screen manager bug if we call TriggerFinish directly. // Can't be bothered right now, so we pick this up in update(). @@ -658,6 +662,7 @@ void ConfirmMemstickMoveScreen::FinishFolderMove() { if (!SwitchMemstickFolderTo(newMemstickFolder_)) { // TODO: More precise errors. error_ = ms->T("That folder doesn't work as a memstick folder."); + RecreateViews(); return; } diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index fc1c75d7d5..6a551370c1 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -133,8 +133,10 @@ protected: } grid->Add(new Choice(pa->T("Save State"), ImageID("I_FILE_SAVE")))->OnClick.Handle(this, &ScreenshotViewScreen::OnSaveState); - // We can unconditionally show the load state button, because you can only pop this dialog up if a state exists. - grid->Add(new Choice(pa->T("Load State"), ImageID("I_FOLDER_OPEN")))->OnClick.Handle(this, &ScreenshotViewScreen::OnLoadState); + // You can only pop this dialog up if a state exists. Hardcore mode doesn't allow loading, like in SaveSlotView. + if (!Achievements::HardcoreModeActive()) { + grid->Add(new Choice(pa->T("Load State"), ImageID("I_FOLDER_OPEN")))->OnClick.Handle(this, &ScreenshotViewScreen::OnLoadState); + } grid->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN")))->OnClick.Handle(this, &ScreenshotViewScreen::OnDeleteState); if (undoButton) { grid->Add(undoButton)->OnClick.Handle(this, &ScreenshotViewScreen::OnUndoState); diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index 35b7aa5db4..f8d91817db 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -290,6 +290,11 @@ void RetroAchievementsLeaderboardScreen::FetchEntries() { void RetroAchievementsLeaderboardScreen::CreateTabs() { auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); const rc_client_leaderboard_t *leaderboard = rc_client_get_leaderboard_info(Achievements::GetClient(), leaderboardID_); + if (!leaderboard) { + // The game was unloaded. There has to be a tab, though. + AddTab("AchievementsLeaderboard", "", [](UI::LinearLayout *parent) {}); + return; + } using namespace UI; AddTab("AchievementsLeaderboard", leaderboard->title, [this, leaderboard](UI::LinearLayout *parent) { diff --git a/UI/Store.cpp b/UI/Store.cpp index 342e935aac..ab2f300dcf 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -354,7 +354,12 @@ void ProductView::OnLaunchClick(UI::EventParams &e) { StoreScreen::StoreScreen() : UISimpleBaseDialogScreen(Path(), SimpleDialogFlags::Default) { lang_ = g_Config.sLanguageIni; + StartListingDownload(); +} + +void StoreScreen::StartListingDownload() { loading_ = true; + connectionError_ = false; std::string indexPath = StoreBaseUrl() + "index.json"; const char *acceptMime = "application/json, */*; q=0.8"; @@ -480,7 +485,7 @@ void StoreScreen::CreateDialogViews(UI::ViewGroup *parent) { // TODO: Similar apps, etc etc productPanel_ = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(0.5f)); - leftScroll->SetTag("StoreMainProduct"); + productPanel_->SetTag("StoreMainProduct"); content->Add(productPanel_); ProductItemView *selectedItem = GetSelectedItem(); @@ -532,6 +537,7 @@ void StoreScreen::OnGameLaunch(UI::EventParams &e) { } void StoreScreen::OnRetry(UI::EventParams &e) { + StartListingDownload(); RecreateViews(); } diff --git a/UI/Store.h b/UI/Store.h index 9d31f03560..c6e5b90cda 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -77,6 +77,7 @@ protected: std::string_view GetTitle() const override; private: + void StartListingDownload(); void ParseListing(const std::string &json); ProductItemView *GetSelectedItem(); diff --git a/UI/SystemInfoScreen.cpp b/UI/SystemInfoScreen.cpp index 4114832bf8..fdef8aa57e 100644 --- a/UI/SystemInfoScreen.cpp +++ b/UI/SystemInfoScreen.cpp @@ -38,7 +38,7 @@ void SystemInfoScreen::update() { // TODO: How can we de-duplicate this and SystemInfoScreen::CreateTabs? static void CopySummaryToClipboard(Draw::DrawContext *draw) { auto di = GetI18NCategory(I18NCat::DIALOG); - auto si = GetI18NCategory(I18NCat::DIALOG); + auto si = GetI18NCategory(I18NCat::SYSINFO); char *summary = new char[100000]; StringWriter w(summary, 100000); @@ -145,7 +145,7 @@ void SystemInfoScreen::CreateDeviceInfoTab(UI::LinearLayout *deviceSpecs) { UI::CollapsibleSection *cpuInfo = deviceSpecs->Add(new UI::CollapsibleSection(si->T("CPU Information"))); // Don't bother showing the CPU name if we don't have one. - if (equals(cpu_info.brand_string, "Unknown")) { + if (!equals(cpu_info.brand_string, "Unknown")) { cpuInfo->Add(new InfoItem(si->T("CPU Name"), cpu_info.brand_string)); } @@ -393,7 +393,8 @@ void SystemInfoScreen::CreateBuildConfigTab(UI::LinearLayout *buildConfig) { } System_CopyStringToClipboard(buildConfigStr.c_str()); auto di = GetI18NCategory(I18NCat::DIALOG); - g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), di->T("Build Configuration")), 0.0f, "copyToClip"); + auto si = GetI18NCategory(I18NCat::SYSINFO); + g_OSD.Show(OSDType::MESSAGE_INFO, ApplySafeSubstitutions(di->T("Copied to clipboard: %1"), si->T("Build Configuration")), 0.0f, "copyToClip"); }); for (auto &item : buildConfigItems) { diff --git a/UI/TabbedDialogScreen.cpp b/UI/TabbedDialogScreen.cpp index ca626a7760..81eae5bc48 100644 --- a/UI/TabbedDialogScreen.cpp +++ b/UI/TabbedDialogScreen.cpp @@ -114,11 +114,6 @@ void UITabbedBaseDialogScreen::CreateViews() { // Let the subclass create its tabs. CreateTabs(); - if (currentTabSetting_) { - tabHolder_->SetInitialTab(*currentTabSetting_); - } else { - tabHolder_->EnsureTab(tabHolder_->GetCurrentTab()); - } tabHolder_->OnChangeTab.Add([this](UI::EventParams &e) { if (currentTabSetting_) { @@ -151,6 +146,13 @@ void UITabbedBaseDialogScreen::CreateViews() { } } + // After the search tab, which the saved tab can be. + if (currentTabSetting_) { + tabHolder_->SetInitialTab(*currentTabSetting_); + } else { + tabHolder_->EnsureTab(tabHolder_->GetCurrentTab()); + } + // Need to handle recreates, both important and accidental ones. if (!searchFilter_.empty()) { ApplySearchFilter();