More layout work. Use GameDB titles on the top of the game screen.

This commit is contained in:
Henrik Rydgård
2025-11-11 11:22:10 +01:00
parent 66c2045d35
commit 09d195c9ef
7 changed files with 92 additions and 33 deletions
+32 -4
View File
@@ -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.
+1
View File
@@ -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!
+1 -1
View File
@@ -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) {
+42 -22
View File
@@ -158,21 +158,28 @@ void GameScreen::CreateContentViews(UI::LinearLayout *parent) {
}
mainGameInfo->SetSpacing(3.0f);
GameDBInfo dbInfo;
std::vector<GameDBInfo> 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<GameDBInfo> 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<GameDBInfo> 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<GameInfo> 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<GameDBInfo> 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);
+2
View File
@@ -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<GameInfo> info_;
mutable std::string titleCache_;
};
+2 -1
View File
@@ -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()) {
+12 -5
View File
@@ -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;