From 75020e2ed7290fece0251c50eb482a1a6b5f6564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 5 Nov 2025 14:42:49 +0100 Subject: [PATCH] Add ability to put icons on tabs --- Common/UI/TabHolder.cpp | 12 ++++++------ Common/UI/TabHolder.h | 10 +++++----- Common/UI/View.h | 8 ++++---- Core/HLE/sceDisplay.cpp | 1 + Core/System.cpp | 2 ++ UI/CustomButtonMappingScreen.cpp | 2 +- UI/DevScreens.cpp | 2 +- UI/GPUDriverTestScreen.cpp | 6 +++--- UI/MainScreen.cpp | 8 ++++---- UI/MiscScreens.cpp | 2 +- UI/RemoteISOScreen.cpp | 4 ++-- UI/ReportScreen.cpp | 4 ++-- UI/RetroAchievementScreens.cpp | 6 +++--- UI/TabbedDialogScreen.cpp | 6 +++--- UI/TabbedDialogScreen.h | 6 +++++- UI/TouchControlVisibilityScreen.cpp | 4 ++-- 16 files changed, 45 insertions(+), 38 deletions(-) diff --git a/Common/UI/TabHolder.cpp b/Common/UI/TabHolder.cpp index c9a41d4c69..3099b025bb 100644 --- a/Common/UI/TabHolder.cpp +++ b/Common/UI/TabHolder.cpp @@ -62,9 +62,9 @@ void TabHolder::AddBack(UIScreen *parent) { } } -void TabHolder::AddTabContents(std::string_view title, ViewGroup *tabContents) { +void TabHolder::AddTabContents(std::string_view title, ImageID imageId, ViewGroup *tabContents) { tabs_.push_back(tabContents); - tabStrip_->AddChoice(title); + tabStrip_->AddChoice(title, imageId); contents_->Add(tabContents); if (tabs_.size() > 1) tabContents->SetVisibility(V_GONE); @@ -76,9 +76,9 @@ void TabHolder::AddTabContents(std::string_view title, ViewGroup *tabContents) { createFuncs_.push_back(nullptr); } -void TabHolder::AddTabDeferred(std::string_view title, std::function createCb) { +void TabHolder::AddTabDeferred(std::string_view title, ImageID imageId, std::function createCb) { tabs_.push_back(nullptr); // marker - tabStrip_->AddChoice(title); + tabStrip_->AddChoice(title, imageId); tabTweens_.push_back(nullptr); createFuncs_.push_back(createCb); @@ -223,8 +223,8 @@ ChoiceStrip::ChoiceStrip(Orientation orientation, LayoutParams *layoutParams) SetSpacing(0.0f); } -void ChoiceStrip::AddChoice(std::string_view title) { - StickyChoice *c = new StickyChoice(title, "", +void ChoiceStrip::AddChoice(std::string_view title, ImageID imageId) { + StickyChoice *c = new StickyChoice(title, imageId, orientation_ == ORIENT_HORIZONTAL ? nullptr : new LinearLayoutParams(FILL_PARENT, ITEM_HEIGHT)); diff --git a/Common/UI/TabHolder.h b/Common/UI/TabHolder.h index beb883d234..f8b42d9961 100644 --- a/Common/UI/TabHolder.h +++ b/Common/UI/TabHolder.h @@ -22,11 +22,11 @@ public: TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams); template - T *AddTab(std::string_view title, T *tabContents) { - AddTabContents(title, tabContents); + T *AddTab(std::string_view title, ImageID imageId, T *tabContents) { + AddTabContents(title, imageId, tabContents); return tabContents; } - void AddTabDeferred(std::string_view title, std::function createCb); + void AddTabDeferred(std::string_view title, ImageID imageId, std::function createCb); void EnableTab(int tab, bool enabled); void AddBack(UIScreen *parent); @@ -48,7 +48,7 @@ public: } private: - void AddTabContents(std::string_view title, ViewGroup *tabContents); + void AddTabContents(std::string_view title, ImageID imageId, ViewGroup *tabContents); void OnTabClick(EventParams &e); bool EnsureTab(int index); // return true if it actually created a tab. @@ -68,7 +68,7 @@ class ChoiceStrip : public LinearLayout { public: ChoiceStrip(Orientation orientation, LayoutParams *layoutParams = 0); - void AddChoice(std::string_view title); + void AddChoice(std::string_view title, ImageID imageId = ImageID::invalid()); void AddChoice(ImageID buttonImage); int GetSelection() const { return selected_; } diff --git a/Common/UI/View.h b/Common/UI/View.h index db524d6421..e3057e4554 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -30,8 +30,6 @@ struct KeyInput; struct TouchInput; struct AxisInput; -struct ImageID; - class DrawBuffer; class Texture; class UIContext; @@ -778,10 +776,12 @@ private: // Different key handling. class StickyChoice : public Choice { public: - StickyChoice(std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = 0) + StickyChoice(std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr) : Choice(text, smallText, false, layoutParams) {} - StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = 0) + StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = nullptr) : Choice(buttonImage, layoutParams) {} + StickyChoice(std::string_view text, ImageID image, LayoutParams *layoutParams = nullptr) + : Choice(text, image, layoutParams) {} bool Key(const KeyInput &key) override; bool Touch(const TouchInput &touch) override; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index f50c1d27d5..31aa24b4ac 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -752,6 +752,7 @@ void hleLagSync(u64 userdata, int cyclesLate) { double now = before; while (now < goal && goal < now + 0.01) { // Tight loop on win32 - intentionally, as timing is otherwise not precise enough. + // TODO: Use the precise waits if available #ifndef _WIN32 const double left = goal - now; if (left > 0.0f && left < 1.0f) { // Sanity check diff --git a/Core/System.cpp b/Core/System.cpp index b98ef349e8..749d6bd75a 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -934,6 +934,8 @@ void DumpFileIfEnabled(const u8 *dataPtr, const u32 length, std::string_view nam delete[] path; } }, path); + } else { + free(path); } return; } diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index 40e4e87e32..e80ffd1318 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -146,7 +146,7 @@ void CustomButtonMappingScreen::CreateViews() { root__->Add(leftColumn); ScrollView *rightScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1.0f)); leftColumn->Add(new Spacer(new LinearLayoutParams(1.0f))); - leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); + leftColumn->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK")))->OnClick.Handle(this, &UIScreen::OnBack); root__->Add(rightScroll); LinearLayout *vertLayout = new LinearLayout(ORIENT_VERTICAL); diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 5a47e977d5..b6644f7113 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -557,7 +557,7 @@ void FrameDumpTestScreen::CreateViews() { LinearLayout *dumps = new LinearLayoutList(ORIENT_VERTICAL); dumps->SetSpacing(0); dumpsScroll->Add(dumps); - tabHolder->AddTab("Dumps", dumpsScroll); + tabHolder->AddTab("Dumps", ImageID::invalid(), dumpsScroll); dumps->Add(new ItemHeader("GE Frame Dumps")); diff --git a/UI/GPUDriverTestScreen.cpp b/UI/GPUDriverTestScreen.cpp index b9993af4ea..cdc9cf5529 100644 --- a/UI/GPUDriverTestScreen.cpp +++ b/UI/GPUDriverTestScreen.cpp @@ -303,10 +303,10 @@ void GPUDriverTestScreen::CreateViews() { tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, TabHolderFlags::Default, nullptr, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, false)); anchor->Add(tabHolder_); - tabHolder_->AddTab("Discard", new LinearLayout(ORIENT_VERTICAL)); - tabHolder_->AddTab("Shader", new LinearLayout(ORIENT_VERTICAL)); + tabHolder_->AddTab("Discard", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL)); + tabHolder_->AddTab("Shader", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL)); - Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(190, WRAP_CONTENT, 10, NONE, NONE, 10)); + Choice *back = new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(190, WRAP_CONTENT, 10, NONE, NONE, 10)); back->OnClick.Handle(this, &UIScreen::OnBack); anchor->Add(back); } diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 52d7c7e616..5a9c19d092 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1099,7 +1099,7 @@ void MainScreen::CreateRecentTab() { scrollRecentGames->Add(tabRecentGames); gameBrowsers_.push_back(tabRecentGames); - tabHolder_->AddTab(mm->T("Recent"), scrollRecentGames); + tabHolder_->AddTab(mm->T("Recent"), ImageID::invalid(), scrollRecentGames); tabRecentGames->OnChoice.Handle(this, &MainScreen::OnGameSelectedInstant); tabRecentGames->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected); tabRecentGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); @@ -1119,7 +1119,7 @@ GameBrowser *MainScreen::CreateBrowserTab(const Path &path, std::string_view tit scrollView->Add(gameBrowser); gameBrowsers_.push_back(gameBrowser); - tabHolder_->AddTab(mm->T(title), scrollView); + tabHolder_->AddTab(mm->T(title), ImageID::invalid(), scrollView); if (scrollPos) { scrollView->RememberPosition(scrollPos); } @@ -1617,7 +1617,7 @@ void UmdReplaceScreen::CreateViews() { Path("!RECENT"), BrowseFlags::NONE, &g_Config.bGridView1, screenManager(), "", "", new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); scrollRecentGames->Add(tabRecentGames); - leftColumn->AddTab(mm->T("Recent"), scrollRecentGames); + leftColumn->AddTab(mm->T("Recent"), ImageID::invalid(), scrollRecentGames); tabRecentGames->OnChoice.Handle(this, &UmdReplaceScreen::OnGameSelected); tabRecentGames->OnHoldChoice.Handle(this, &UmdReplaceScreen::OnGameSelected); } @@ -1630,7 +1630,7 @@ void UmdReplaceScreen::CreateViews() { scrollAllGames->Add(tabAllGames); - leftColumn->AddTab(mm->T("Games"), scrollAllGames); + leftColumn->AddTab(mm->T("Games"), ImageID::invalid(), scrollAllGames); tabAllGames->OnChoice.Handle(this, &UmdReplaceScreen::OnGameSelected); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index c74db93103..d910929fcb 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -154,7 +154,7 @@ void UIBaseScreen::sendMessage(UIMessage message, const char *value) { void UIBaseDialogScreen::AddStandardBack(UI::ViewGroup *parent) { using namespace UI; auto di = GetI18NCategory(I18NCat::DIALOG); - parent->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(190, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); + parent->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(190, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); } PromptScreen::PromptScreen(const Path &gamePath, std::string_view message, std::string_view yesButtonText, std::string_view noButtonText, std::function callback) diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 654fe6b24d..45590a96ca 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -613,7 +613,7 @@ void RemoteISOBrowseScreen::CreateViews() { scrollRecentGames->Add(tabRemoteGames); gameBrowsers_.push_back(tabRemoteGames); - leftColumn->AddTab(ri->T("Remote Server"), scrollRecentGames); + leftColumn->AddTab(ri->T("Remote Server"), ImageID::invalid(), scrollRecentGames); tabRemoteGames->OnChoice.Handle(this, &MainScreen::OnGameSelectedInstant); tabRemoteGames->OnHoldChoice.Handle(this, &MainScreen::OnGameSelected); tabRemoteGames->OnHighlight.Handle(this, &MainScreen::OnGameHighlight); @@ -623,7 +623,7 @@ void RemoteISOBrowseScreen::CreateViews() { rightColumnItems->SetSpacing(0.0f); rightColumn->Add(rightColumnItems); - rightColumnItems->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); + rightColumnItems->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); if (portrait) { root_ = new LinearLayout(ORIENT_VERTICAL); diff --git a/UI/ReportScreen.cpp b/UI/ReportScreen.cpp index 13039a0663..8a3ff439fd 100644 --- a/UI/ReportScreen.cpp +++ b/UI/ReportScreen.cpp @@ -325,7 +325,7 @@ void ReportScreen::CreateViews() { UpdateOverallDescription(); rightColumnItems->Add(new Spacer(25.0)); - rightColumnItems->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); + rightColumnItems->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); root_ = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); root_->Add(leftColumn); @@ -432,7 +432,7 @@ void ReportFinishScreen::CreateViews() { rightColumnItems->Add(new Choice(rp->T("View Feedback")))->OnClick.Handle(this, &ReportFinishScreen::HandleViewFeedback); rightColumnItems->Add(new Spacer(25.0)); - rightColumnItems->Add(new Choice(di->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); + rightColumnItems->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); root_ = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); root_->Add(leftColumn); diff --git a/UI/RetroAchievementScreens.cpp b/UI/RetroAchievementScreens.cpp index 154b43a580..a579c85273 100644 --- a/UI/RetroAchievementScreens.cpp +++ b/UI/RetroAchievementScreens.cpp @@ -64,18 +64,18 @@ AudioFileChooser::AudioFileChooser(RequesterToken token, std::string *value, std void RetroAchievementsListScreen::CreateTabs() { auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS); - AddTab("Achievements", ac->T("Achievements"), [this](UI::LinearLayout *parent) { + AddTab("Achievements", ac->T("Achievements"), ImageID::invalid(), [this](UI::LinearLayout *parent) { parent->SetSpacing(5.0f); CreateAchievementsTab(parent); }); - AddTab("Leaderboards", ac->T("Leaderboards"), [this](UI::LinearLayout *parent) { + AddTab("Leaderboards", ac->T("Leaderboards"), ImageID::invalid(), [this](UI::LinearLayout *parent) { parent->SetSpacing(5.0f); CreateLeaderboardsTab(parent); }); #ifdef _DEBUG - AddTab("AchievementsStatistics", ac->T("Statistics"), [this](UI::LinearLayout *parent) { + AddTab("AchievementsStatistics", ac->T("Statistics"), ImageID::invalid(), [this](UI::LinearLayout *parent) { parent->SetSpacing(5.0f); CreateStatisticsTab(parent); }); diff --git a/UI/TabbedDialogScreen.cpp b/UI/TabbedDialogScreen.cpp index 5a30dee559..d8fbdb43cc 100644 --- a/UI/TabbedDialogScreen.cpp +++ b/UI/TabbedDialogScreen.cpp @@ -12,10 +12,10 @@ #include "UI/MiscViews.h" #include "UI/TabbedDialogScreen.h" -void UITabbedBaseDialogScreen::AddTab(const char *tag, std::string_view title, std::function createCallback, TabFlags flags) { +void UITabbedBaseDialogScreen::AddTab(const char *tag, std::string_view title, ImageID imageId, std::function createCallback, TabFlags flags) { using namespace UI; - tabHolder_->AddTabDeferred(title, [createCallback = std::move(createCallback), tag, flags]() -> UI::ViewGroup * { + tabHolder_->AddTabDeferred(title, imageId, [createCallback = std::move(createCallback), tag, flags]() -> UI::ViewGroup * { using namespace UI; ViewGroup *scroll = nullptr; if (!(flags & TabFlags::NonScrollable)) { @@ -90,7 +90,7 @@ void UITabbedBaseDialogScreen::CreateViews() { if ((g_display.dp_xres < g_display.dp_yres || g_display.dp_yres >= 500) && (deviceType != DEVICE_TYPE_VR) && ShowSearchControls()) { // Search auto ms = GetI18NCategory(I18NCat::MAINSETTINGS); - AddTab("GameSettingsSearch", ms->T("Search"), [this](UI::LinearLayout *searchSettings) { + AddTab("GameSettingsSearch", ms->T("Search"), ImageID("I_SEARCH"), [this](UI::LinearLayout *searchSettings) { auto se = GetI18NCategory(I18NCat::SEARCH); searchSettings->Add(new ItemHeader(se->T("Find settings"))); diff --git a/UI/TabbedDialogScreen.h b/UI/TabbedDialogScreen.h index 1193354c13..b78d4f7e9d 100644 --- a/UI/TabbedDialogScreen.h +++ b/UI/TabbedDialogScreen.h @@ -4,6 +4,7 @@ #include #include "Common/UI/UIScreen.h" +#include "Common/Render/TextureAtlas.h" #include "Common/System/System.h" #include "Core/ConfigValues.h" #include "UI/BaseScreens.h" @@ -26,7 +27,10 @@ public: ignoreBottomInset_ = true; } - void AddTab(const char *tag, std::string_view title, std::function createCallback, TabFlags flags = TabFlags::Default); + void AddTab(const char *tag, std::string_view title, ImageID imageId, std::function createCallback, TabFlags flags = TabFlags::Default); + void AddTab(const char *tag, std::string_view title, std::function createCallback, TabFlags flags = TabFlags::Default) { + AddTab(tag, title, ImageID::invalid(), createCallback, flags); + } void CreateViews() override; protected: diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index f9c7355364..95aa90895d 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -149,12 +149,12 @@ void RightAnalogMappingScreen::CreateViews() { auto mc = GetI18NCategory(I18NCat::MAPPABLECONTROLS); root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); - Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10)); + Choice *back = new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new AnchorLayoutParams(leftColumnWidth - 10, WRAP_CONTENT, 10, NONE, NONE, 10)); root_->Add(back)->OnClick.Handle(this, &UIScreen::OnBack); TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, TabHolderFlags::Default, nullptr, new AnchorLayoutParams(10, 0, 10, 0, false)); root_->Add(tabHolder); ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL); - tabHolder->AddTab(co->T("Binds"), rightPanel); + tabHolder->AddTab(co->T("Binds"), ImageID::invalid(), rightPanel); LinearLayout *vert = rightPanel->Add(new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT))); vert->SetSpacing(0);