mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-25 03:09:13 +01:00
Merge pull request #21006 from hrydgard/new-game-screen
Rework the game screen to work better in portrait mode
This commit is contained in:
32 files changed
+440
-192
No files matched your search
@@ -54,6 +54,10 @@ struct Bounds {
|
||||
return Bounds(x_ - radius, y_ - radius, radius * 2.0f, radius * 2.0f);
|
||||
}
|
||||
|
||||
static Bounds FromCenterWH(float x_, float y_, float w, float h) {
|
||||
return Bounds(x_ - w * 0.5f, y_ - h * 0.5f, w, h);
|
||||
}
|
||||
|
||||
float GetSize(Orientation o) const {
|
||||
return (o == ORIENT_HORIZONTAL) ? w : h;
|
||||
}
|
||||
@@ -116,6 +120,10 @@ struct Bounds {
|
||||
return Bounds(x + left, y + top, w - left - right, h - top - bottom);
|
||||
}
|
||||
|
||||
float AspectRatio() const {
|
||||
return w / h;
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
|
||||
@@ -267,6 +267,13 @@ void PopupContextMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
|
||||
ap->top = sourceView_->GetBounds().y2();
|
||||
}
|
||||
|
||||
void PopupCallbackScreen::CreatePopupContents(ViewGroup *parent) {
|
||||
createViews_(parent);
|
||||
for (int i = 0; i < parent->GetNumSubviews(); i++) {
|
||||
parent->GetViewByIndex(i)->SetAutoResult(DialogResult::DR_OK);
|
||||
}
|
||||
}
|
||||
|
||||
std::string ChopTitle(const std::string &title) {
|
||||
size_t pos = title.find('\n');
|
||||
if (pos != title.npos) {
|
||||
|
||||
@@ -241,7 +241,6 @@ struct ContextMenuItem {
|
||||
class PopupContextMenuScreen : public PopupScreen {
|
||||
public:
|
||||
PopupContextMenuScreen(const ContextMenuItem *items, size_t itemCount, I18NCat category, UI::View *sourceView);
|
||||
void CreatePopupContents(ViewGroup *parent) override;
|
||||
|
||||
const char *tag() const override { return "ContextMenuPopup"; }
|
||||
|
||||
@@ -252,6 +251,7 @@ public:
|
||||
UI::Event OnChoice;
|
||||
|
||||
private:
|
||||
void CreatePopupContents(ViewGroup *parent) override;
|
||||
const ContextMenuItem *items_;
|
||||
size_t itemCount_;
|
||||
I18NCat category_;
|
||||
@@ -259,6 +259,21 @@ private:
|
||||
std::vector<bool> enabled_;
|
||||
};
|
||||
|
||||
class PopupCallbackScreen : public PopupScreen {
|
||||
public:
|
||||
PopupCallbackScreen(std::function<void(UI::ViewGroup *)> createViews, UI::View *sourceView) : PopupScreen(""), createViews_(createViews) {
|
||||
if (sourceView) {
|
||||
SetPopupOrigin(sourceView);
|
||||
}
|
||||
}
|
||||
|
||||
const char *tag() const override { return "ContextMenuPopup"; }
|
||||
|
||||
private:
|
||||
void CreatePopupContents(ViewGroup *parent) override;
|
||||
std::function<void(UI::ViewGroup *)> createViews_;
|
||||
};
|
||||
|
||||
// Reads and writes value to determine the current selection.
|
||||
class PopupMultiChoice : public AbstractChoiceWithValueDisplay {
|
||||
public:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace UI {
|
||||
|
||||
TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams)
|
||||
TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, std::function<void()> contextMenuCallback, LayoutParams *layoutParams)
|
||||
: LinearLayout(Opposite(orientation), layoutParams), tabOrientation_(orientation), flags_(flags) {
|
||||
SetSpacing(0.0f);
|
||||
if (orientation == ORIENT_HORIZONTAL) {
|
||||
@@ -26,6 +26,13 @@ TabHolder::TabHolder(Orientation orientation, float stripSize, TabHolderFlags fl
|
||||
tabScroll_ = new ScrollView(orientation, new LinearLayoutParams(1.0f));
|
||||
tabScroll_->Add(tabStrip_);
|
||||
container->Add(tabScroll_);
|
||||
if (contextMenuCallback) {
|
||||
Choice *menuChoice = new Choice(ImageID("I_THREE_DOTS"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT));
|
||||
menuChoice->OnClick.Add([contextMenuCallback](EventParams &e) {
|
||||
contextMenuCallback();
|
||||
});
|
||||
container->Add(menuChoice);
|
||||
}
|
||||
Add(container);
|
||||
} else {
|
||||
tabScroll_ = new ScrollView(orientation, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
|
||||
@@ -21,7 +21,7 @@ ENUM_CLASS_BITOPS(TabHolderFlags);
|
||||
|
||||
class TabHolder : public LinearLayout {
|
||||
public:
|
||||
TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, LayoutParams *layoutParams);
|
||||
TabHolder(Orientation orientation, float stripSize, TabHolderFlags flags, View *bannerView, std::function<void()> contextMenuCallback, LayoutParams *layoutParams);
|
||||
|
||||
template <class T>
|
||||
T *AddTab(std::string_view title, ImageID imageId, T *tabContents) {
|
||||
|
||||
@@ -221,6 +221,9 @@ void Clickable::DrawBG(UIContext &dc, const Style &style) {
|
||||
void Clickable::ClickInternal() {
|
||||
UI::EventParams e{};
|
||||
e.v = this;
|
||||
if (hasAutoResult_) {
|
||||
e.bubbleResult = autoResult_;
|
||||
}
|
||||
OnClick.Trigger(e);
|
||||
};
|
||||
|
||||
|
||||
+9
-1
@@ -463,6 +463,10 @@ public:
|
||||
}
|
||||
|
||||
virtual void Recurse(void (*func)(View *view)) {}
|
||||
virtual void SetAutoResult(DialogResult result) {
|
||||
hasAutoResult_ = true;
|
||||
autoResult_ = result;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Inputs to layout
|
||||
@@ -482,6 +486,8 @@ protected:
|
||||
|
||||
// Whether to use popup colors for styling.
|
||||
bool popupStyle_ = false;
|
||||
bool hasAutoResult_ = false;
|
||||
DialogResult autoResult_ = DR_OK;
|
||||
|
||||
private:
|
||||
std::function<bool()> enabledFunc_;
|
||||
@@ -893,6 +899,9 @@ public:
|
||||
virtual void Toggle();
|
||||
virtual bool Toggled() const;
|
||||
|
||||
// we don't allow these for checkboxes.
|
||||
virtual void SetAutoResult(DialogResult result) {}
|
||||
|
||||
protected:
|
||||
void ClickInternal() override;
|
||||
|
||||
@@ -998,7 +1007,6 @@ private:
|
||||
bool big_ = false;
|
||||
};
|
||||
|
||||
|
||||
class TextView : public InertView {
|
||||
public:
|
||||
TextView(std::string_view text, LayoutParams *layoutParams = 0)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+40
-2
@@ -443,6 +443,12 @@ uint32_t GetBackgroundColorWithAlpha(const UIContext &dc) {
|
||||
return colorAlpha(colorBlend(dc.GetTheme().backgroundColor, 0, 0.5f), 0.65f); // 0.65 = 166 = A6
|
||||
}
|
||||
|
||||
enum class BackgroundFillMode {
|
||||
Stretch = 0,
|
||||
CropToScreen = 1,
|
||||
FitToScreen = 2,
|
||||
};
|
||||
|
||||
void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, float z) {
|
||||
using namespace Draw;
|
||||
using namespace UI;
|
||||
@@ -454,10 +460,42 @@ void DrawGameBackground(UIContext &dc, const Path &gamePath, float x, float y, f
|
||||
}
|
||||
|
||||
GameInfoTex *pic = (ginfo && ginfo->Ready(GameInfoFlags::PIC1)) ? ginfo->GetPIC1() : nullptr;
|
||||
if (pic) {
|
||||
if (pic && pic->texture) {
|
||||
dc.GetDrawContext()->BindTexture(0, pic->texture);
|
||||
uint32_t color = whiteAlpha(ease((time_now_d() - pic->timeLoaded) * 3)) & 0xFFc0c0c0;
|
||||
dc.Draw()->DrawTexRect(dc.GetBounds(), 0, 0, 1, 1, color);
|
||||
|
||||
// TODO: Make this configurable?
|
||||
const BackgroundFillMode mode = BackgroundFillMode::CropToScreen;
|
||||
|
||||
const Bounds screenBounds = dc.GetBounds();
|
||||
float imageW = screenBounds.w;
|
||||
float imageH = screenBounds.h;
|
||||
float imageAspect = (float)pic->texture->Width() / (float)pic->texture->Height();
|
||||
|
||||
// Fit the image into the screen bounds according to the fill mode.
|
||||
if (imageAspect > screenBounds.AspectRatio()) {
|
||||
// Image is wider than screen.
|
||||
if (mode == BackgroundFillMode::CropToScreen) {
|
||||
// Crop width.
|
||||
imageW = screenBounds.h * imageAspect;
|
||||
} else if (mode == BackgroundFillMode::FitToScreen) {
|
||||
// Fit height.
|
||||
imageH = screenBounds.w / imageAspect;
|
||||
}
|
||||
} else {
|
||||
// Image is taller than screen.
|
||||
if (mode == BackgroundFillMode::CropToScreen) {
|
||||
// Crop height.
|
||||
imageH = screenBounds.w / imageAspect;
|
||||
} else if (mode == BackgroundFillMode::FitToScreen) {
|
||||
// Fit width.
|
||||
imageW = screenBounds.h / imageAspect;
|
||||
}
|
||||
}
|
||||
|
||||
Bounds finalImageBounds = Bounds::FromCenterWH(screenBounds.centerX(), screenBounds.centerY(), imageW, imageH);
|
||||
|
||||
dc.Draw()->DrawTexRect(finalImageBounds, 0, 0, 1, 1, color);
|
||||
dc.Flush();
|
||||
dc.RebindTexture();
|
||||
} else {
|
||||
|
||||
@@ -232,7 +232,7 @@ static const BindingCategory cats[] = {
|
||||
{}, // sentinel
|
||||
};
|
||||
|
||||
void ControlMappingScreen::CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) {
|
||||
void ControlMappingScreen::CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) {
|
||||
using namespace UI;
|
||||
auto km = GetI18NCategory(I18NCat::KEYMAPPING);
|
||||
verticalLayout->Add(new Choice(km->T("Clear All")))->OnClick.Add([](UI::EventParams &) {
|
||||
@@ -480,7 +480,7 @@ void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) {
|
||||
}
|
||||
}
|
||||
|
||||
AnalogCalibrationScreen::AnalogCalibrationScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath) {
|
||||
AnalogCalibrationScreen::AnalogCalibrationScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::Default) {
|
||||
mapper_.SetCallbacks(
|
||||
[](int vkey, bool down) {},
|
||||
[](int vkey, float analogValue) {},
|
||||
@@ -532,10 +532,10 @@ 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) {
|
||||
void AnalogCalibrationScreen::CreateSettingsViews(UI::ViewGroup *scrollContents) {
|
||||
using namespace UI;
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
@@ -551,7 +551,7 @@ void AnalogCalibrationScreen::CreateSettingsViews(UI::LinearLayout *scrollConten
|
||||
scrollContents->Add(new Choice(co->T("Reset to defaults")))->OnClick.Handle(this, &AnalogCalibrationScreen::OnResetToDefaults);
|
||||
}
|
||||
|
||||
void AnalogCalibrationScreen::CreateContentViews(UI::LinearLayout *parent) {
|
||||
void AnalogCalibrationScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
using namespace UI;
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class SingleControlMapper;
|
||||
|
||||
class ControlMappingScreen : public UITabbedBaseDialogScreen {
|
||||
public:
|
||||
ControlMappingScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath) {
|
||||
ControlMappingScreen(const Path &gamePath) : UITabbedBaseDialogScreen(gamePath, TabDialogFlags::ContextMenuInPortrait) {
|
||||
categoryToggles_[0] = true;
|
||||
categoryToggles_[1] = true;
|
||||
categoryToggles_[2] = true;
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
|
||||
protected:
|
||||
void CreateTabs() override;
|
||||
void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) override;
|
||||
void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override;
|
||||
void update() override;
|
||||
|
||||
private:
|
||||
@@ -137,8 +137,8 @@ public:
|
||||
const char *tag() const override { return "AnalogSetup"; }
|
||||
|
||||
protected:
|
||||
void CreateSettingsViews(UI::LinearLayout *parent) override;
|
||||
void CreateContentViews(UI::LinearLayout *parent) override;
|
||||
void CreateSettingsViews(UI::ViewGroup *parent) override;
|
||||
void CreateContentViews(UI::ViewGroup *parent) override;
|
||||
|
||||
std::string_view GetTitle() const override;
|
||||
private:
|
||||
|
||||
@@ -301,7 +301,7 @@ void GPUDriverTestScreen::CreateViews() {
|
||||
AnchorLayout *anchor = new AnchorLayout();
|
||||
root_ = anchor;
|
||||
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, TabHolderFlags::Default, nullptr, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, Centering::None));
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 30.0f, TabHolderFlags::Default, nullptr, nullptr, new AnchorLayoutParams(FILL_PARENT, FILL_PARENT, Centering::None));
|
||||
anchor->Add(tabHolder_);
|
||||
tabHolder_->AddTab("Discard", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL));
|
||||
tabHolder_->AddTab("Shader", ImageID::invalid(), new LinearLayout(ORIENT_VERTICAL));
|
||||
|
||||
+143
-107
@@ -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, TwoPaneFlags::SettingsToTheRight | TwoPaneFlags::SettingsInContextMenu), 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<GameInfo> 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::ViewGroup *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,76 @@ void GameScreen::CreateViews() {
|
||||
|
||||
Margins actionMenuMargins(0, 15, 15, 0);
|
||||
|
||||
root_ = new LinearLayout(ORIENT_HORIZONTAL);
|
||||
ScrollView *leftScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f, Margins(8)));
|
||||
|
||||
ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f));
|
||||
root_->Add(leftColumn);
|
||||
ViewGroup *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
|
||||
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;
|
||||
leftScroll->Add(leftColumn);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent->Add(leftScroll);
|
||||
|
||||
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);
|
||||
|
||||
GameDBInfo dbInfo;
|
||||
std::vector<GameDBInfo> dbInfos;
|
||||
const bool inGameDB = g_gameDB.GetGameInfos(info_->id_version, &dbInfos);
|
||||
|
||||
if (knownFlags_ & GameInfoFlags::PARAM_SFO) {
|
||||
// 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);
|
||||
|
||||
std::string regionID = ReplaceAll(info_->id_version, "_", " v");
|
||||
regionID += ": ";
|
||||
|
||||
if (info_->region != GameRegion::UNKNOWN) {
|
||||
regionID += GameRegionToString(info_->region);
|
||||
} else {
|
||||
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));
|
||||
leftColumn->Add(infoLayout);
|
||||
|
||||
if (hasFlags & 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)));
|
||||
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);
|
||||
|
||||
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)));
|
||||
tvFileType->SetShadow(true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((hasFlags & GameInfoFlags::UNCOMPRESSED_SIZE) && (hasFlags & GameInfoFlags::SIZE)) {
|
||||
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 +215,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 +234,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,9 +247,9 @@ void GameScreen::CreateViews() {
|
||||
|
||||
// Let's check the CRC in the game database, looking up the ID and also matching the crc.
|
||||
std::vector<GameDBInfo> 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) {
|
||||
for (const auto &dbInfo : dbInfos) {
|
||||
if (dbInfo.crc == crcVal) {
|
||||
found = true;
|
||||
}
|
||||
@@ -248,17 +260,15 @@ void GameScreen::CreateViews() {
|
||||
}
|
||||
}
|
||||
} else if (!isHomebrew_) {
|
||||
GameDBInfo dbInfo;
|
||||
std::vector<GameDBInfo> dbInfos;
|
||||
if ((hasFlags & 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);
|
||||
} 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 +276,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 +290,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 +300,37 @@ void GameScreen::CreateViews() {
|
||||
}
|
||||
}
|
||||
|
||||
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
|
||||
root_->Add(rightColumn);
|
||||
if (portrait) {
|
||||
parent->Add(new Choice(ga->T("Play"), ImageID("I_PLAY")))->OnClick.Handle(this, &GameScreen::OnPlay);
|
||||
}
|
||||
}
|
||||
|
||||
void GameScreen::CreateSettingsViews(UI::ViewGroup *rightColumn) {
|
||||
using namespace UI;
|
||||
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
auto ga = GetI18NCategory(I18NCat::GAME);
|
||||
|
||||
const bool fileTypeSupportCRC = FileTypeSupportsCRC(info_->fileType);
|
||||
const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
|
||||
|
||||
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);
|
||||
if (!inGame_ && !portrait) {
|
||||
rightColumnItems->Add(new Choice(ga->T("Play"), ImageID("I_PLAY")))->OnClick.Handle(this, &GameScreen::OnPlay);
|
||||
}
|
||||
|
||||
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_->id.empty()) {
|
||||
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"), ImageID("I_GEAR")));
|
||||
btnGameSettings->OnClick.Handle(this, &GameScreen::OnGameSettings);
|
||||
if (inGame_)
|
||||
btnGameSettings->SetEnabled(false);
|
||||
|
||||
if (info->hasConfig) {
|
||||
Choice *btnDeleteGameConfig = rightColumnItems->Add(new Choice(ga->T("Delete Game Config")));
|
||||
btnDeleteGameConfig->OnClick.Handle(this, &GameScreen::OnDeleteConfig);
|
||||
if (inGame_)
|
||||
@@ -320,7 +343,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 +355,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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO, &hasFlags);
|
||||
std::shared_ptr<GameInfo> 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 +376,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 +386,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 &) {
|
||||
@@ -379,17 +402,30 @@ void GameScreen::CreateViews() {
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -466,20 +502,20 @@ void GameScreen::OnPlay(UI::EventParams &e) {
|
||||
}
|
||||
|
||||
void GameScreen::OnGameSettings(UI::EventParams &e) {
|
||||
std::shared_ptr<GameInfo> 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<GameInfo> 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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO | GameInfoFlags::SIZE);
|
||||
if (info) {
|
||||
std::shared_ptr<GameInfo> 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 +523,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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
|
||||
info->DeleteAllSaveData();
|
||||
info->saveDataSize = 0;
|
||||
info->installDataSize = 0;
|
||||
std::shared_ptr<GameInfo> info_ = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
|
||||
info_->DeleteAllSaveData();
|
||||
info_->saveDataSize = 0;
|
||||
info_->installDataSize = 0;
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -499,8 +535,8 @@ void GameScreen::OnDeleteSaveData(UI::EventParams &e) {
|
||||
}
|
||||
|
||||
void GameScreen::OnDeleteGame(UI::EventParams &e) {
|
||||
std::shared_ptr<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath_, GameInfoFlags::PARAM_SFO);
|
||||
if (info->Ready(GameInfoFlags::PARAM_SFO)) {
|
||||
std::shared_ptr<GameInfo> 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 +549,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<GameInfo> info = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
|
||||
info->Delete();
|
||||
std::shared_ptr<GameInfo> info_ = g_gameInfoCache->GetInfo(NULL, gamePath, GameInfoFlags::PARAM_SFO);
|
||||
info_->Delete();
|
||||
g_gameInfoCache->Clear();
|
||||
g_recentFiles.Remove(gamePath.c_str());
|
||||
sm->switchScreen(new MainScreen());
|
||||
|
||||
+10
-3
@@ -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::ViewGroup *parent) override;
|
||||
void CreateSettingsViews(UI::ViewGroup *parent) override;
|
||||
|
||||
std::string_view GetTitle() const override;
|
||||
|
||||
private:
|
||||
// Event handlers
|
||||
@@ -69,4 +73,7 @@ private:
|
||||
GameInfoFlags knownFlags_ = GameInfoFlags::EMPTY;
|
||||
|
||||
bool knownHasCRC_ = false;
|
||||
|
||||
std::shared_ptr<GameInfo> info_;
|
||||
mutable std::string titleCache_;
|
||||
};
|
||||
+15
-9
@@ -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));
|
||||
|
||||
@@ -1220,7 +1224,7 @@ void MainScreen::CreateViews() {
|
||||
|
||||
auto mm = GetI18NCategory(I18NCat::MAINMENU);
|
||||
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));
|
||||
ViewGroup *leftColumn = tabHolder_;
|
||||
tabHolder_->SetTag("MainScreenGames");
|
||||
gameBrowsers_.clear();
|
||||
@@ -1604,7 +1608,9 @@ void UmdReplaceScreen::CreateViews() {
|
||||
auto mm = GetI18NCategory(I18NCat::MAINMENU);
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
|
||||
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0));
|
||||
const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
|
||||
|
||||
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, 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));
|
||||
|
||||
|
||||
+3
-2
@@ -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;
|
||||
|
||||
+24
-7
@@ -508,7 +508,7 @@ std::string_view CreditsScreen::GetTitle() const {
|
||||
return mm->T("About PPSSPP");
|
||||
}
|
||||
|
||||
void CreditsScreen::CreateDialogViews(UI::ViewGroup *root) {
|
||||
void CreditsScreen::CreateDialogViews(UI::ViewGroup *parent) {
|
||||
using namespace UI;
|
||||
|
||||
ignoreBottomInset_ = false;
|
||||
@@ -528,18 +528,35 @@ void CreditsScreen::CreateDialogViews(UI::ViewGroup *root) {
|
||||
root_->Add(new ImageView(ImageID("I_ICON"), "", IS_DEFAULT, new AnchorLayoutParams(WRAP_CONTENT, WRAP_CONTENT, 10, 10, NONE, NONE, false)))->SetScale(1.5f);
|
||||
}*/
|
||||
|
||||
root->Add(new CreditsScroller(new LinearLayoutParams(1.0f)));
|
||||
LinearLayout *left;
|
||||
LinearLayout *right;
|
||||
if (portrait) {
|
||||
parent->Add(new CreditsScroller(new LinearLayoutParams(1.0f)));
|
||||
|
||||
LinearLayout *columns = root->Add(new LinearLayout(ORIENT_HORIZONTAL));
|
||||
LinearLayout *columns = parent->Add(new LinearLayout(ORIENT_HORIZONTAL));
|
||||
|
||||
LinearLayout *left = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, WRAP_CONTENT, Margins(10))));
|
||||
LinearLayout *middle = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
|
||||
LinearLayout *right = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, WRAP_CONTENT, Margins(10))));
|
||||
left = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, WRAP_CONTENT, Margins(10))));
|
||||
columns->Add(new Spacer(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
|
||||
right = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, WRAP_CONTENT, Margins(10))));
|
||||
} else {
|
||||
LinearLayout *columns = parent->Add(new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(1.0f)));
|
||||
|
||||
left = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, FILL_PARENT, Margins(10))));
|
||||
left->Add(new Spacer(0.0f, new LinearLayoutParams(1.0f)));
|
||||
columns->Add(new CreditsScroller(new LinearLayoutParams(WRAP_CONTENT, FILL_PARENT, 1.0f)));
|
||||
right = columns->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250.0f, FILL_PARENT, Margins(10))));
|
||||
right->Add(new Spacer(0.0f, new LinearLayoutParams(1.0f)));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
+8
-4
@@ -42,7 +42,7 @@ CopyableText::CopyableText(ImageID imageID, std::string_view text, UI::LinearLay
|
||||
});
|
||||
}
|
||||
|
||||
TopBar::TopBar(const UIContext &ctx, bool usePortraitLayout, std::string_view title, UI::LayoutParams *layoutParams) : UI::LinearLayout(ORIENT_HORIZONTAL, layoutParams) {
|
||||
TopBar::TopBar(const UIContext &ctx, TopBarFlags flags, std::string_view title, UI::LayoutParams *layoutParams) : UI::LinearLayout(ORIENT_HORIZONTAL, layoutParams), flags_(flags) {
|
||||
using namespace UI;
|
||||
SetSpacing(10.0f);
|
||||
if (!layoutParams) {
|
||||
@@ -55,9 +55,6 @@ TopBar::TopBar(const UIContext &ctx, bool usePortraitLayout, std::string_view ti
|
||||
backButton_ ->OnClick.Add([](UI::EventParams &e) {
|
||||
e.bubbleResult = DR_BACK;
|
||||
});
|
||||
if (!usePortraitLayout) {
|
||||
backButton_->SetText(dlg->T("Back"));
|
||||
}
|
||||
|
||||
if (!title.empty()) {
|
||||
TextView *titleView = Add(new TextView(title, ALIGN_VCENTER | FLAG_WRAP_TEXT, false, new LinearLayoutParams(1.0f, Gravity::G_VCENTER)));
|
||||
@@ -66,6 +63,13 @@ TopBar::TopBar(const UIContext &ctx, bool usePortraitLayout, std::string_view ti
|
||||
// If using HCENTER, to balance the centering, add a spacer on the right.
|
||||
// Add(new Spacer(50.0f));
|
||||
}
|
||||
|
||||
if (flags & TopBarFlags::ContextMenuButton) {
|
||||
Choice *menuButton = Add(new Choice(ImageID("I_THREE_DOTS"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)));
|
||||
menuButton->OnClick.Add([this](UI::EventParams &e) {
|
||||
this->OnContextMenuClick.Trigger(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SettingInfoMessage::SettingInfoMessage(int align, float cutOffY, UI::AnchorLayoutParams *lp)
|
||||
|
||||
+12
-1
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Common.h"
|
||||
#include "Common/UI/View.h"
|
||||
#include "UI/ViewGroup.h"
|
||||
|
||||
@@ -15,14 +16,24 @@ public:
|
||||
CopyableText(ImageID imageID, std::string_view text, UI::LinearLayoutParams *layoutParams = nullptr);
|
||||
};
|
||||
|
||||
enum class TopBarFlags {
|
||||
Default = 0,
|
||||
Portrait = 1,
|
||||
ContextMenuButton = 2,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(TopBarFlags);
|
||||
|
||||
class TopBar : public UI::LinearLayout {
|
||||
public:
|
||||
// The context is needed to get the theme for the background.
|
||||
TopBar(const UIContext &ctx, bool usePortraitLayout, std::string_view title, UI::LayoutParams *layoutParams = nullptr);
|
||||
TopBar(const UIContext &ctx, TopBarFlags flags, std::string_view title, UI::LayoutParams *layoutParams = nullptr);
|
||||
UI::View *GetBackButton() const { return backButton_; }
|
||||
|
||||
UI::Event OnContextMenuClick;
|
||||
|
||||
private:
|
||||
UI::Choice *backButton_ = nullptr;
|
||||
TopBarFlags flags_ = TopBarFlags::Default;
|
||||
};
|
||||
|
||||
class SettingInfoMessage : public UI::LinearLayout {
|
||||
|
||||
@@ -592,11 +592,11 @@ 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;
|
||||
|
||||
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
TabHolder *leftColumn = new TabHolder(ORIENT_HORIZONTAL, 64, TabHolderFlags::Default, nullptr, nullptr, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
|
||||
tabHolder_ = leftColumn;
|
||||
tabHolder_->SetTag("RemoteGames");
|
||||
gameBrowsers_.clear();
|
||||
@@ -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_));
|
||||
|
||||
|
||||
@@ -431,26 +431,26 @@ void SavedataBrowser::Update() {
|
||||
int n = gameList_->GetNumSubviews();
|
||||
bool matches = searchFilter_.empty();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
SavedataButton *v = static_cast<SavedataButton *>(gameList_->GetViewByIndex(i));
|
||||
|
||||
View *view = gameList_->GetViewByIndex(i);
|
||||
// Note: might be resetting to empty string. Can do that right away.
|
||||
if (searchFilter_.empty()) {
|
||||
v->SetVisibility(UI::V_VISIBLE);
|
||||
view->SetVisibility(UI::V_VISIBLE);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!v->UpdateText()) {
|
||||
SavedataButton *savedataButton = static_cast<SavedataButton *>(view);
|
||||
if (!savedataButton->UpdateText()) {
|
||||
// We'll need to wait until the text is loaded.
|
||||
searchPending_ = true;
|
||||
v->SetVisibility(UI::V_GONE);
|
||||
view->SetVisibility(UI::V_GONE);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string label = v->DescribeText();
|
||||
std::string label = view->DescribeText();
|
||||
std::transform(label.begin(), label.end(), label.begin(), tolower);
|
||||
bool match = label.find(searchFilter_) != label.npos;
|
||||
matches = matches || match;
|
||||
v->SetVisibility(match ? UI::V_VISIBLE : UI::V_GONE);
|
||||
view->SetVisibility(match ? UI::V_VISIBLE : UI::V_GONE);
|
||||
}
|
||||
|
||||
if (searchingView_) {
|
||||
@@ -671,7 +671,7 @@ void SavedataScreen::CreateTabs() {
|
||||
});
|
||||
}
|
||||
|
||||
void SavedataScreen::CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) {
|
||||
void SavedataScreen::CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) {
|
||||
using namespace UI;
|
||||
if (System_GetPropertyBool(SYSPROP_HAS_TEXT_INPUT_DIALOG)) {
|
||||
auto di = GetI18NCategory(I18NCat::DIALOG);
|
||||
|
||||
+2
-2
@@ -80,10 +80,10 @@ public:
|
||||
|
||||
protected:
|
||||
void CreateTabs() override;
|
||||
void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) override;
|
||||
void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) override;
|
||||
|
||||
bool ShowSearchControls() const override { return false; }
|
||||
|
||||
|
||||
private:
|
||||
void OnSavedataButtonClick(UI::EventParams &e);
|
||||
void OnSearch(UI::EventParams &e);
|
||||
|
||||
+40
-12
@@ -1,6 +1,7 @@
|
||||
#include "Common/UI/ScrollView.h"
|
||||
#include "Common/Data/Text/I18n.h"
|
||||
#include "UI/SimpleDialogScreen.h"
|
||||
#include "Common/UI/PopupScreens.h"
|
||||
#include "UI/MiscViews.h"
|
||||
|
||||
void UISimpleBaseDialogScreen::CreateViews() {
|
||||
@@ -12,7 +13,7 @@ void UISimpleBaseDialogScreen::CreateViews() {
|
||||
const bool portrait = GetDeviceOrientation() == DeviceOrientation::Portrait;
|
||||
|
||||
root_ = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
root_->Add(new TopBar(*screenManager()->getUIContext(), portrait, GetTitle()));
|
||||
root_->Add(new TopBar(*screenManager()->getUIContext(), portrait ? TopBarFlags::Portrait : TopBarFlags::Default, GetTitle()));
|
||||
|
||||
if (canScroll) {
|
||||
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f));
|
||||
@@ -37,28 +38,55 @@ void UITwoPaneBaseDialogScreen::CreateViews() {
|
||||
// Portrait layout is just a vertical stack.
|
||||
ignoreBottomInset_ = true;
|
||||
LinearLayout *root = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
root->Add(new TopBar(*screenManager()->getUIContext(), portrait, GetTitle()));
|
||||
|
||||
TopBarFlags topBarFlags = TopBarFlags::Portrait;
|
||||
if (flags_ & TwoPaneFlags::SettingsInContextMenu) {
|
||||
topBarFlags |= TopBarFlags::ContextMenuButton;
|
||||
}
|
||||
TopBar *topBar = root->Add(new TopBar(*screenManager()->getUIContext(), topBarFlags, GetTitle()));
|
||||
root->SetSpacing(0);
|
||||
CreateContentViews(root);
|
||||
|
||||
ScrollView *settingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT, 1.0f));
|
||||
LinearLayout *settingsPane = new LinearLayout(ORIENT_VERTICAL);
|
||||
settingsScroll->Add(settingsPane);
|
||||
CreateSettingsViews(settingsPane);
|
||||
root->Add(settingsScroll);
|
||||
|
||||
if (flags_ & TwoPaneFlags::SettingsInContextMenu) {
|
||||
topBar->OnContextMenuClick.Add([this](UI::EventParams &e) {
|
||||
this->screenManager()->push(new PopupCallbackScreen([this](UI::ViewGroup *parent) {
|
||||
CreateSettingsViews(parent);
|
||||
}, nullptr));
|
||||
});
|
||||
} else {
|
||||
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);
|
||||
root->Add(settingsScroll);
|
||||
}
|
||||
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 ? TopBarFlags::Portrait : TopBarFlags::Default, 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)));
|
||||
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<UIScreen>(this, &UIScreen::OnBack);
|
||||
root->Add(settingsPane);
|
||||
CreateContentViews(root);
|
||||
settingsScroll->Add(settingsPane);
|
||||
|
||||
if (flags_ & TwoPaneFlags::SettingsToTheRight) {
|
||||
CreateContentViews(columns);
|
||||
columns->Add(settingsScroll);
|
||||
} else {
|
||||
columns->Add(settingsScroll);
|
||||
CreateContentViews(columns);
|
||||
}
|
||||
|
||||
root_ = root;
|
||||
}
|
||||
|
||||
+12
-3
@@ -24,22 +24,31 @@ private:
|
||||
void CreateViews() override;
|
||||
};
|
||||
|
||||
enum class TwoPaneFlags {
|
||||
Default = 0,
|
||||
SettingsToTheRight = 1,
|
||||
SettingsInContextMenu = 2,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(TwoPaneFlags);
|
||||
|
||||
// A two-pane version of the above, where settings are meant to go in the settings pane,
|
||||
// and contents in the content pane. Will generate nice layouts for portrait and landscape.
|
||||
// but with a consistent portrait-compatible back button and title.
|
||||
// The settings pane is scrollable while the other is not.
|
||||
class UITwoPaneBaseDialogScreen : public UIBaseDialogScreen {
|
||||
public:
|
||||
UITwoPaneBaseDialogScreen(const Path &gamePath = Path()) : UIBaseDialogScreen(gamePath) {
|
||||
UITwoPaneBaseDialogScreen(const Path &gamePath, TwoPaneFlags flags) : UIBaseDialogScreen(gamePath), flags_(flags) {
|
||||
// We need to check CanScroll before we know whether to ignore
|
||||
// bottom inset. Can't do that here, we do it in CreateViews
|
||||
}
|
||||
|
||||
// Override this, don't override CreateViews. And don't touch root_ directly.
|
||||
virtual void CreateSettingsViews(UI::LinearLayout *parent) = 0;
|
||||
virtual void CreateContentViews(UI::LinearLayout *parent) = 0;
|
||||
virtual void CreateSettingsViews(UI::ViewGroup *parent) = 0;
|
||||
virtual void CreateContentViews(UI::ViewGroup *parent) = 0;
|
||||
virtual std::string_view GetTitle() const { return ""; }
|
||||
virtual float SettingsWidth() const { return 350.0f; }
|
||||
|
||||
private:
|
||||
void CreateViews() override;
|
||||
TwoPaneFlags flags_ = TwoPaneFlags::Default;
|
||||
};
|
||||
@@ -58,16 +58,26 @@ void UITabbedBaseDialogScreen::CreateViews() {
|
||||
if (flags_ & TabDialogFlags::HorizontalOnlyIcons) {
|
||||
tabHolderFlags |= TabHolderFlags::HorizontalOnlyIcons;
|
||||
}
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 200, tabHolderFlags, filterNotice_, new LinearLayoutParams(1.0f));
|
||||
std::function<void()> contextMenu;
|
||||
if (flags_ & TabDialogFlags::ContextMenuInPortrait) {
|
||||
contextMenu = [this]() {
|
||||
this->screenManager()->push(new PopupCallbackScreen([this](UI::ViewGroup *parent) {
|
||||
CreateExtraButtons(parent, 0);
|
||||
}, nullptr));
|
||||
};
|
||||
}
|
||||
tabHolder_ = new TabHolder(ORIENT_HORIZONTAL, 200, tabHolderFlags, filterNotice_, contextMenu, new LinearLayoutParams(1.0f));
|
||||
verticalLayout->Add(tabHolder_);
|
||||
CreateExtraButtons(verticalLayout, 0);
|
||||
if (!(flags_ & TabDialogFlags::ContextMenuInPortrait)) {
|
||||
CreateExtraButtons(verticalLayout, 0);
|
||||
}
|
||||
root_->Add(verticalLayout);
|
||||
} else {
|
||||
TabHolderFlags tabHolderFlags = TabHolderFlags::Default;
|
||||
if (flags_ & TabDialogFlags::VerticalShowIcons) {
|
||||
tabHolderFlags |= TabHolderFlags::VerticalShowIcons;
|
||||
}
|
||||
tabHolder_ = new TabHolder(ORIENT_VERTICAL, 300, tabHolderFlags, filterNotice_, new AnchorLayoutParams(10, 0, 10, 0));
|
||||
tabHolder_ = new TabHolder(ORIENT_VERTICAL, 300, tabHolderFlags, filterNotice_, nullptr, new AnchorLayoutParams(10, 0, 10, 0));
|
||||
CreateExtraButtons(tabHolder_->Container(), 10);
|
||||
tabHolder_->AddBack(this);
|
||||
root_->Add(tabHolder_);
|
||||
|
||||
@@ -20,6 +20,7 @@ enum class TabDialogFlags {
|
||||
HorizontalOnlyIcons = 1,
|
||||
VerticalShowIcons = 2,
|
||||
AddTitles = 4,
|
||||
ContextMenuInPortrait = 8,
|
||||
};
|
||||
ENUM_CLASS_BITOPS(TabDialogFlags);
|
||||
|
||||
@@ -45,9 +46,10 @@ protected:
|
||||
// Load data and define your tabs here.
|
||||
virtual void PreCreateViews() {}
|
||||
virtual void CreateTabs() = 0;
|
||||
virtual void CreateExtraButtons(UI::LinearLayout *verticalLayout, int margins) {}
|
||||
virtual void CreateExtraButtons(UI::ViewGroup *verticalLayout, int margins) {}
|
||||
virtual bool ShowSearchControls() const { return true; }
|
||||
virtual void EnsureTabs();
|
||||
virtual bool UsePopupMenuInPortrait() const { return false; }
|
||||
virtual bool ForceHorizontalTabs() const { return false; }
|
||||
|
||||
int GetCurrentTab() const;
|
||||
|
||||
@@ -83,12 +83,12 @@ std::string_view TiltAnalogSettingsScreen::GetTitle() const {
|
||||
return co->T("Tilt control setup");
|
||||
}
|
||||
|
||||
void TiltAnalogSettingsScreen::CreateContentViews(UI::LinearLayout *parent) {
|
||||
void TiltAnalogSettingsScreen::CreateContentViews(UI::ViewGroup *parent) {
|
||||
using namespace UI;
|
||||
CreateCalibrationView(parent, new LinearLayoutParams(300.0f, 300.0f, 1.0f, Gravity::G_CENTER));
|
||||
}
|
||||
|
||||
void TiltAnalogSettingsScreen::CreateSettingsViews(UI::LinearLayout *settings) {
|
||||
void TiltAnalogSettingsScreen::CreateSettingsViews(UI::ViewGroup *settings) {
|
||||
using namespace UI;
|
||||
auto co = GetI18NCategory(I18NCat::CONTROLS);
|
||||
|
||||
|
||||
@@ -26,10 +26,10 @@ class GamepadView;
|
||||
|
||||
class TiltAnalogSettingsScreen : public UITwoPaneBaseDialogScreen {
|
||||
public:
|
||||
TiltAnalogSettingsScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath) {}
|
||||
TiltAnalogSettingsScreen(const Path &gamePath) : UITwoPaneBaseDialogScreen(gamePath, TwoPaneFlags::Default) {}
|
||||
|
||||
void CreateSettingsViews(UI::LinearLayout *parent) override;
|
||||
void CreateContentViews(UI::LinearLayout *parent) override;
|
||||
void CreateSettingsViews(UI::ViewGroup *parent) override;
|
||||
void CreateContentViews(UI::ViewGroup*parent) override;
|
||||
std::string_view GetTitle() const override;
|
||||
|
||||
void update() override;
|
||||
|
||||
@@ -155,7 +155,7 @@ void RightAnalogMappingScreen::CreateViews() {
|
||||
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
|
||||
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<UIScreen>(this, &UIScreen::OnBack);
|
||||
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, TabHolderFlags::Default, nullptr, new AnchorLayoutParams(10, 0, 10, 0, Centering::None));
|
||||
TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, TabHolderFlags::Default, nullptr, nullptr, new AnchorLayoutParams(10, 0, 10, 0, Centering::None));
|
||||
root_->Add(tabHolder);
|
||||
ScrollView *rightPanel = new ScrollView(ORIENT_VERTICAL);
|
||||
tabHolder->AddTab(co->T("Binds"), ImageID::invalid(), rightPanel);
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user