Merge pull request #22721 from hrydgard/settings-bugfixes

Assorted UI bugfixes
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-10-22 11:31:42 +02:00
commit 4932e0466d
24 files changed
+222 -205

No files matched your search

+4
View File
@@ -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);
+3
View File
@@ -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() {
+2 -1
View File
@@ -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);
+9 -10
View File
@@ -308,20 +308,16 @@ void ControlMappingScreen::OnAutoConfigure(UI::EventParams &params) {
}
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;
}
-2
View File
@@ -57,8 +57,6 @@ protected:
private:
void OnAutoConfigure(UI::EventParams &params);
void dialogFinished(const Screen *dialog, DialogResult result) override;
std::vector<SingleControlMapper *> mappers_;
int keyMapGeneration_ = -1;
+10 -9
View File
@@ -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();
};
+1 -8
View File
@@ -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_;
};
+11 -11
View File
@@ -160,14 +160,11 @@ bool CwCheatScreen::WantsTextInput() const {
bool CwCheatScreen::TryLoadCheatInfo() {
std::shared_ptr<GameInfo> 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<const CwCheatDownloadPopupScreen *>(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 &params) {
}
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;
}
+7 -7
View File
@@ -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();
}
);
}
-2
View File
@@ -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;
+5 -2
View File
@@ -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<std::string> 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)
+7 -6
View File
@@ -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<GameInfo> 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));
}
}
+1
View File
@@ -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;
+108 -111
View File
@@ -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);
}
+7 -7
View File
@@ -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<void()> 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
+6 -4
View File
@@ -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;
+7 -13
View File
@@ -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<Path> 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<Path> 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) {
+6 -1
View File
@@ -442,6 +442,9 @@ void MemStickScreen::Browse(UI::EventParams &params) {
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 &params) {
}
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;
}
+4 -2
View File
@@ -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);
+5
View File
@@ -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) {
+7 -1
View File
@@ -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();
}
+1
View File
@@ -77,6 +77,7 @@ protected:
std::string_view GetTitle() const override;
private:
void StartListingDownload();
void ParseListing(const std::string &json);
ProductItemView *GetSelectedItem();
+4 -3
View File
@@ -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) {
+7 -5
View File
@@ -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();