From 1ae76e871befdcabcdec253005461f76fd781b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 10 Nov 2025 19:07:57 +0100 Subject: [PATCH] Partial game-info-screen rework --- Common/UI/ViewGroup.h | 2 + UI/GameScreen.cpp | 185 ++++++++++++++++++++------------------ UI/GameScreen.h | 12 ++- UI/MainScreen.cpp | 20 +++-- UI/MainScreen.h | 5 +- UI/MiscScreens.cpp | 7 +- UI/RemoteISOScreen.cpp | 4 +- UI/SimpleDialogScreen.cpp | 16 +++- UI/SimpleDialogScreen.h | 3 + UI/UploadScreen.cpp | 2 +- 10 files changed, 147 insertions(+), 109 deletions(-) diff --git a/Common/UI/ViewGroup.h b/Common/UI/ViewGroup.h index 899f80be93..cda6ca77a9 100644 --- a/Common/UI/ViewGroup.h +++ b/Common/UI/ViewGroup.h @@ -170,6 +170,8 @@ public: : LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(grav), margins(mgn), hasMargins_(true) {} LinearLayoutParams(Size w, Size h, const Margins &mgn) : LayoutParams(w, h, LP_LINEAR), weight(0.0f), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {} + LinearLayoutParams(Size w, Size h, Gravity grav, const Margins &mgn) + : LayoutParams(w, h, LP_LINEAR), weight(0.0f), gravity(grav), margins(mgn), hasMargins_(true) {} LinearLayoutParams(Size w, Size h, float wgt, const Margins &mgn) : LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(Gravity::G_TOPLEFT), margins(mgn), hasMargins_(true) {} LinearLayoutParams(const Margins &mgn) diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 1f7624b313..18747748d6 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -56,13 +56,11 @@ constexpr GameInfoFlags g_desiredFlags = GameInfoFlags::PARAM_SFO | GameInfoFlags::ICON | GameInfoFlags::PIC0 | GameInfoFlags::PIC1 | GameInfoFlags::UNCOMPRESSED_SIZE | GameInfoFlags::SIZE; -GameScreen::GameScreen(const Path &gamePath, bool inGame) : UIBaseDialogScreen(gamePath), inGame_(inGame) { +GameScreen::GameScreen(const Path &gamePath, bool inGame) : UITwoPaneBaseDialogScreen(gamePath), inGame_(inGame) { g_BackgroundAudio.SetGame(gamePath); System_PostUIMessage(UIMessage::GAME_SELECTED, gamePath.ToString()); - // Start fetching info, we'll display it as it arrives. We keep the known fetch status in knownFlags_, - // and recreate views whenever info->hasFlags != knownFlags_. - g_gameInfoCache->GetInfo(NULL, gamePath_, g_desiredFlags, &knownFlags_); + info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, g_desiredFlags, &knownFlags_); } GameScreen::~GameScreen() { @@ -109,14 +107,26 @@ void GameScreen::update() { } } -void GameScreen::CreateViews() { - GameInfoFlags hasFlags; - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, g_desiredFlags, &hasFlags); - if (!info) { +static bool FileTypeSupportsCRC(IdentifiedFileType fileType) { + switch (fileType) { + case IdentifiedFileType::PSP_PBP: + case IdentifiedFileType::PSP_PBP_DIRECTORY: + case IdentifiedFileType::PSP_ISO_NP: + case IdentifiedFileType::PSP_ISO: + return true; + default: + return false; + } +} + +void GameScreen::CreateContentViews(UI::LinearLayout *parent) { + if (!info_) { // Shouldn't happen return; } + const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + auto di = GetI18NCategory(I18NCat::DIALOG); auto ga = GetI18NCategory(I18NCat::GAME); @@ -127,74 +137,64 @@ void GameScreen::CreateViews() { Margins actionMenuMargins(0, 15, 15, 0); - root_ = new LinearLayout(ORIENT_HORIZONTAL); + ViewGroup *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f, Margins(8))); + parent->Add(leftColumn); - ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f)); - root_->Add(leftColumn); - - bool fileTypeSupportCRC = false; - if (info) { - switch (info->fileType) { - case IdentifiedFileType::PSP_PBP: - case IdentifiedFileType::PSP_PBP_DIRECTORY: - case IdentifiedFileType::PSP_ISO_NP: - case IdentifiedFileType::PSP_ISO: - fileTypeSupportCRC = true; - break; - - default: - break; - } - } - - AddStandardBack(leftColumn); - - ViewGroup *badgeHolder = new LinearLayout(ORIENT_HORIZONTAL, new AnchorLayoutParams(10, 10, 110, NONE)); - LinearLayout *mainGameInfo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); - mainGameInfo->SetSpacing(3.0f); + const bool fileTypeSupportCRC = FileTypeSupportsCRC(info_->fileType); // Need an explicit size here because homebrew uses screenshots as icons. - badgeHolder->Add(new GameIconView(gamePath_, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0)))); - badgeHolder->Add(mainGameInfo); - leftColumn->Add(badgeHolder); + LinearLayout *mainGameInfo; + if (portrait) { + mainGameInfo = new LinearLayout(ORIENT_VERTICAL); + leftColumn->Add(new Spacer(8.0f)); + leftColumn->Add(new GameIconView(gamePath_, 2.0f, new LinearLayoutParams(UI::Margins(0)))); + leftColumn->Add(mainGameInfo); + } else { + mainGameInfo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)); + ViewGroup *badgeHolder = new LinearLayout(ORIENT_HORIZONTAL); + badgeHolder->Add(new GameIconView(gamePath_, 2.0f, new LinearLayoutParams(144 * 2, 80 * 2, UI::Margins(0)))); + badgeHolder->Add(mainGameInfo); + leftColumn->Add(badgeHolder); + } + mainGameInfo->SetSpacing(3.0f); - LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE)); - leftColumn->Add(infoLayout); - - if (hasFlags & GameInfoFlags::PARAM_SFO) { + if (knownFlags_ & GameInfoFlags::PARAM_SFO) { // TODO: Add non-translated title here if available in gameDB. - TextView *tvTitle = mainGameInfo->Add(new TextView(info->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + TextView *tvTitle = mainGameInfo->Add(new TextView(info_->GetTitle(), ALIGN_LEFT | FLAG_WRAP_TEXT, false, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvTitle->SetShadow(true); - TextView *tvID = mainGameInfo->Add(new TextView(ReplaceAll(info->id_version, "_", " v"), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + TextView *tvID = mainGameInfo->Add(new TextView(ReplaceAll(info_->id_version, "_", " v"), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvID->SetShadow(true); - if (info->region != GameRegion::UNKNOWN) { - TextView *tvRegion = mainGameInfo->Add(new TextView(ga->T(GameRegionToString(info->region)), ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + if (info_->region != GameRegion::UNKNOWN) { + TextView *tvRegion = mainGameInfo->Add(new TextView(ga->T(GameRegionToString(info_->region)), ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvRegion->SetShadow(true); } else { - TextView *tvFileType = mainGameInfo->Add(new TextView(ga->T(IdentifiedFileTypeToString(info->fileType)), ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + TextView *tvFileType = mainGameInfo->Add(new TextView(ga->T(IdentifiedFileTypeToString(info_->fileType)), ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvFileType->SetShadow(true); } } - if ((hasFlags & GameInfoFlags::UNCOMPRESSED_SIZE) && (hasFlags & GameInfoFlags::SIZE)) { + LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE)); + leftColumn->Add(infoLayout); + + if ((knownFlags_ & GameInfoFlags::UNCOMPRESSED_SIZE) && (knownFlags_ & GameInfoFlags::SIZE)) { char temp[256]; - snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("Game"), NiceSizeFormat(info->gameSizeOnDisk).c_str()); - if (info->gameSizeUncompressed != info->gameSizeOnDisk) { + snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("Game"), NiceSizeFormat(info_->gameSizeOnDisk).c_str()); + if (info_->gameSizeUncompressed != info_->gameSizeOnDisk) { size_t len = strlen(temp); - snprintf(temp + len, sizeof(temp) - len, " (%s: %s)", ga->T_cstr("Uncompressed"), NiceSizeFormat(info->gameSizeUncompressed).c_str()); + snprintf(temp + len, sizeof(temp) - len, " (%s: %s)", ga->T_cstr("Uncompressed"), NiceSizeFormat(info_->gameSizeUncompressed).c_str()); } TextView *tvGameSize = mainGameInfo->Add(new TextView(temp, ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvGameSize->SetShadow(true); - - if (info->saveDataSize > 0) { - snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("SaveData"), NiceSizeFormat(info->saveDataSize).c_str()); + + if (info_->saveDataSize > 0) { + snprintf(temp, sizeof(temp), "%s: %s", ga->T_cstr("SaveData"), NiceSizeFormat(info_->saveDataSize).c_str()); TextView *tvSaveDataSize = infoLayout->Add(new TextView(temp, ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvSaveDataSize->SetShadow(true); } - if (info->installDataSize > 0) { - snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T_cstr("InstallData"), (float)(info->installDataSize) / 1024.f / 1024.f, ga->T_cstr("MB")); + if (info_->installDataSize > 0) { + snprintf(temp, sizeof(temp), "%s: %1.2f %s", ga->T_cstr("InstallData"), (float)(info_->installDataSize) / 1024.f / 1024.f, ga->T_cstr("MB")); TextView *tvInstallDataSize = infoLayout->Add(new TextView(temp, ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvInstallDataSize->SetShadow(true); } @@ -203,7 +203,7 @@ void GameScreen::CreateViews() { infoLayout->Add(new TextView(gamePath_.ToVisualString(), ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetShadow(true); std::string str; - if (g_Config.TimeTracker().GetPlayedTimeString(info->id, &str)) { + if (g_Config.TimeTracker().GetPlayedTimeString(info_->id, &str)) { TextView *tvPlayTime = infoLayout->Add(new TextView(str, ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); tvPlayTime->SetShadow(true); tvPlayTime->SetText(str); @@ -222,7 +222,7 @@ void GameScreen::CreateViews() { tvCRC->SetShadow(true); if (System_GetPropertyBool(SYSPROP_HAS_TEXT_CLIPBOARD)) { - Button *tvCRCCopy = crcHoriz->Add(new Button(di->T("Copy to clipboard"), new LinearLayoutParams(0.0, Gravity::G_VCENTER))); + Choice *tvCRCCopy = crcHoriz->Add(new Choice(ImageID("I_FILE_COPY"), new LinearLayoutParams(0.0, Gravity::G_VCENTER))); tvCRCCopy->OnClick.Add([this](UI::EventParams &) { u32 crc = Reporting::RetrieveCRC(gamePath_); char buffer[16]; @@ -235,7 +235,7 @@ void GameScreen::CreateViews() { // Let's check the CRC in the game database, looking up the ID and also matching the crc. std::vector dbInfos; - if ((hasFlags & GameInfoFlags::PARAM_SFO) && g_gameDB.GetGameInfos(info->id_version, &dbInfos)) { + if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && g_gameDB.GetGameInfos(info_->id_version, &dbInfos)) { bool found = false; for (auto &dbInfo : dbInfos) { if (dbInfo.crc == crcVal) { @@ -250,15 +250,15 @@ void GameScreen::CreateViews() { } else if (!isHomebrew_) { GameDBInfo dbInfo; std::vector dbInfos; - if ((hasFlags & GameInfoFlags::PARAM_SFO) && !g_gameDB.GetGameInfos(info->id_version, &dbInfos)) { + if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && !g_gameDB.GetGameInfos(info_->id_version, &dbInfos)) { // tvVerified_->SetText(ga->T("Game ID unknown - not in the ReDump database")); // tvVerified_->SetVisibility(UI::V_VISIBLE); // tvVerified_->SetLevel(NoticeLevel::WARN); - } else if ((hasFlags & GameInfoFlags::UNCOMPRESSED_SIZE) && info->gameSizeUncompressed != 0) { // don't do this check if info still pending + } else if ((knownFlags_ & GameInfoFlags::UNCOMPRESSED_SIZE) && info_->gameSizeUncompressed != 0) { // don't do this check if info_ still pending bool found = false; for (auto &dbInfo : dbInfos) { // TODO: Doesn't take CSO/CHD into account. - if (info->gameSizeUncompressed == dbInfo.size) { + if (info_->gameSizeUncompressed == dbInfo.size) { found = true; } } @@ -266,7 +266,7 @@ void GameScreen::CreateViews() { // tvVerified_->SetText(ga->T("File size incorrect, bad or modified ISO")); // tvVerified_->SetVisibility(UI::V_VISIBLE); // tvVerified_->SetLevel(NoticeLevel::ERROR); - // INFO_LOG(Log::Loader, "File size %d not matching game DB", (int)info->gameSizeUncompressed); + // INFO_LOG(Log::Loader, "File size %d not matching game DB", (int)info_->gameSizeUncompressed); } NoticeView *tvVerified = infoLayout->Add(new NoticeView(NoticeLevel::INFO, ga->T("Click \"Calculate CRC\" to verify ISO"), "", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); @@ -280,8 +280,8 @@ void GameScreen::CreateViews() { tvVerified->SetVisibility(UI::V_GONE); tvVerified->SetSquishy(true); - // Show plugin info, if any. Later might add checkboxes. - auto plugins = HLEPlugins::FindPlugins(info->id, g_Config.sLanguageIni); + // Show plugin info_, if any. Later might add checkboxes. + auto plugins = HLEPlugins::FindPlugins(info_->id, g_Config.sLanguageIni); if (!plugins.empty()) { auto sy = GetI18NCategory(I18NCat::SYSTEM); infoLayout->Add(new TextView(sy->T("Plugins"), ALIGN_LEFT, true)); @@ -290,24 +290,31 @@ void GameScreen::CreateViews() { } } - ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins)); - root_->Add(rightColumn); +} + +void GameScreen::CreateSettingsViews(UI::LinearLayout *rightColumn) { + using namespace UI; + + auto di = GetI18NCategory(I18NCat::DIALOG); + auto ga = GetI18NCategory(I18NCat::GAME); + + const bool fileTypeSupportCRC = FileTypeSupportsCRC(info_->fileType); LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL); rightColumnItems->SetSpacing(0.0f); rightColumn->Add(rightColumnItems); if (!inGame_) { - rightColumnItems->Add(new Choice(ga->T("Play")))->OnClick.Handle(this, &GameScreen::OnPlay); + rightColumnItems->Add(new Choice(ga->T("Play"), ImageID("I_PLAY")))->OnClick.Handle(this, &GameScreen::OnPlay); } - if (!info->id.empty()) { + if (!info_->id.empty()) { Choice *btnGameSettings = rightColumnItems->Add(new Choice(ga->T("Game Settings"))); btnGameSettings->OnClick.Handle(this, &GameScreen::OnGameSettings); if (inGame_) btnGameSettings->SetEnabled(false); - if (info->hasConfig) { + if (info_->hasConfig) { Choice *btnDeleteGameConfig = rightColumnItems->Add(new Choice(ga->T("Delete Game Config"))); btnDeleteGameConfig->OnClick.Handle(this, &GameScreen::OnDeleteConfig); if (inGame_) @@ -320,7 +327,7 @@ void GameScreen::CreateViews() { } } - if (info->saveDataSize) { + if (info_->saveDataSize) { Choice *btnDeleteSaveData = new Choice(ga->T("Delete Save Data")); rightColumnItems->Add(btnDeleteSaveData)->OnClick.Handle(this, &GameScreen::OnDeleteSaveData); } @@ -332,12 +339,12 @@ void GameScreen::CreateViews() { deleteChoice->SetEnabled(false); } - if ((hasFlags & GameInfoFlags::PARAM_SFO) && System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) { + if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && System_GetPropertyBool(SYSPROP_CAN_CREATE_SHORTCUT)) { rightColumnItems->Add(new Choice(ga->T("Create Shortcut")))->OnClick.Add([this](UI::EventParams &e) { GameInfoFlags hasFlags; - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO, &hasFlags); + std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO, &hasFlags); if (hasFlags & GameInfoFlags::PARAM_SFO) { - System_CreateGameShortcut(gamePath_, info->GetTitle()); + System_CreateGameShortcut(gamePath_, info_->GetTitle()); g_OSD.Show(OSDType::MESSAGE_SUCCESS, GetI18NCategory(I18NCat::DIALOG)->T("Desktop shortcut created"), 2.0f); } }); @@ -353,7 +360,7 @@ void GameScreen::CreateViews() { } if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - rightColumnItems->Add(new Choice(di->T("Show in folder")))->OnClick.Add([this](UI::EventParams &e) { + rightColumnItems->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER")))->OnClick.Add([this](UI::EventParams &e) { System_ShowFileInFolder(gamePath_); }); } @@ -363,12 +370,12 @@ void GameScreen::CreateViews() { rightColumnItems->Add(new Choice(pa->T("Cheats")))->OnClick.Handle(this, &GameScreen::OnCwCheat); } - if (info->pic1.texture) { + if (info_->pic1.texture) { Choice *btnSetBackground = rightColumnItems->Add(new Choice(ga->T("Use UI background"))); btnSetBackground->OnClick.Handle(this, &GameScreen::OnSetBackground); } - isHomebrew_ = info && info->region == GameRegion::HOMEBREW; + isHomebrew_ = info_ && info_->region == GameRegion::HOMEBREW; if (fileTypeSupportCRC && !isHomebrew_ && !Reporting::HasCRC(gamePath_) ) { Choice *btnCalcCRC = rightColumnItems->Add(new ChoiceWithValueDisplay(&CRC32string, ga->T("Calculate CRC"), I18NCat::NONE)); btnCalcCRC->OnClick.Add([this](UI::EventParams &) { @@ -466,20 +473,20 @@ void GameScreen::OnPlay(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/")) + 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)); } } void GameScreen::OnDeleteSaveData(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE); - if (info) { + std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE); + if (info_) { // Check that there's any savedata to delete - if (info->saveDataSize) { + if (info_->saveDataSize) { const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN); auto di = GetI18NCategory(I18NCat::DIALOG); Path gamePath = gamePath_; @@ -487,10 +494,10 @@ void GameScreen::OnDeleteSaveData(UI::EventParams &e) { new UI::MessagePopupScreen(di->T("Delete"), di->T("DeleteConfirmAll", "Do you really want to delete all\nyour save data for this game?"), trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"), [gamePath](bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO); - info->DeleteAllSaveData(); - info->saveDataSize = 0; - info->installDataSize = 0; + std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO); + info_->DeleteAllSaveData(); + info_->saveDataSize = 0; + info_->installDataSize = 0; } })); } @@ -499,8 +506,8 @@ void GameScreen::OnDeleteSaveData(UI::EventParams &e) { } void GameScreen::OnDeleteGame(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); - if (info->Ready(GameInfoFlags::PARAM_SFO)) { + std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO); + if (info_->Ready(GameInfoFlags::PARAM_SFO)) { auto di = GetI18NCategory(I18NCat::DIALOG); auto ga = GetI18NCategory(I18NCat::GAME); std::string prompt; @@ -513,8 +520,8 @@ void GameScreen::OnDeleteGame(UI::EventParams &e) { new UI::MessagePopupScreen(ga->T("Delete Game"), prompt, trashAvailable ? di->T("Move to trash") : di->T("Delete"), di->T("Cancel"), [sm, gamePath](bool yes) { if (yes) { - std::shared_ptr info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO); - info->Delete(); + std::shared_ptr info_ = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO); + info_->Delete(); g_gameInfoCache->Clear(); g_recentFiles.Remove(gamePath.c_str()); sm->switchScreen(new MainScreen()); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index c308171e9c..e350e4fcb4 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -22,8 +22,9 @@ #include "UI/BaseScreens.h" #include "Common/UI/UIScreen.h" #include "Common/File/Path.h" - #include "UI/GameInfoCache.h" +#include "UI/SimpleDialogScreen.h" + class NoticeView; @@ -33,7 +34,7 @@ class NoticeView; // Uses GameInfoCache heavily to implement the functionality. // Should possibly merge this with the PauseScreen. -class GameScreen : public UIBaseDialogScreen { +class GameScreen : public UITwoPaneBaseDialogScreen { public: GameScreen(const Path &gamePath, bool inGame); ~GameScreen(); @@ -45,7 +46,10 @@ public: const char *tag() const override { return "Game"; } protected: - void CreateViews() override; + void CreateContentViews(UI::LinearLayout *parent) override; + void CreateSettingsViews(UI::LinearLayout *parent) override; + + bool SettingsToTheRight() const override { return true; } private: // Event handlers @@ -69,4 +73,6 @@ private: GameInfoFlags knownFlags_ = GameInfoFlags::EMPTY; bool knownHasCRC_ = false; + + std::shared_ptr info_; }; diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 91b5eb7a13..963515565c 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -515,8 +515,8 @@ void DirButton::Draw(UIContext &dc) { } } -GameBrowser::GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string_view lastText, std::string_view lastLink, UI::LayoutParams *layoutParams) - : LinearLayout(ORIENT_VERTICAL, layoutParams), gridStyle_(gridStyle), browseFlags_(browseFlags), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager), token_(token) { +GameBrowser::GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool portrait, bool *gridStyle, ScreenManager *screenManager, std::string_view lastText, std::string_view lastLink, UI::LayoutParams *layoutParams) + : LinearLayout(ORIENT_VERTICAL, layoutParams), gridStyle_(gridStyle), browseFlags_(browseFlags), portrait_(portrait), lastText_(lastText), lastLink_(lastLink), screenManager_(screenManager), token_(token) { using namespace UI; path_.SetUserAgent(StringFromFormat("PPSSPP/%s", PPSSPP_GIT_VERSION)); Path memstickRoot = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT); @@ -796,7 +796,7 @@ void GameBrowser::Refresh() { #else if ((browseFlags_ & BrowseFlags::BROWSE) && System_GetPropertyBool(SYSPROP_HAS_FOLDER_BROWSER)) { // Collapse the button title on very small screens (Retroid Pocket) or portrait mode. - std::string_view browseTitle = g_display.pixel_xres <= 550 ? "" : mm->T("Browse"); + std::string_view browseTitle = (g_display.dp_xres <= 550 || portrait_) ? "" : mm->T("Browse"); topBar->Add(new Choice(browseTitle, ImageID("I_FOLDER_OPEN"), new LayoutParams(WRAP_CONTENT, 64.0f)))->OnClick.Handle(this, &GameBrowser::BrowseClick); } if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { @@ -1092,8 +1092,10 @@ void MainScreen::CreateRecentTab() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("MainScreenRecentGames"); + bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + GameBrowser *tabRecentGames = new GameBrowser(GetRequesterToken(), - Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", + Path("!RECENT"), BrowseFlags::NONE, portrait, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->Add(tabRecentGames); @@ -1109,10 +1111,12 @@ GameBrowser *MainScreen::CreateBrowserTab(const Path &path, std::string_view tit using namespace UI; auto mm = GetI18NCategory(I18NCat::MAINMENU); + const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + ScrollView *scrollView = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollView->SetTag(title); // Re-use title as tag, should be fine. - GameBrowser *gameBrowser = new GameBrowser(GetRequesterToken(), path, browseFlags, bGridView, screenManager(), + GameBrowser *gameBrowser = new GameBrowser(GetRequesterToken(), path, browseFlags, portrait, bGridView, screenManager(), mm->T(howToTitle), howToUri, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); @@ -1604,6 +1608,8 @@ void UmdReplaceScreen::CreateViews() { auto mm = GetI18NCategory(I18NCat::MAINMENU); auto di = GetI18NCategory(I18NCat::DIALOG); + const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0)); leftColumn->SetTag("UmdReplace"); leftColumn->SetClip(true); @@ -1617,7 +1623,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("UmdReplaceRecentGames"); GameBrowser *tabRecentGames = new GameBrowser(GetRequesterToken(), - Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", + Path("!RECENT"), BrowseFlags::NONE, portrait, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); leftColumn->AddTab(mm->T("Recent"), ImageID::invalid(), scrollRecentGames); @@ -1627,7 +1633,7 @@ void UmdReplaceScreen::CreateViews() { ScrollView *scrollAllGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollAllGames->SetTag("UmdReplaceAllGames"); - GameBrowser *tabAllGames = new GameBrowser(GetRequesterToken(), Path(g_Config.currentDirectory), BrowseFlags::STANDARD, &g_Config.bGridView2, screenManager(), + GameBrowser *tabAllGames = new GameBrowser(GetRequesterToken(), Path(g_Config.currentDirectory), BrowseFlags::STANDARD, portrait, &g_Config.bGridView2, screenManager(), mm->T("How to get games"), "https://www.ppsspp.org/getgames.html", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 5c9a694ee6..6ba953b195 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -46,7 +46,7 @@ ENUM_CLASS_BITOPS(BrowseFlags); class GameBrowser : public UI::LinearLayout { public: - GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool *gridStyle, ScreenManager *screenManager, std::string_view lastText, std::string_view lastLink, UI::LayoutParams *layoutParams = nullptr); + GameBrowser(int token, const Path &path, BrowseFlags browseFlags, bool portrait, bool *gridStyle, ScreenManager *screenManager, std::string_view lastText, std::string_view lastLink, UI::LayoutParams *layoutParams = nullptr); UI::Event OnChoice; UI::Event OnHoldChoice; @@ -114,7 +114,8 @@ private: float lastScale_ = 1.0f; bool lastLayoutWasGrid_ = true; ScreenManager *screenManager_; - int token_; + int token_ = -1; + bool portrait_ = false; }; class RemoteISOBrowseScreen; diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index c2197565b6..0556c8b603 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -539,7 +539,12 @@ void CreditsScreen::CreateDialogViews(UI::ViewGroup *root) { int rightYOffset = 0; if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) { ScreenManager *sm = screenManager(); - left->Add(new Choice(mm->T("Buy PPSSPP Gold")))->OnClick.Add([sm](UI::EventParams) { + Choice *gold = new Choice(mm->T("Buy PPSSPP Gold")); + gold->SetIcon(ImageID("I_ICON_GOLD"), 0.5f); + gold->SetImageScale(0.6f); // for the left-icon in case of vertical. + gold->SetShine(true); + + left->Add(gold)->OnClick.Add([sm](UI::EventParams) { LaunchBuyGold(sm); }); rightYOffset = 74; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 45590a96ca..777bd506f0 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -592,7 +592,7 @@ void RemoteISOBrowseScreen::CreateViews() { auto di = GetI18NCategory(I18NCat::DIALOG); auto ri = GetI18NCategory(I18NCat::REMOTEISO); - bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; + const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait; using namespace UI; @@ -606,7 +606,7 @@ void RemoteISOBrowseScreen::CreateViews() { ScrollView *scrollRecentGames = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); scrollRecentGames->SetTag("RemoteGamesTab"); GameBrowser *tabRemoteGames = new GameBrowser(GetRequesterToken(), - Path(url_), BrowseFlags::NAVIGATE, &g_Config.bGridView1, screenManager(), "", "", + Path(url_), BrowseFlags::NAVIGATE, portrait, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); tabRemoteGames->SetHomePath(Path(url_)); diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index de99343d50..3dbf1af41c 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -41,7 +41,7 @@ void UITwoPaneBaseDialogScreen::CreateViews() { root->SetSpacing(0); CreateContentViews(root); - ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); + ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f, Margins(8))); LinearLayout *settingsPane = new LinearLayout(ORIENT_VERTICAL); settingsScroll->Add(settingsPane); CreateSettingsViews(settingsPane); @@ -52,13 +52,21 @@ void UITwoPaneBaseDialogScreen::CreateViews() { ignoreBottomInset_ = false; LinearLayout *root = new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); // root_->Add(new TopBar(*screenManager()->getUIContext(), portrait, GetTitle(), new LayoutParams(FILL_PARENT, FILL_PARENT))); - LinearLayout *settingsPane = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(400.0f, FILL_PARENT)); + ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(SettingsWidth(), FILL_PARENT, 0.0f, Margins(8))); + LinearLayout *settingsPane = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT)); settingsPane->SetSpacing(0); CreateSettingsViews(settingsPane); settingsPane->Add(new BorderView(BORDER_BOTTOM, BorderStyle::HEADER_FG, 2.0f, new LayoutParams(FILL_PARENT, 40.0f))); settingsPane->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK")))->OnClick.Handle(this, &UIScreen::OnBack); - root->Add(settingsPane); - CreateContentViews(root); + settingsScroll->Add(settingsPane); + + if (SettingsToTheRight()) { + CreateContentViews(root); + root->Add(settingsScroll); + } else { + root->Add(settingsScroll); + CreateContentViews(root); + } root_ = root; } diff --git a/UI/SimpleDialogScreen.h b/UI/SimpleDialogScreen.h index bff9cd6cdf..ace3b4158f 100644 --- a/UI/SimpleDialogScreen.h +++ b/UI/SimpleDialogScreen.h @@ -39,6 +39,9 @@ public: virtual void CreateSettingsViews(UI::LinearLayout *parent) = 0; virtual void CreateContentViews(UI::LinearLayout *parent) = 0; virtual std::string_view GetTitle() const { return ""; } + virtual float SettingsWidth() const { return 350.0f; } + + virtual bool SettingsToTheRight() const { return false; } private: void CreateViews() override; diff --git a/UI/UploadScreen.cpp b/UI/UploadScreen.cpp index c8dea29d37..9004d5b021 100644 --- a/UI/UploadScreen.cpp +++ b/UI/UploadScreen.cpp @@ -39,7 +39,7 @@ void UploadScreen::CreateDialogViews(UI::ViewGroup *root) { if (prevRunning_) { container->Add(new TextWithImage(ImageID("I_WIFI"), n->T("With a web browser on the same network, go to:"))); for (const auto &ip : localIPs_) { - std::string url = StringFromFormat("http://%s:%d/upload", ip.c_str(), WebServerPort()); + std::string url = StringFromFormat("%s:%d/upload", ip.c_str(), WebServerPort()); container->Add(new CopyableText(ImageID("I_WEB_BROWSER"), url)); } }