diff --git a/Common/Log.cpp b/Common/Log.cpp index f95bb7549c..2937c0f308 100644 --- a/Common/Log.cpp +++ b/Common/Log.cpp @@ -174,3 +174,16 @@ bool HitAnyAsserts() { void ResetHitAnyAsserts() { hitAnyAsserts = false; } + +void ShowMessageDialog(const char *title, const char *message, bool isError) { +#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) + const int msgBoxStyle = MB_ICONINFORMATION | MB_OK; + std::string text = message; + const char *caption = title; + std::wstring wcaption = ConvertUTF8ToWString(std::string(caption)); + MessageBox(g_dialogParent, ConvertUTF8ToWString(text).c_str(), wcaption.c_str(), msgBoxStyle); +#else + // Just printf. + fprintf(stderr, "%s: %s\n", title, message); +#endif +} diff --git a/Common/Log.h b/Common/Log.h index 198c765b6a..26abef28f6 100644 --- a/Common/Log.h +++ b/Common/Log.h @@ -154,6 +154,7 @@ void SetAssertCancelCallback(AssertNoCallbackFunc callback, void *userdata); void SetCleanExitOnAssert(); void BreakIntoPSPDebugger(const char *reason = "(userbreak)"); void SetAssertDialogParent(void *handle); // HWND on windows. Ignored on other platforms. +void ShowMessageDialog(const char *title, const char *message, bool isError = false); #if defined(__ANDROID__) // Tricky macro to get the basename, that also works if *built* on Win32. diff --git a/Common/UI/PopupScreens.cpp b/Common/UI/PopupScreens.cpp index 5f02810e5d..e7a301ebb8 100644 --- a/Common/UI/PopupScreens.cpp +++ b/Common/UI/PopupScreens.cpp @@ -193,7 +193,7 @@ void PopupScreen::CreateViews() { box_->SetRadiusFactor(1.44f); // Since we scale a bit, make the dropshadow bleed past the edges. box_->SetDropShadowExpand(std::max(g_display.dp_xres, g_display.dp_yres)); - box_->SetSpacing(0.0f); + box_->SetSpacing(12.0f); box_->SetPadding(Margins(12)); if (!title_.empty()) { diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index d9901f28f2..0f7b8140a6 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -641,6 +641,59 @@ std::string ItemHeader::DescribeText() const { return ApplySafeSubstitutions(u->T("%1 heading"), text_); } +PopupHeader::PopupHeader(std::string_view text, LayoutParams *layoutParams) + : Item(layoutParams), text_(text) { + layoutParams_->width = FILL_PARENT; + layoutParams_->height = 44; +} + +void PopupHeader::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const { + float availWidth = layoutParams_->width; + if (availWidth < 0) { + // If there's no size, let's grow as big as we want. + availWidth = horiz.size == 0 ? MAX_ITEM_SIZE : horiz.size; + } + ApplyBoundBySpec(availWidth, horiz); + dc.MeasureTextRect(dc.GetTheme().uiFontBig, 1.0f, 1.0f, text_, availWidth, &w, &h, ALIGN_LEFT | ALIGN_VCENTER); +} + +void PopupHeader::Draw(UIContext &dc) { + const float availableWidth = bounds_.w; + + float tw, th; + dc.SetFontStyle(dc.GetTheme().uiFontBig); + dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text_, &tw, &th, 0); + + float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; + + float tx = 0; + if (availableWidth < tw) { + float overageRatio = 1.5f * availableWidth * 1.0f / tw; + tx -= (1.0f + sin(time_now_d() * overageRatio)) * sineWidth; + Bounds tb = bounds_; + tb.x = bounds_.x; + tb.w = bounds_.w; + dc.PushScissor(tb); + } + + // Header background + dc.FillRoundRect(dc.GetTheme().popupTitleStyle.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); + // Header title text + dc.DrawText(text_, bounds_.x + tx, bounds_.centerY(), dc.GetTheme().popupTitleStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); + + // Underline. Skipping it with the new style. + // dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.GetTheme().popupTitleStyle.fgColor); + + if (availableWidth < tw) { + dc.PopScissor(); + } +} + +std::string PopupHeader::DescribeText() const { + auto u = GetI18NCategory(I18NCat::UI_ELEMENTS); + return ApplySafeSubstitutions(u->T("%1 heading"), text_); +} + CollapsibleHeader::CollapsibleHeader(bool *toggle, std::string_view text, LayoutParams *layoutParams) : CheckBox(toggle, text, "", layoutParams) { layoutParams_->width = FILL_PARENT; @@ -713,43 +766,6 @@ void BorderView::GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec hor h = (borderFlags_ & BORDER_HORIZ) != 0 ? bounds.h : 0; } -void PopupHeader::Draw(UIContext &dc) { - const float availableWidth = bounds_.w; - - float tw, th; - dc.SetFontStyle(dc.GetTheme().uiFontBig); - dc.MeasureText(dc.GetFontStyle(), 1.0f, 1.0f, text_, &tw, &th, 0); - - float sineWidth = std::max(0.0f, (tw - availableWidth)) / 2.0f; - - float tx = 0; - if (availableWidth < tw) { - float overageRatio = 1.5f * availableWidth * 1.0f / tw; - tx -= (1.0f + sin(time_now_d() * overageRatio)) * sineWidth; - Bounds tb = bounds_; - tb.x = bounds_.x; - tb.w = bounds_.w; - dc.PushScissor(tb); - } - - // Header background - dc.FillRoundRect(dc.GetTheme().popupTitleStyle.background, bounds_, dc.GetTheme().choiceRoundRadius, roundCorners_); - // Header title text - dc.DrawText(text_, bounds_.x + tx, bounds_.centerY(), dc.GetTheme().popupTitleStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER); - - // Underline. Skipping it with the new style. - // dc.Draw()->DrawImageCenterTexel(dc.GetTheme().whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.GetTheme().popupTitleStyle.fgColor); - - if (availableWidth < tw) { - dc.PopScissor(); - } -} - -std::string PopupHeader::DescribeText() const { - auto u = GetI18NCategory(I18NCat::UI_ELEMENTS); - return ApplySafeSubstitutions(u->T("%1 heading"), text_); -} - void CheckBox::Toggle() { if (toggle_) { *toggle_ = !(*toggle_); diff --git a/Common/UI/View.h b/Common/UI/View.h index dbddad7079..70fc2508d9 100644 --- a/Common/UI/View.h +++ b/Common/UI/View.h @@ -939,12 +939,9 @@ private: class PopupHeader : public Item { public: - PopupHeader(std::string_view text, LayoutParams *layoutParams = 0) - : Item(layoutParams), text_(text) { - layoutParams_->width = FILL_PARENT; - layoutParams_->height = 64; - } + PopupHeader(std::string_view text, LayoutParams *layoutParams = 0); void Draw(UIContext &dc) override; + void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override; std::string DescribeText() const override; private: diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index be16799532..4d6e71685b 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -145,7 +145,7 @@ static void LaunchFile(ScreenManager *screenManager, Screen *currentScreen, cons // Show the savedata popup, why not? waitForParamSFO(); std::string title = SanitizeString(info->GetTitle(), StringRestriction::NoLineBreaksOrSpecials, 0, 200); - screenManager->push(new SavedataPopupScreen(Path(), path, title)); + screenManager->push(new SavedataPopupScreen(Path(), path, true, title)); return; } case IdentifiedFileType::PSP_PBP: diff --git a/UI/MemStickScreen.cpp b/UI/MemStickScreen.cpp index a9369b003b..c3f2c2bdfa 100644 --- a/UI/MemStickScreen.cpp +++ b/UI/MemStickScreen.cpp @@ -192,7 +192,7 @@ void MemStickScreen::CreateViews() { ViewGroup *leftColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); subColumns->Add(leftColumn); - ViewGroup *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250, FILL_PARENT, actionMenuMargins)); + LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(250, FILL_PARENT, actionMenuMargins)); subColumns->Add(rightColumnItems); // For legacy Android systems, so you can switch back to the old ways if you move to SD or something. @@ -276,6 +276,8 @@ void MemStickScreen::CreateViews() { rightColumnItems->Add(new UI::Choice(ms->T("WhatsThis", "What's this?"), ImageID("I_LINK_OUT_QUESTION")))->OnClick.Handle(this, &MemStickScreen::OnHelp); } + rightColumnItems->ApplyRoundCorners(); + INFO_LOG(Log::System, "MemStickScreen: initialSetup=%d", (int)initialSetup_); } diff --git a/UI/MiscViews.h b/UI/MiscViews.h index e5b0285482..e0b20fa2b3 100644 --- a/UI/MiscViews.h +++ b/UI/MiscViews.h @@ -167,3 +167,15 @@ private: int width_ = 0; int height_ = 0; }; + +class HintView : public UI::TextView { +public: + HintView(std::string_view str, UI::LayoutParams *layoutParams = nullptr) : TextView(str, FLAG_WRAP_TEXT, false, layoutParams) { + using namespace UI; + layoutParams_->width = FILL_PARENT; + layoutParams_->height = WRAP_CONTENT; + SetTextSize(TextSize::Small); + SetPadding(Margins(12)); + SetFrame(true); + } +}; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index ad91aa7a6b..ba58c24400 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -904,6 +904,9 @@ void NativeInit(int argc, const char *argv[], const CommandLineOptions &cmdLineO } else if (equals(cmdLineOptions.startScreen.value(), "firmware")) { g_screenManager->switchScreen(new MainScreen()); g_screenManager->push(new FirmwareScreen(Path())); + } else if (equals(cmdLineOptions.startScreen.value(), "memsticksetup")) { + g_screenManager->switchScreen(new MainScreen()); + g_screenManager->push(new MemStickScreen(true)); } else { WARN_LOG(Log::System, "Unknown start screen '%s'", cmdLineOptions.startScreen.value().c_str()); g_screenManager->switchScreen(new LogoScreen(AfterLogoScreen::DEFAULT)); diff --git a/UI/PauseScreen.cpp b/UI/PauseScreen.cpp index 5208b93a8d..27cd07e1e2 100644 --- a/UI/PauseScreen.cpp +++ b/UI/PauseScreen.cpp @@ -497,14 +497,14 @@ void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems, LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(Margins(10, 0, 0, 10))); if (g_Config.bEnableStateUndo && !Achievements::HardcoreModeActive() && NetworkAllowSaveState()) { - UI::Choice *saveUndoButton = buttonRow->Add(new Choice(pa->T("Undo last save"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT))); + UI::Button *saveUndoButton = buttonRow->Add(new Button(pa->T("Undo last save"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT))); saveUndoButton->SetEnabled(SaveState::HasUndoLastSave(saveStatePrefix_)); saveUndoButton->OnClick.Add([this](UI::EventParams &e) { SaveState::UndoLastSave(saveStatePrefix_); RecreateViews(); }); - UI::Choice *loadUndoButton = buttonRow->Add(new Choice(pa->T("Undo last load"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT))); + UI::Button *loadUndoButton = buttonRow->Add(new Button(pa->T("Undo last load"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT))); loadUndoButton->SetEnabled(SaveState::HasUndoLoad(saveStatePrefix_)); loadUndoButton->OnClick.Add([this](UI::EventParams &e) { CloseAfterStateLoad([this](SaveState::Callback callback) { @@ -514,7 +514,7 @@ void GamePauseScreen::CreateSavestateControls(UI::LinearLayout *leftColumnItems, } if (g_Config.iRewindSnapshotInterval > 0 && !Achievements::HardcoreModeActive() && NetworkAllowSaveState()) { - UI::Choice *rewindButton = buttonRow->Add(new Choice(pa->T("Rewind"), ImageID("I_REWIND"), new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT))); + UI::Button *rewindButton = buttonRow->Add(new Button(pa->T("Rewind"), ImageID("I_REWIND"), new LinearLayoutParams(WRAP_CONTENT, ITEM_HEIGHT))); rewindButton->SetEnabled(SaveState::CanRewind()); rewindButton->OnClick.Add([this](UI::EventParams &e) { CloseAfterStateLoad([](SaveState::Callback callback) { diff --git a/UI/SavedataScreen.cpp b/UI/SavedataScreen.cpp index 0e0977e8dc..645a6d4ded 100644 --- a/UI/SavedataScreen.cpp +++ b/UI/SavedataScreen.cpp @@ -132,7 +132,7 @@ SavedataView::SavedataView(UIContext &dc, GameInfo *ginfo, IdentifiedFileType ty } } -SavedataPopupScreen::SavedataPopupScreen(Path gamePath, Path savePath, std::string_view title) : PopupScreen(StripSpaces(title)), savePath_(savePath), gamePath_(gamePath) {} +SavedataPopupScreen::SavedataPopupScreen(Path gamePath, Path savePath, bool showHint, std::string_view title) : PopupScreen(StripSpaces(title)), savePath_(savePath), gamePath_(gamePath), showHint_(showHint) {} void SavedataPopupScreen::update() { PopupScreen::update(); @@ -158,17 +158,23 @@ void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { ScrollView *contentScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f, UI::Margins(0, 3))); parent->Add(contentScroll); + LinearLayout *content = contentScroll->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + + if (showHint_) { + auto g = GetI18NCategory(I18NCat::GAME); + content->Add(new HintView(g->T("SaveData"))); + } + // TODO: If the game info wasn't already loaded, we'll get a bogus fileType here. - savedataView_ = contentScroll->Add(new SavedataView(dc, ginfo.get(), ginfo->fileType, true)); + savedataView_ = content->Add(new SavedataView(dc, ginfo.get(), ginfo->fileType, true)); auto di = GetI18NCategory(I18NCat::DIALOG); LinearLayout *buttonRow = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)); - buttonRow->SetSpacing(0); - Margins buttonMargins(5, 5, 5, 13); // not sure why we need more at the bottom to make it look right + buttonRow->SetSpacing(12); - buttonRow->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Handle(this, &UIScreen::OnBack); - buttonRow->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) { + buttonRow->Add(new Choice(di->T("Back"), ImageID("I_NAVIGATE_BACK"), new LinearLayoutParams(1.0f)))->OnClick.Handle(this, &UIScreen::OnBack); + buttonRow->Add(new Choice(di->T("Delete"), ImageID("I_TRASHCAN"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { auto di = GetI18NCategory(I18NCat::DIALOG); std::shared_ptr ginfo = g_gameInfoCache->GetInfo(nullptr, savePath_, GameInfoFlags::PARAM_SFO); @@ -183,10 +189,11 @@ void SavedataPopupScreen::CreatePopupContents(UI::ViewGroup *parent) { })); }); if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) { - buttonRow->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f, buttonMargins)))->OnClick.Add([this](UI::EventParams &e) { + buttonRow->Add(new Choice(di->T("Show in folder"), ImageID("I_FOLDER"), new LinearLayoutParams(1.0f)))->OnClick.Add([this](UI::EventParams &e) { System_ShowFileInFolder(savePath_); }); } + buttonRow->ApplyRoundCorners(); parent->Add(buttonRow); } @@ -690,7 +697,7 @@ void SavedataScreen::OnSavedataButtonClick(UI::EventParams &e) { // Sanitize the title. std::string title = SanitizeString(ginfo->GetTitle(), StringRestriction::NoLineBreaksOrSpecials, 0, 200); - SavedataPopupScreen *popupScreen = new SavedataPopupScreen(gamePath_, Path(e.s), title); + SavedataPopupScreen *popupScreen = new SavedataPopupScreen(gamePath_, Path(e.s), false, title); if (e.v) { popupScreen->SetPopupOrigin(e.v); } diff --git a/UI/SavedataScreen.h b/UI/SavedataScreen.h index 66fdce3463..c0040b0795 100644 --- a/UI/SavedataScreen.h +++ b/UI/SavedataScreen.h @@ -71,7 +71,7 @@ class SavedataView; class SavedataPopupScreen : public UI::PopupScreen { public: - SavedataPopupScreen(Path gamePath, Path savePath, std::string_view title); + SavedataPopupScreen(Path gamePath, Path savePath, bool showHint, std::string_view title); const char *tag() const override { return "SavedataPopup"; } void update() override; @@ -84,6 +84,7 @@ private: SavedataView *savedataView_ = nullptr; Path savePath_; Path gamePath_; + bool showHint_ = false; }; class SavedataScreen : public UITabbedBaseDialogScreen {