From ed25b5e99159cbaa69e771ecb8ca1966a48ce87b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 26 Nov 2025 11:14:37 +0100 Subject: [PATCH] Add convenient UI on the pause screen for changing/locking the screen orientation --- .gitignore | 1 + Common/UI/PopupScreens.cpp | 24 ++++- Common/UI/PopupScreens.h | 33 +++---- Common/UI/ScrollView.cpp | 19 +++- Common/UI/View.h | 13 ++- Core/Config.cpp | 7 ++ Core/ConfigValues.h | 4 +- UI/CustomButtonMappingScreen.cpp | 4 +- UI/GameSettingsScreen.cpp | 21 +--- UI/GameSettingsScreen.h | 1 - UI/IAPScreen.cpp | 2 +- UI/MainScreen.cpp | 2 +- UI/MiscScreens.cpp | 2 +- UI/MiscViews.cpp | 24 +++++ UI/MiscViews.h | 2 + UI/PauseScreen.cpp | 12 ++- UI/PauseScreen.h | 2 +- UI/RemoteISOScreen.cpp | 2 +- UI/TouchControlVisibilityScreen.cpp | 2 +- UI/UIAtlas.cpp | 15 ++- assets/ui_images/images.svg | 142 +++++++++++++++++++++++----- 21 files changed, 247 insertions(+), 87 deletions(-) diff --git a/.gitignore b/.gitignore index cfeaf2e674..4631907118 100644 --- a/.gitignore +++ b/.gitignore @@ -106,6 +106,7 @@ imgui.ini # debug file ui_atlas_gen.png +buttons_rasterized.png # For vim *.swp diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index c5d888e82b..5da19c0866 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -309,6 +309,20 @@ std::string ChopTitle(const std::string &title) { return title; } +PopupMultiChoice::PopupMultiChoice(int *value, std::string_view text, const char **choices, int minVal, int numChoices, + I18NCat category, ScreenManager *screenManager, UI::LayoutParams *layoutParams) + : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), choices_(choices), minVal_(minVal), numChoices_(numChoices), category_(category), screenManager_(screenManager) { + if (choices) { + // If choices is nullptr, we're being called from PopupMultiChoiceDynamic where value doesn't yet point to anything valid. + if (*value >= numChoices + minVal) + *value = numChoices + minVal - 1; + if (*value < minVal) + *value = minVal; + UpdateText(); + } + OnClick.Handle(this, &PopupMultiChoice::HandleClick); +} + void PopupMultiChoice::HandleClick(UI::EventParams &e) { if (!callbackExecuted_ && preOpenCallback_) { preOpenCallback_(this); @@ -324,8 +338,7 @@ void PopupMultiChoice::HandleClick(UI::EventParams &e) { choices.push_back(category ? std::string(category->T(choices_[i])) : std::string(choices_[i])); } - ListPopupScreen *popupScreen = new ListPopupScreen(ChopTitle(text_), choices, *value_ - minVal_, - std::bind(&PopupMultiChoice::ChoiceCallback, this, std::placeholders::_1)); + ListPopupScreen *popupScreen = new ListPopupScreen(ChopTitle(text_), choices, *value_ - minVal_, [this](int num) {ChoiceCallback(num);}); popupScreen->SetHiddenChoices(hidden_); popupScreen->SetChoiceIcons(icons_); if (e.v) @@ -870,6 +883,13 @@ void AbstractChoiceWithValueDisplay::Draw(UIContext &dc) { dc.SetFontScale(1.0f, 1.0f); } else { Choice::Draw(dc); + + if (text_.empty() && !image_.isValid()) { + // In this case we only display the image of the choice. Useful for small buttons spawning a popup. + dc.Draw()->DrawImageRotated(ValueImage(), bounds_.centerX(), bounds_.centerY(), imgScale_, imgRot_, style.fgColor, imgFlipH_); + return; + } + float scale = CalculateValueScale(dc, valueText, bounds_.w); dc.SetFontScale(scale, scale); dc.DrawTextRect(valueText, bounds_.Expand(-paddingX, 0.0f), style.fgColor, ALIGN_LEFT | ALIGN_VCENTER | FLAG_WRAP_TEXT); diff --git a/Common/UI/PopupScreens.h b/Common/UI/PopupScreens.h index 09714220de..aab9c9ec2a 100644 --- a/Common/UI/PopupScreens.h +++ b/Common/UI/PopupScreens.h @@ -282,28 +282,13 @@ private: class PopupMultiChoice : public AbstractChoiceWithValueDisplay { public: PopupMultiChoice(int *value, std::string_view text, const char **choices, int minVal, int numChoices, - I18NCat category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr) - : AbstractChoiceWithValueDisplay(text, layoutParams), value_(value), choices_(choices), minVal_(minVal), numChoices_(numChoices), - category_(category), screenManager_(screenManager) { - if (choices) { - // If choices is nullptr, we're being called from PopupMultiChoiceDynamic where value doesn't yet point to anything valid. - if (*value >= numChoices + minVal) - *value = numChoices + minVal - 1; - if (*value < minVal) - *value = minVal; - UpdateText(); - } - OnClick.Handle(this, &PopupMultiChoice::HandleClick); - } + I18NCat category, ScreenManager *screenManager, UI::LayoutParams *layoutParams = nullptr); void Update() override; void HideChoice(int c) { hidden_.insert(c); } - void SetChoiceIcon(int c, ImageID id) { - icons_[c] = id; - } bool IsChoiceHidden(int c) const { return hidden_.find(c) != hidden_.end(); } @@ -311,11 +296,24 @@ public: void SetPreOpenCallback(std::function callback) { preOpenCallback_ = callback; } + void SetChoiceIcon(int c, ImageID id) { + icons_[c] = id; + } + void SetChoiceIcons(std::map icons) { + icons_ = icons; + } UI::Event OnChoice; protected: std::string ValueText() const override; + ImageID ValueImage() const override { + auto iter = icons_.find(*value_); + if (iter != icons_.end()) { + return iter->second; + } + return ImageID::invalid(); + } int *value_; const char **choices_; @@ -346,8 +344,7 @@ public: // TODO: This all is absolutely terrible, just done this way to be conformant with the internals of PopupMultiChoice. PopupMultiChoiceDynamic(std::string *value, std::string_view text, const std::vector &choices, I18NCat category, ScreenManager *screenManager, std::vector *values = nullptr, UI::LayoutParams *layoutParams = nullptr) - : UI::PopupMultiChoice(&valueInt_, text, nullptr, 0, (int)choices.size(), category, screenManager, layoutParams), - valueStr_(value) { + : UI::PopupMultiChoice(&valueInt_, text, nullptr, 0, (int)choices.size(), category, screenManager, layoutParams), valueStr_(value) { if (values) { _dbg_assert_(choices.size() == values->size()); } diff --git a/Common/UI/ScrollView.cpp b/Common/UI/ScrollView.cpp index 4ce5e86c8c..a46a73641f 100644 --- a/Common/UI/ScrollView.cpp +++ b/Common/UI/ScrollView.cpp @@ -574,7 +574,9 @@ void ListView::CreateAllItems() { imageID = &iter->second; } View *v = linLayout_->Add(adaptor_->CreateItemView(i, imageID)); - adaptor_->AddEventCallback(v, std::bind(&ListView::OnItemCallback, this, i, std::placeholders::_1)); + adaptor_->AddEventCallback(v, [this, i](UI::EventParams &e) { + OnItemCallback(i, e); + }); } } } @@ -601,9 +603,12 @@ void ListView::OnItemCallback(int num, EventParams &e) { } View *ChoiceListAdaptor::CreateItemView(int index, ImageID *optionalImageID) { - Choice *choice = new Choice(items_[index]); + Choice *choice; if (optionalImageID) { - choice->SetIcon(*optionalImageID); + choice = new Choice(items_[index], *optionalImageID); + } else { + choice = new Choice(items_[index]); + //choice->SetIconRight(*optionalImageID); } return choice; } @@ -614,10 +619,14 @@ void ChoiceListAdaptor::AddEventCallback(View *view, std::functionSetIcon(*optionalImageID); + temp = *optionalImageID; } + Choice *choice = new Choice(items_[index], temp); + // if (optionalImageID) { + // choice->SetIconRight(*optionalImageID); + // } return choice; } diff --git a/Common/UI/View.h b/Common/UI/View.h index 3f3293f054..6bdbef3987 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -706,10 +706,10 @@ public: class Choice : public ClickableItem { public: Choice(std::string_view text, LayoutParams *layoutParams = nullptr) - : Choice(text, "", false, layoutParams) { } + : ClickableItem(layoutParams), text_(text) { } Choice(std::string_view text, ImageID image, LayoutParams *layoutParams = nullptr) : ClickableItem(layoutParams), text_(text), image_(image) {} - Choice(std::string_view text, std::string_view smallText, bool selected = false, LayoutParams *layoutParams = nullptr) + Choice(std::string_view text, std::string_view smallText, LayoutParams *layoutParams = nullptr) : ClickableItem(layoutParams), text_(text), smallText_(smallText), image_(ImageID::invalid()) {} Choice(ImageID image, LayoutParams *layoutParams = nullptr) : ClickableItem(layoutParams), image_(image), rightIconImage_(ImageID::invalid()) {} @@ -725,7 +725,10 @@ public: void SetDrawTextFlags(u32 flags) { drawTextFlags_ = flags; } - void SetIcon(ImageID iconImage, float scale = 1.0f, float rot = 0.0f, bool flipH = false, bool keepColor = true) { + void SetIconLeft(ImageID iconImage) { + image_ = iconImage; + } + void SetIconRight(ImageID iconImage, float scale = 1.0f, float rot = 0.0f, bool flipH = false, bool keepColor = true) { rightIconKeepColor_ = keepColor; rightIconScale_ = scale; rightIconRot_ = rot; @@ -776,7 +779,7 @@ private: class StickyChoice : public Choice { public: StickyChoice(std::string_view text, std::string_view smallText = "", LayoutParams *layoutParams = nullptr) - : Choice(text, smallText, false, layoutParams) {} + : Choice(text, smallText, layoutParams) {} StickyChoice(ImageID buttonImage, LayoutParams *layoutParams = nullptr) : Choice(buttonImage, layoutParams) {} StickyChoice(std::string_view text, ImageID image, LayoutParams *layoutParams = nullptr) @@ -835,8 +838,10 @@ public: void SetPasswordDisplay() { passwordMasking_ = true; } + protected: virtual std::string ValueText() const = 0; + virtual ImageID ValueImage() const { return ImageID::invalid(); } float CalculateValueScale(const UIContext &dc, std::string_view valueText, float availWidth) const; diff --git a/Core/Config.cpp b/Core/Config.cpp index 75c4613f28..6dc513aa8c 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1511,6 +1511,13 @@ void Config::PostLoadCleanup() { if (g_Config.sCustomDriver == "Default") { g_Config.sCustomDriver = ""; } + + // Squash unsupported screen rotations. + if (g_Config.iScreenRotation == ROTATION_AUTO_HORIZONTAL) { + g_Config.iScreenRotation = ROTATION_LOCKED_HORIZONTAL; + } else if (g_Config.iScreenRotation == ROTATION_LOCKED_VERTICAL180) { + g_Config.iScreenRotation = ROTATION_LOCKED_VERTICAL; + } } void Config::PreSaveCleanup() { diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index 40f71f4e50..eb32f16306 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -84,8 +84,8 @@ enum { ROTATION_LOCKED_HORIZONTAL = 1, ROTATION_LOCKED_VERTICAL = 2, ROTATION_LOCKED_HORIZONTAL180 = 3, - ROTATION_LOCKED_VERTICAL180 = 4, - ROTATION_AUTO_HORIZONTAL = 5, + ROTATION_LOCKED_VERTICAL180 = 4, // Deprecated + ROTATION_AUTO_HORIZONTAL = 5, // Deprecated }; enum TextureFiltering { diff --git a/UI/CustomButtonMappingScreen.cpp b/UI/CustomButtonMappingScreen.cpp index 010b78e9af..a2cab19c47 100644 --- a/UI/CustomButtonMappingScreen.cpp +++ b/UI/CustomButtonMappingScreen.cpp @@ -158,7 +158,7 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { vertLayout->Add(new CheckBox(show, co->T("Visible"))); Choice *icon = vertLayout->Add(new Choice(co->T("Icon"))); - icon->SetIcon(ImageID(customKeyImages[cfg->image].i), 1.0f, customKeyImages[cfg->image].r*PI/180, false, false); // Set right icon on the choice + icon->SetIconRight(ImageID(customKeyImages[cfg->image].i), 1.0f, customKeyImages[cfg->image].r*PI/180, false, false); // Set right icon on the choice icon->OnClick.Add([=](UI::EventParams &e) { auto iconScreen = new ButtonIconScreen(co->T("Icon"), &(cfg->image)); if (e.v) @@ -168,7 +168,7 @@ void CustomButtonMappingScreen::CreateDialogViews(UI::ViewGroup *parent) { }); Choice *shape = vertLayout->Add(new Choice(co->T("Shape"))); - shape->SetIcon(ImageID(customKeyShapes[cfg->shape].l), 0.6f, customKeyShapes[cfg->shape].r*PI/180, customKeyShapes[cfg->shape].f, false); // Set right icon on the choice + shape->SetIconRight(ImageID(customKeyShapes[cfg->shape].l), 0.6f, customKeyShapes[cfg->shape].r*PI/180, customKeyShapes[cfg->shape].f, false); // Set right icon on the choice shape->OnClick.Add([=](UI::EventParams &e) { auto shape = new ButtonShapeScreen(co->T("Shape"), &(cfg->shape)); if (e.v) diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 988f1ed5b1..3b473ad8cf 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -376,7 +376,7 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects; }); if (g_Config.iMultiSampleLevel > 1 && draw->GetDeviceCaps().isTilingGPU) { - msaaChoice->SetIcon(ImageID("I_WARNING"), 0.7f); + msaaChoice->SetIconRight(ImageID("I_WARNING"), 0.7f); } msaaChoice->SetEnabledFunc([] { return !g_Config.bSoftwareRendering && !g_Config.bSkipBufferEffects; @@ -1115,7 +1115,7 @@ void GameSettingsScreen::CreateToolsSettings(UI::ViewGroup *tools) { retro->OnClick.Add([=](UI::EventParams &) -> void { screenManager()->push(new RetroAchievementsSettingsScreen(gamePath_)); }); - retro->SetIcon(ImageID("I_RETROACHIEVEMENTS_LOGO")); + retro->SetIconRight(ImageID("I_RETROACHIEVEMENTS_LOGO")); } // These were moved here so use the wrong translation objects, to avoid having to change all inis... This isn't a sustainable situation :P @@ -1358,15 +1358,7 @@ void GameSettingsScreen::CreateSystemSettings(UI::ViewGroup *systemSettings) { if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) { auto co = GetI18NCategory(I18NCat::CONTROLS); - static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed", "Portrait Reversed", "Landscape Auto" }; - PopupMultiChoice *rot = systemSettings->Add(new PopupMultiChoice(&g_Config.iScreenRotation, co->T("Screen Rotation"), screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager())); - #if PPSSPP_PLATFORM(IOS) - // Portrait Reversed is not recommended on iPhone (and we also ban it in the plist). - // However it's recommended to support it on iPad, so maybe we will in the future. - rot->HideChoice(4); - #endif - - rot->OnChoice.Handle(this, &GameSettingsScreen::OnScreenRotation); + AddRotationPicker(screenManager(), systemSettings, true); if (System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE)) { systemSettings->Add(new CheckBox(&g_Config.bSustainedPerformanceMode, sy->T("Sustained performance mode")))->OnClick.Handle(this, &GameSettingsScreen::OnSustainedPerformanceModeChange); @@ -1467,13 +1459,6 @@ void GameSettingsScreen::OnAutoFrameskip(UI::EventParams &e) { g_Config.UpdateAfterSettingAutoFrameSkip(); } -void GameSettingsScreen::OnScreenRotation(UI::EventParams &e) { - INFO_LOG(Log::System, "New display rotation: %d", g_Config.iScreenRotation); - INFO_LOG(Log::System, "Sending rotate"); - System_Notify(SystemNotification::ROTATE_UPDATED); - INFO_LOG(Log::System, "Got back from rotate"); -} - void GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) { auto n = GetI18NCategory(I18NCat::NETWORKING); std::string url(n->T("MultiplayerHowToURL", "https://github.com/hrydgard/ppsspp/wiki/How-to-play-multiplayer-games-with-PPSSPP")); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 7d384da062..0bf5e63fbe 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -100,7 +100,6 @@ private: void OnMemoryStickMyDoc(UI::EventParams &e); void OnMemoryStickOther(UI::EventParams &e); #endif - void OnScreenRotation(UI::EventParams &e); void OnImmersiveModeChange(UI::EventParams &e); void OnSustainedPerformanceModeChange(UI::EventParams &e); diff --git a/UI/IAPScreen.cpp b/UI/IAPScreen.cpp index 36826d7fe3..5db05de21b 100644 --- a/UI/IAPScreen.cpp +++ b/UI/IAPScreen.cpp @@ -70,7 +70,7 @@ void IAPScreen::CreateViews() { image = ImageID("I_LOGO_APP_STORE"); #endif Choice *buyButton = rightColumnItems->Add(new Choice(mm->T("Buy PPSSPP Gold"), image)); - buyButton->SetIcon(ImageID("I_ICON_GOLD"), 0.5f); + buyButton->SetIconRight(ImageID("I_ICON_GOLD"), 0.5f); buyButton->SetShine(true); const int requesterToken = GetRequesterToken(); buyButton->OnClick.Add([this, requesterToken](UI::EventParams &) { diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 7e6e351423..b949eb4109 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -1206,7 +1206,7 @@ void MainScreen::CreateMainButtons(UI::ViewGroup *parent, bool portrait) { gold->OnClick.Add([this](UI::EventParams &) { LaunchBuyGold(this->screenManager()); }); - gold->SetIcon(ImageID("I_ICON_GOLD"), 0.5f); + gold->SetIconRight(ImageID("I_ICON_GOLD"), 0.5f); gold->SetImageScale(0.6f); // for the left-icon in case of vertical. gold->SetShine(true); } diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 0e0fce9b23..8566b33847 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -555,7 +555,7 @@ void CreditsScreen::CreateDialogViews(UI::ViewGroup *parent) { if (!System_GetPropertyBool(SYSPROP_APP_GOLD)) { ScreenManager *sm = screenManager(); Choice *gold = new Choice(mm->T("Buy PPSSPP Gold")); - gold->SetIcon(ImageID("I_ICON_GOLD"), 0.5f); + gold->SetIconRight(ImageID("I_ICON_GOLD"), 0.5f); gold->SetImageScale(0.6f); // for the left-icon in case of vertical. gold->SetShine(true); diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index da94334e51..b1e8d51fa2 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -9,6 +9,7 @@ #include "Common/StringUtils.h" #include "UI/MiscViews.h" #include "UI/GameInfoCache.h" +#include "Common/UI/PopupScreens.h" #include "Core/Config.h" TextWithImage::TextWithImage(ImageID imageID, std::string_view text, UI::LinearLayoutParams *layoutParams) : UI::LinearLayout(ORIENT_HORIZONTAL, layoutParams) { @@ -224,3 +225,26 @@ void GameImageView::Draw(UIContext &dc) { dc.Flush(); dc.RebindTexture(); } + +void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool text) { + using namespace UI; + static const char *screenRotation[] = { "Auto", "Landscape", "Portrait", "Landscape Reversed" }; + static const std::map screenRotationIcons{ + {ROTATION_AUTO, ImageID("I_DEVICE_ROTATION_AUTO")}, + {ROTATION_LOCKED_HORIZONTAL, ImageID("I_DEVICE_ROTATION_LANDSCAPE")}, + {ROTATION_LOCKED_VERTICAL, ImageID("I_DEVICE_ROTATION_PORTRAIT")}, + {ROTATION_LOCKED_HORIZONTAL180, ImageID("I_DEVICE_ROTATION_LANDSCAPE_REV")}, + }; + + auto co = GetI18NCategory(I18NCat::CONTROLS); + + PopupMultiChoice *rot = parent->Add(new PopupMultiChoice(&g_Config.iScreenRotation, text ? co->T("Screen Rotation") : "", screenRotation, 0, ARRAY_SIZE(screenRotation), I18NCat::CONTROLS, screenManager, text ? nullptr : new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT))); + rot->SetChoiceIcons(screenRotationIcons); + // Portrait Reversed is not recommended on iPhone (and we also ban it in the plist). + // However it's recommended to support it on iPad, so maybe we will in the future. + rot->HideChoice(4); + rot->OnChoice.Add([](UI::EventParams &) { + INFO_LOG(Log::System, "New display rotation: %d", g_Config.iScreenRotation); + System_Notify(SystemNotification::ROTATE_UPDATED); + }); +} diff --git a/UI/MiscViews.h b/UI/MiscViews.h index 4fed63ce60..20775a6f73 100644 --- a/UI/MiscViews.h +++ b/UI/MiscViews.h @@ -76,3 +76,5 @@ private: GameInfoFlags image_; float scale_ = 1.0f; }; + +void AddRotationPicker(ScreenManager *screenManager, UI::ViewGroup *parent, bool text); diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 5839e24bf7..369996bda4 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -632,22 +632,26 @@ void GamePauseScreen::CreateViews() { if (middleColumn) { middleColumn->SetSpacing(portrait ? 8.0f : 20.0f); - playButton_ = middleColumn->Add(new Button("", g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY"), new LinearLayoutParams(64, 64))); + playButton_ = middleColumn->Add(new Choice(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY"), new LinearLayoutParams(64, 64))); playButton_->OnClick.Add([=](UI::EventParams &e) { g_Config.bRunBehindPauseMenu = !g_Config.bRunBehindPauseMenu; - playButton_->SetImageID(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY")); + playButton_->SetIconLeft(g_Config.bRunBehindPauseMenu ? ImageID("I_PAUSE") : ImageID("I_PLAY")); }); bool mustRunBehind = MustRunBehind(); playButton_->SetVisibility(mustRunBehind ? UI::V_GONE : UI::V_VISIBLE); - Button *infoButton = middleColumn->Add(new Button("", ImageID("I_INFO"), new LinearLayoutParams(64, 64))); + Choice *infoButton = middleColumn->Add(new Choice(ImageID("I_INFO"), new LinearLayoutParams(64, 64))); infoButton->OnClick.Add([=](UI::EventParams &e) { screenManager()->push(new GameScreen(gamePath_, true)); }); + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) { + AddRotationPicker(screenManager(), middleColumn, false); + } + if (!portrait) { - Button *menuButton = middleColumn->Add(new Button("", ImageID("I_THREE_DOTS"), new LinearLayoutParams(64, 64))); + Choice *menuButton = middleColumn->Add(new Choice("", ImageID("I_THREE_DOTS"), new LinearLayoutParams(64, 64))); menuButton->OnClick.Add([this, menuButton, portrait](UI::EventParams &e) { ShowContextMenu(menuButton, portrait); }); diff --git a/UI/PauseScreen.h b/UI/PauseScreen.h index 43b79205e1..ebcb992dfd 100644 --- a/UI/PauseScreen.h +++ b/UI/PauseScreen.h @@ -65,7 +65,7 @@ private: bool finishNextFrame_ = false; DialogResult finishNextFrameResult_ = DR_CANCEL; - UI::Button *playButton_ = nullptr; + UI::Choice *playButton_ = nullptr; // State change tracking, a bit ugly heh, but works. bool lastOnline_ = false; diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index c6c0437301..ba6fa19608 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -469,7 +469,7 @@ void RemoteISOConnectScreen::CreateViews() { statusView_ = leftColumnItems->Add(new TextView(ri->T("RemoteISOScanning", "Scanning... click Share Games on your desktop"), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5)))); rightColumnItems->SetSpacing(0.0f); - rightColumnItems->Add(new Choice(di->T("Cancel"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); + rightColumnItems->Add(new Choice(di->T("Cancel"), "", 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/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 9e584f964d..ca27d77083 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -125,7 +125,7 @@ void TouchControlVisibilityScreen::CreateContentViews(UI::ViewGroup *parent) { } else { truncate_cpy(translated, sizeof(translated), mc->T(toggle.key)); } - choice = new Choice(std::string(translated) + " (" + std::string(mc->T("tap to customize")) + ")", "", false, new LinearLayoutParams(1.0f)); + choice = new Choice(std::string(translated) + " (" + std::string(mc->T("tap to customize")) + ")", "", new LinearLayoutParams(1.0f)); choice->OnClick.Add(toggle.handle); } else if (toggle.img.isValid()) { choice = new CheckBoxChoice(toggle.img, checkbox, new LinearLayoutParams(1.0f)); diff --git a/UI/UIAtlas.cpp b/UI/UIAtlas.cpp index 65f1109c53..e4f5c91014 100644 --- a/UI/UIAtlas.cpp +++ b/UI/UIAtlas.cpp @@ -21,6 +21,7 @@ #include "ext/nanosvg/src/nanosvgrast.h" constexpr bool SAVE_DEBUG_IMAGES = false; +constexpr bool SAVE_DEBUG_ATLAS = false; static Atlas ui_atlas; static Atlas font_atlas; @@ -164,6 +165,10 @@ static const ImageMeta imageIDs[] = { {"I_EXIT", false}, {"I_CHEAT", false}, {"I_HAMBURGER", false}, + {"I_DEVICE_ROTATION_LANDSCAPE_REV", false}, + {"I_DEVICE_ROTATION_AUTO", false}, + {"I_DEVICE_ROTATION_LANDSCAPE", false}, + {"I_DEVICE_ROTATION_PORTRAIT", false}, }; static std::string PNGNameFromID(std::string_view id) { @@ -192,6 +197,12 @@ static bool IsImageID(std::string_view id) { static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest, int maxTextureSize) { Bucket bucket; +#ifdef _DEBUG + for (int i = 0; i < ARRAY_SIZE(imageIDs); i++) { + _dbg_assert_(imageIDs[i].id.size() < 32); + } +#endif + // Script fully read, now read images and rasterize the fonts. std::vector images(ARRAY_SIZE(imageIDs)); int resultIds[ARRAY_SIZE(imageIDs)]{}; @@ -313,7 +324,7 @@ static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest, int shapeCount = (int)usedShapes.size(); - if (SAVE_DEBUG_IMAGES) { + if (SAVE_DEBUG_ATLAS) { WARN_LOG(Log::G3D, "Writing debug image buttons_rasterized.png"); pngSave(Path("../buttons_rasterized.png"), svgImg, svgWidth, svgHeight, 4); } @@ -416,7 +427,7 @@ static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest, int atlas->num_images = (int)genAtlasImages.size(); // For debug, write out the atlas. - if (SAVE_DEBUG_IMAGES) { + if (SAVE_DEBUG_ATLAS) { WARN_LOG(Log::G3D, "Writing debug image ui_atlas_gen.png"); dest->SavePNG("../ui_atlas_gen.png"); } diff --git a/assets/ui_images/images.svg b/assets/ui_images/images.svg index de3fbbc6ee..ff06ccfb52 100644 --- a/assets/ui_images/images.svg +++ b/assets/ui_images/images.svg @@ -3,8 +3,8 @@ + + + +