diff --git a/Common/UI/View.cpp b/Common/UI/View.cpp index fdbebeed6d..fe54afe42a 100644 --- a/Common/UI/View.cpp +++ b/Common/UI/View.cpp @@ -344,7 +344,6 @@ bool Clickable::Key(const KeyInput &key) { bool StickyChoice::Touch(const TouchInput &touch) { bool contains = bounds_.Contains(touch.x, touch.y); - dragging_ = false; if (!IsEnabled()) { down_ = false; return contains; @@ -352,12 +351,18 @@ bool StickyChoice::Touch(const TouchInput &touch) { if (touch.flags & TouchInputFlags::DOWN) { if (contains) { + dragging_ = true; + } + } + if (touch.flags & TouchInputFlags::UP) { + if (dragging_ && contains && !(touch.flags & TouchInputFlags::CANCEL)) { if (IsFocusMovementEnabled()) SetFocusedView(this); - down_ = true; ClickInternal(); + dragging_ = false; return true; } + dragging_ = false; } return false; } diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 81beacd036..597ea5d4e3 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -1167,3 +1167,27 @@ bool CheatsInEffect() { return false; return cheatEngine->HasCheats(); } + +bool DetectCheatTitle(std::string_view name, std::string_view *title) { + // Use Saramagrean title convention (the most common cheats.db file). + const size_t firstOffset = name.find("_[>>>"); + const size_t secondOffset = name.rfind("<<<]_"); + const bool isTitle = firstOffset != std::string::npos && secondOffset != std::string::npos && firstOffset < secondOffset; + if (!isTitle) { + return false; + } + // Extract the title substring. + *title = std::string_view(name.data() + firstOffset + 5, secondOffset - (firstOffset + 5)); + return true; +} + +bool DetectCheatPostComment(std::string_view name, std::string_view *comment) { + const size_t firstOffset = name.find("\xE2\x86\x91[#"); // Up arrow and [# + const size_t secondOffset = name.rfind(']'); + const bool isPostComment = firstOffset != std::string::npos && secondOffset != std::string::npos && secondOffset > firstOffset; + if (!isPostComment) { + return false; + } + *comment = std::string_view(name.data() + firstOffset + 5, secondOffset - (firstOffset + 5)); + return true; +} diff --git a/Core/CwCheat.h b/Core/CwCheat.h index e697177f17..3f8c22a655 100644 --- a/Core/CwCheat.h +++ b/Core/CwCheat.h @@ -25,6 +25,9 @@ struct CheatLine { uint32_t part2; }; +bool DetectCheatTitle(std::string_view name, std::string_view *title); +bool DetectCheatPostComment(std::string_view name, std::string_view *title); + struct CheatCode { std::string name; std::vector lines; @@ -34,6 +37,13 @@ struct CheatFileInfo { int lineNum; std::string name; bool enabled; + + bool IsTitle(std::string_view *title) const { + return DetectCheatTitle(name, title); + } + bool IsPostComment(std::string_view *comment) const { + return DetectCheatPostComment(name, comment); + } }; struct CheatOperation; diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index d37821aea5..24ab8b184b 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -33,6 +33,7 @@ #include "UI/GameInfoCache.h" #include "UI/CwCheatScreen.h" +#include "UI/MiscViews.h" static const int FILE_CHECK_FRAME_INTERVAL = 53; @@ -124,12 +125,30 @@ void CwCheatScreen::CreateContentViews(UI::ViewGroup *parent) { rightScroll->RememberPosition(&g_Config.fCwCheatScrollPosition); LinearLayout *rightColumn = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(200, FILL_PARENT)); + rightColumn->SetSpacing(0.0f); rightScroll->Add(rightColumn); rightColumn->Add(new ItemHeader(cw->T("Cheats"))); + bool prevIsTitle = false; for (size_t i = 0; i < fileInfo_.size(); ++i) { - rightColumn->Add(new CheckBox(&fileInfo_[i].enabled, fileInfo_[i].name))->OnClick.Add([=](UI::EventParams &) { - OnCheckBox((int)i); - }); + std::string_view text; + if (fileInfo_[i].IsTitle(&text)) { + // Title. + TextView *titleView = rightColumn->Add(new TextView(text, new LinearLayoutParams(WRAP_CONTENT, WRAP_CONTENT, UI::Margins(8, 8, 8, 0)))); + titleView->SetTextSize(UI::TextSize::Big); + prevIsTitle = true; + } else if (fileInfo_[i].IsPostComment(&text)) { + rightColumn->Add(new SettingHint(text, nullptr)); + prevIsTitle = false; + } else { + // Regular cheat code. + if (!prevIsTitle) { + rightColumn->Add(new Spacer(8.0f)); + } + rightColumn->Add(new CheckBox(&fileInfo_[i].enabled, fileInfo_[i].name))->OnClick.Add([=](UI::EventParams &) { + OnCheckBox((int)i); + }); + prevIsTitle = false; + } } }