diff --git a/Common/UI/ScrollView.cpp b/Common/UI/ScrollView.cpp index 30b9c12ff0..4ce5e86c8c 100644 --- a/Common/UI/ScrollView.cpp +++ b/Common/UI/ScrollView.cpp @@ -226,15 +226,19 @@ float ScrollView::ChildSize() const { return std::max(0.01f, views_[0]->GetBounds().GetSize(orientation_) + extraSpace); } +float ScrollView::ScrollMax() const { + const float childHeight = ChildSize(); + return std::max(0.0f, childHeight - bounds_.GetSize(orientation_)); +} + ScrollView::Bob ScrollView::ComputeBob() const { Bob bob{}; if (views_.empty()) { return bob; } const float childHeight = ChildSize(); - float scrollMax = std::max(0.0f, childHeight - bounds_.h); - float ratio = bounds_.h / childHeight; - + const float ratio = bounds_.h / childHeight; + const float scrollMax = ScrollMax(); if (ratio < 1.0f && scrollMax > 0.0f) { bob.show = true; bob.thickness = draggingBob_ ? 15.0f : 6.0f; @@ -260,6 +264,8 @@ void ScrollView::Draw(UIContext &dc) { // If not anchored at the top of the screen exactly, and not scrolled to the top, // draw a subtle drop shadow to indicate scrollability. + + const float darkness = 0.4f; if (bounds_.y > 0.0f && orientation_ == ORIENT_VERTICAL) { float radius = 20.0f; @@ -269,11 +275,33 @@ void ScrollView::Draw(UIContext &dc) { shadowBounds.y -= radius * 2; shadowBounds.h = radius * 2; - float fade = std::clamp(scrollPos_ * 0.1f, 0.0f, 0.6f); + float fade = std::clamp(scrollPos_ * 0.05f, 0.0f, 1.0f) * darkness; dc.DrawRectDropShadow(shadowBounds, radius, fade); } + // Same at the bottom. + float y2 = dc.GetLayoutBounds().y2(); + if (bounds_.y2() < y2 && orientation_ == ORIENT_VERTICAL) { + float radius = 20.0f; + + Bounds shadowBounds = bounds_; + shadowBounds.x -= radius; + shadowBounds.w += radius * 2; + shadowBounds.y = bounds_.y2() - radius * 2; + shadowBounds.h = radius * 2; + + float fade = std::clamp((ScrollMax() - scrollPos_) * 0.05f, 0.0f, 1.0f) * darkness; + + dc.DrawRectDropShadow(shadowBounds, radius, fade); + } + + // If there are any additional views, we treat them as overlays attached at the top - they don't scroll, and they're + // drawn on top of the shadow. TODO: Give them an idea about the scroll position so they can fade in? + for (size_t i = 1; i < views_.size(); ++i) { + views_[i]->Draw(dc); + } + dc.PopScissor(); // Vertical scroll bob. We don't support a horizontal yet. diff --git a/Common/UI/ScrollView.h b/Common/UI/ScrollView.h index 480644f675..f090c9f9d9 100644 --- a/Common/UI/ScrollView.h +++ b/Common/UI/ScrollView.h @@ -48,6 +48,7 @@ public: private: float ChildSize() const; // in the scrolled direction + float ScrollMax() const; float HardClampedScrollPos(float pos) const; // TODO: Don't adjust pull_ within this! diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 83b2b9fcf7..572432fd49 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -532,7 +532,7 @@ void AnalogCalibrationScreen::axis(const AxisInput &axis) { std::string_view AnalogCalibrationScreen::GetTitle() const { auto co = GetI18NCategory(I18NCat::CONTROLS); - return co->T("Analog Settings"); + return co->T("Calibrate analog stick"); } void AnalogCalibrationScreen::CreateSettingsViews(UI::LinearLayout *scrollContents) { diff --git a/UI/GameScreen.cpp b/UI/GameScreen.cpp index 18747748d6..dbac4757c6 100644 --- a/UI/GameScreen.cpp +++ b/UI/GameScreen.cpp @@ -158,21 +158,28 @@ void GameScreen::CreateContentViews(UI::LinearLayout *parent) { } mainGameInfo->SetSpacing(3.0f); + GameDBInfo dbInfo; + std::vector dbInfos; + const bool inGameDB = g_gameDB.GetGameInfos(info_->id_version, &dbInfos); + 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))); + // Show the game ID title below the icon. The top title will be from the DB. + std::string title = info_->GetTitle(); + + TextView *tvTitle = mainGameInfo->Add(new TextView(title, 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))); - tvID->SetShadow(true); + std::string regionID = ReplaceAll(info_->id_version, "_", " v"); + regionID += ": "; 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); + regionID += GameRegionToString(info_->region); } else { - TextView *tvFileType = mainGameInfo->Add(new TextView(ga->T(IdentifiedFileTypeToString(info_->fileType)), ALIGN_LEFT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); - tvFileType->SetShadow(true); + regionID += IdentifiedFileTypeToString(info_->fileType); } + + TextView *tvID = mainGameInfo->Add(new TextView(regionID, ALIGN_LEFT | FLAG_WRAP_TEXT, true, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + tvID->SetShadow(true); } LinearLayout *infoLayout = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(10, 200, NONE, NONE)); @@ -237,7 +244,7 @@ void GameScreen::CreateContentViews(UI::LinearLayout *parent) { std::vector dbInfos; if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && g_gameDB.GetGameInfos(info_->id_version, &dbInfos)) { bool found = false; - for (auto &dbInfo : dbInfos) { + for (const auto &dbInfo : dbInfos) { if (dbInfo.crc == crcVal) { found = true; } @@ -248,9 +255,7 @@ void GameScreen::CreateContentViews(UI::LinearLayout *parent) { } } } else if (!isHomebrew_) { - GameDBInfo dbInfo; - std::vector dbInfos; - if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && !g_gameDB.GetGameInfos(info_->id_version, &dbInfos)) { + if ((knownFlags_ & GameInfoFlags::PARAM_SFO) && !inGameDB) { // tvVerified_->SetText(ga->T("Game ID unknown - not in the ReDump database")); // tvVerified_->SetVisibility(UI::V_VISIBLE); // tvVerified_->SetLevel(NoticeLevel::WARN); @@ -309,12 +314,14 @@ void GameScreen::CreateSettingsViews(UI::LinearLayout *rightColumn) { } 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) { + // Only show the Game Settings button here if the game has a config. Always showing it + // is confusing since it'll just control the global settings. + Choice *btnGameSettings = rightColumnItems->Add(new Choice(ga->T("Game Settings"))); + btnGameSettings->OnClick.Handle(this, &GameScreen::OnGameSettings); + if (inGame_) + btnGameSettings->SetEnabled(false); + Choice *btnDeleteGameConfig = rightColumnItems->Add(new Choice(ga->T("Delete Game Config"))); btnDeleteGameConfig->OnClick.Handle(this, &GameScreen::OnDeleteConfig); if (inGame_) @@ -386,17 +393,30 @@ void GameScreen::CreateSettingsViews(UI::LinearLayout *rightColumn) { } void GameScreen::OnCreateConfig(UI::EventParams &e) { - std::shared_ptr info = g_gameInfoCache->GetInfo(nullptr, gamePath_, GameInfoFlags::PARAM_SFO); - if (!info->Ready(GameInfoFlags::PARAM_SFO)) { + if (!info_->Ready(GameInfoFlags::PARAM_SFO)) { return; } - g_Config.CreateGameConfig(info->id); - g_Config.SaveGameConfig(info->id, info->GetTitle()); - info->hasConfig = true; + g_Config.CreateGameConfig(info_->id); + g_Config.SaveGameConfig(info_->id, info_->GetTitle()); + info_->hasConfig = true; screenManager()->topScreen()->RecreateViews(); } +std::string_view GameScreen::GetTitle() const { + if (knownFlags_ & GameInfoFlags::PARAM_SFO) { + std::vector dbInfos; + const bool inGameDB = g_gameDB.GetGameInfos(info_->id_version, &dbInfos); + if (inGameDB) { + titleCache_ = dbInfos[0].title; + } else { + titleCache_ = std::string(info_->GetTitle()); + } + } + + return titleCache_; +} + void GameScreen::OnDeleteConfig(UI::EventParams &e) { auto di = GetI18NCategory(I18NCat::DIALOG); const bool trashAvailable = System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN); diff --git a/UI/GameScreen.h b/UI/GameScreen.h index e350e4fcb4..012efd7bfa 100644 --- a/UI/GameScreen.h +++ b/UI/GameScreen.h @@ -50,6 +50,7 @@ protected: void CreateSettingsViews(UI::LinearLayout *parent) override; bool SettingsToTheRight() const override { return true; } + std::string_view GetTitle() const override; private: // Event handlers @@ -75,4 +76,5 @@ private: bool knownHasCRC_ = false; std::shared_ptr info_; + mutable std::string titleCache_; }; diff --git a/UI/MiscViews.cpp b/UI/MiscViews.cpp index 42767595cf..d361828cf7 100644 --- a/UI/MiscViews.cpp +++ b/UI/MiscViews.cpp @@ -56,7 +56,8 @@ TopBar::TopBar(const UIContext &ctx, bool usePortraitLayout, std::string_view ti e.bubbleResult = DR_BACK; }); if (!usePortraitLayout) { - backButton_->SetText(dlg->T("Back")); + // Not really liking this.. + // backButton_->SetText(dlg->T("Back")); } if (!title.empty()) { diff --git a/UI/SimpleDialogScreen.cpp b/UI/SimpleDialogScreen.cpp index 3dbf1af41c..35824cd964 100644 --- a/UI/SimpleDialogScreen.cpp +++ b/UI/SimpleDialogScreen.cpp @@ -50,7 +50,14 @@ void UITwoPaneBaseDialogScreen::CreateViews() { root_ = root; } else { ignoreBottomInset_ = false; - LinearLayout *root = new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); + LinearLayout *root = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT)); + std::string title(GetTitle()); + root->Add(new TopBar(*screenManager()->getUIContext(), portrait, title)); + root->SetSpacing(0); + + LinearLayout *columns = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f)); + root->Add(columns); + // root_->Add(new TopBar(*screenManager()->getUIContext(), portrait, GetTitle(), new LayoutParams(FILL_PARENT, 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)); @@ -61,11 +68,11 @@ void UITwoPaneBaseDialogScreen::CreateViews() { settingsScroll->Add(settingsPane); if (SettingsToTheRight()) { - CreateContentViews(root); - root->Add(settingsScroll); + CreateContentViews(columns); + columns->Add(settingsScroll); } else { - root->Add(settingsScroll); - CreateContentViews(root); + columns->Add(settingsScroll); + CreateContentViews(columns); } root_ = root;