diff --git a/CMakeLists.txt b/CMakeLists.txt index 74f8ca2..b2f8206 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") set(CMAKE_CXX_VISIBILITY_PRESET hidden) -project(SearchHistory VERSION 1.0.4) +project(SearchHistory VERSION 1.1.0) add_library(${PROJECT_NAME} SHARED src/main.cpp diff --git a/README.md b/README.md index ac56dd4..7976679 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,7 @@ A mod that allows you to view your search history. - [hiimjustin000](https://gdbrowser.com/u/7466002) - Creator of the mod ## Gallery - - + # License This mod is licensed under the [MIT License](./LICENSE). \ No newline at end of file diff --git a/about.md b/about.md index 05db883..f921019 100644 --- a/about.md +++ b/about.md @@ -13,5 +13,4 @@ A mod that allows you to view your search history. - [hiimjustin000](user:7466002) - Creator of the mod ## Gallery -\ - \ No newline at end of file + \ No newline at end of file diff --git a/changelog.md b/changelog.md index 805979b..bd468bf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,9 @@ # Search History Changelog +## v1.1.0 (2024-09-08) +- Revamped the search history popup +- Expanded the number of search history entries per page from 5 to 10 +- Removed duplicate search history entries from the same day + ## v1.0.4 (2024-09-07) - Removed properties from saved.json that are default values - Changed the search history button texture and mod logo diff --git a/mod.json b/mod.json index a6f3ef8..ef02aeb 100644 --- a/mod.json +++ b/mod.json @@ -5,7 +5,7 @@ "win": "2.206", "mac": "2.206" }, - "version": "v1.0.4", + "version": "v1.1.0", "id": "hiimjustin000.search_history", "name": "Search History", "developer": "hiimjustin000", diff --git a/resources/image1.png b/resources/image1.png deleted file mode 100644 index 5acb14e..0000000 Binary files a/resources/image1.png and /dev/null differ diff --git a/resources/image2.png b/resources/image2.png deleted file mode 100644 index 997c0b6..0000000 Binary files a/resources/image2.png and /dev/null differ diff --git a/resources/search-history.png b/resources/search-history.png new file mode 100644 index 0000000..f2ac43f Binary files /dev/null and b/resources/search-history.png differ diff --git a/resources/square-bottom.png b/resources/square-bottom.png new file mode 100644 index 0000000..b41aad8 Binary files /dev/null and b/resources/square-bottom.png differ diff --git a/resources/square-middle.png b/resources/square-middle.png new file mode 100644 index 0000000..4166f2a Binary files /dev/null and b/resources/square-middle.png differ diff --git a/resources/square-top.png b/resources/square-top.png new file mode 100644 index 0000000..36b1e71 Binary files /dev/null and b/resources/square-top.png differ diff --git a/src/SearchHistory.cpp b/src/SearchHistory.cpp index 1bfbe99..ff1b78e 100644 --- a/src/SearchHistory.cpp +++ b/src/SearchHistory.cpp @@ -15,7 +15,7 @@ void SearchHistory::add(GJSearchObject* search, time_t time, int type) { lengths.push_back(std::stoi(str)); } - history.insert(history.begin(), { + SearchHistoryObject obj = { .time = time, .type = type, .query = search->m_searchQuery, @@ -36,8 +36,16 @@ void SearchHistory::add(GJSearchObject* search, time_t time, int type) { .demonFilter = (int)search->m_demonFilter, .noStar = search->m_noStarFilter, .star = search->m_starFilter + }; + + auto found = std::find_if(history.begin(), history.end(), [&obj](SearchHistoryObject const& o) { + return obj == o; }); + if (found != history.end()) history.erase(found); + + history.insert(history.begin(), obj); + Mod::get()->setSavedValue("search-history", history); } diff --git a/src/SearchHistory.hpp b/src/SearchHistory.hpp index ea6c306..4cd2e77 100644 --- a/src/SearchHistory.hpp +++ b/src/SearchHistory.hpp @@ -23,6 +23,28 @@ struct SearchHistoryObject { int demonFilter; bool noStar; bool star; + + bool operator==(SearchHistoryObject const& other) const { + return floor(time / 86400.0) == floor(other.time / 86400.0) && + type == other.type && + query == other.query && + difficulties == other.difficulties && + lengths == other.lengths && + uncompleted == other.uncompleted && + completed == other.completed && + featured == other.featured && + original == other.original && + twoPlayer == other.twoPlayer && + coins == other.coins && + epic == other.epic && + legendary == other.legendary && + mythic == other.mythic && + song == other.song && + (!song || (customSong == other.customSong && songID == other.songID)) && + demonFilter == other.demonFilter && + noStar == other.noStar && + star == other.star; + } }; class SearchHistory { @@ -55,7 +77,7 @@ struct matjson::Serialize<std::vector<SearchHistoryObject>> { } } - vec.push_back({ + SearchHistoryObject obj = { .time = (int64_t)PROPERTY_OR_DEFAULT(elem, "time", is_number, as_double, 0), .type = PROPERTY_OR_DEFAULT(elem, "type", is_number, as_int, 0), .query = PROPERTY_OR_DEFAULT(elem, "query", is_string, as_string, ""), @@ -76,7 +98,11 @@ struct matjson::Serialize<std::vector<SearchHistoryObject>> { .demonFilter = PROPERTY_OR_DEFAULT(elem, "demon-filter", is_number, as_int, 0), .noStar = PROPERTY_OR_DEFAULT(elem, "no-star", is_bool, as_bool, false), .star = PROPERTY_OR_DEFAULT(elem, "star", is_bool, as_bool, false) - }); + }; + + if (!std::any_of(vec.begin(), vec.end(), [&obj](SearchHistoryObject const& o) { + return obj == o; + })) vec.push_back(obj); } return vec; diff --git a/src/SearchHistoryNode.cpp b/src/SearchHistoryNode.cpp index 1fd910a..3637a63 100644 --- a/src/SearchHistoryNode.cpp +++ b/src/SearchHistoryNode.cpp @@ -1,8 +1,10 @@ #include "SearchHistoryNode.hpp" -SearchHistoryNode* SearchHistoryNode::create(SearchHistoryObject const& object, int index, SearchCallback search, RemoveCallback remove, bool h12, bool white) { +SearchHistoryNode* SearchHistoryNode::create( + SearchHistoryObject const& object, int index, int count, SearchCallback search, RemoveCallback remove, bool h12, bool white, bool dark +) { auto ret = new SearchHistoryNode(); - if (ret->init(object, index, search, remove, h12, white)) { + if (ret->init(object, index, count, search, remove, h12, white, dark)) { ret->autorelease(); return ret; } @@ -10,59 +12,65 @@ SearchHistoryNode* SearchHistoryNode::create(SearchHistoryObject const& object, return nullptr; } -bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, SearchCallback search, RemoveCallback remove, bool h12, bool white) { +bool SearchHistoryNode::init( + SearchHistoryObject const& object, int index, int count, SearchCallback search, RemoveCallback remove, bool h12, bool white, bool dark +) { if (!CCNode::init()) return false; - setContentSize({ 400.0f, 100.0f }); + setContentSize({ 400.0f, 50.0f }); m_object = object; m_searchCallback = search; m_removeCallback = remove; m_index = index; - - auto background = CCScale9Sprite::create("GJ_square01.png", { 0, 0, 80, 80 }); - background->setContentSize({ 400.0f, 100.0f }); - background->setPosition(200.0f, 50.0f); + m_count = count; + + auto background = CCScale9Sprite::create( + index % 10 == 0 ? "square-top.png"_spr : index % 10 == 9 || index == count - 1 ? "square-bottom.png"_spr : "square-middle.png"_spr, { 0, 0, 80, 80 }); + if (dark) background->setColor(index % 2 == 0 ? ccColor3B { 48, 48, 48 } : ccColor3B { 80, 80, 80 }); + else background->setColor(index % 2 == 0 ? ccColor3B { 161, 88, 44 } : ccColor3B { 194, 114, 62 }); + background->setContentSize({ 400.0f, 50.0f }); + background->setPosition(200.0f, 25.0f); addChild(background); auto queryLabel = CCLabelBMFont::create(object.query.empty() ? "(No Query)" : object.query.c_str(), "bigFont.fnt"); queryLabel->setColor(object.query.empty() ? ccColor3B { 127, 127, 127 } : ccColor3B { 255, 255, 255 }); - queryLabel->setScale(0.75f); + queryLabel->setScale(0.375f); queryLabel->setAnchorPoint({ 0.0f, 0.5f }); - queryLabel->setPositionX(100.0f); - queryLabel->limitLabelWidth(225.0f, 0.75f, 0.1f); + queryLabel->setPositionX(50.0f); + queryLabel->limitLabelWidth(225.0f, 0.375f, 0.1f); addChild(queryLabel); auto type = object.type; switch (type) { case 0: { - queryLabel->setPositionY(80.0f); + queryLabel->setPositionY(42.5f); auto levelSprite = CCSprite::createWithSpriteFrameName("GJ_viewLevelsBtn_001.png"); - levelSprite->setScale(1.75f); - levelSprite->setPosition({ 50.0f, 50.0f }); + levelSprite->setScale(0.875f); + levelSprite->setPosition({ 25.0f, 25.0f }); addChild(levelSprite); break; } case 1: { - queryLabel->setPositionY(65.0f); + queryLabel->setPositionY(32.5f); auto listSprite = CCSprite::createWithSpriteFrameName("GJ_viewListsBtn_001.png"); - listSprite->setScale(1.75f); - listSprite->setPosition({ 50.0f, 50.0f }); + listSprite->setScale(0.875f); + listSprite->setPosition({ 25.0f, 25.0f }); addChild(listSprite); break; } case 2: { - queryLabel->setPositionY(50.0f); + queryLabel->setPositionY(25.0f); auto userSprite = CCSprite::createWithSpriteFrameName("GJ_profileButton_001.png"); - userSprite->setScale(1.45f); - userSprite->setPosition({ 50.0f, 50.0f }); + userSprite->setScale(0.725f); + userSprite->setPosition({ 25.0f, 25.0f }); addChild(userSprite); break; } } auto buttonMenu = CCMenu::create(); - buttonMenu->setContentSize({ 400.0f, 100.0f }); + buttonMenu->setContentSize({ 400.0f, 50.0f }); buttonMenu->setPosition(0.0f, 0.0f); addChild(buttonMenu); @@ -71,20 +79,20 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc if (btn2) m_removeCallback(m_index); }); }); - removeButton->setPosition(queryLabel->getScaledContentSize().width + 120.0f, queryLabel->getPositionY()); + removeButton->setPosition(380.0f, 25.0f); buttonMenu->addChild(removeButton); auto searchButton = CCMenuItemExt::createSpriteExtraWithFrameName("GJ_undoBtn_001.png", 0.6f, [this](auto) { m_searchCallback(m_object); }); - searchButton->setPosition(queryLabel->getScaledContentSize().width + 150.0f, queryLabel->getPositionY()); + searchButton->setPosition(350.0f, 25.0f); buttonMenu->addChild(searchButton); if (type < 2) { if (type < 1) { auto filtersNode = CCNode::create(); filtersNode->setAnchorPoint({ 0.0f, 0.5f }); - filtersNode->setContentSize({ 300.0f, 15.0f }); - filtersNode->setPosition(100.0f, 55.0f); - filtersNode->setLayout(RowLayout::create()->setGap(3.0f)->setAxisAlignment(AxisAlignment::Start)->setAutoScale(false)); + filtersNode->setContentSize({ 250.0f, 8.0f }); + filtersNode->setPosition(50.0f, 30.0f); + filtersNode->setLayout(RowLayout::create()->setGap(1.5f)->setAxisAlignment(AxisAlignment::Start)->setAutoScale(false)); addChild(filtersNode); if (object.uncompleted) filtersNode->addChild(CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png")); @@ -99,7 +107,7 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc if (object.noStar) filtersNode->addChild(CCSprite::createWithSpriteFrameName("GJ_starsIcon_gray_001.png")); for (auto child : CCArrayExt<CCNode*>(filtersNode->getChildren())) { - child->setScale(0.7f); + child->setScale(0.35f); } if (filtersNode->getChildrenCount() == 0) { @@ -107,8 +115,8 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc auto noFiltersLabel = CCLabelBMFont::create("(No Filters)", "bigFont.fnt"); noFiltersLabel->setAnchorPoint({ 0.0f, 0.5f }); noFiltersLabel->setColor({ 127, 127, 127 }); - noFiltersLabel->setScale(0.5f); - noFiltersLabel->setPosition(100.0f, 55.0f); + noFiltersLabel->setScale(0.25f); + noFiltersLabel->setPosition(50.0f, 30.0f); addChild(noFiltersLabel); } else filtersNode->updateLayout(); @@ -116,9 +124,9 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc auto difficultiesNode = CCNode::create(); difficultiesNode->setAnchorPoint({ 0.0f, 0.5f }); - difficultiesNode->setContentSize({ 300.0f, 15.0f }); - difficultiesNode->setPosition(100.0f, 35.0f + (type >= 1 ? 5.0f : 0.0f)); - difficultiesNode->setLayout(RowLayout::create()->setGap(3.0f)->setAxisAlignment(AxisAlignment::Start)->setAutoScale(false)); + difficultiesNode->setContentSize({ 250.0f, 8.0f }); + difficultiesNode->setPosition(50.0f, 18.0f + (type > 0 ? 2.0f : 0.0f)); + difficultiesNode->setLayout(RowLayout::create()->setGap(1.5f)->setAxisAlignment(AxisAlignment::Start)->setAutoScale(false)); addChild(difficultiesNode); for (auto difficulty : object.difficulties) { @@ -131,13 +139,13 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc } auto difficultySprite = CCSprite::createWithSpriteFrameName(difficultyFrame.c_str()); - difficultySprite->setScale(0.5f); + difficultySprite->setScale(0.25f); difficultiesNode->addChild(difficultySprite); } if (std::find(object.difficulties.begin(), object.difficulties.end(), -2) != object.difficulties.end() && object.demonFilter > 0) { auto demonFilterSprite = CCSprite::createWithSpriteFrameName(fmt::format("diffIcon_{:02d}_btn_001.png", object.demonFilter).c_str()); - demonFilterSprite->setScale(0.5f); + demonFilterSprite->setScale(0.25f); difficultiesNode->addChild(demonFilterSprite); } @@ -153,46 +161,46 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc } auto lengthLabel = CCLabelBMFont::create(length, "bigFont.fnt"); - lengthLabel->setScale(0.3f); + lengthLabel->setScale(0.25f); difficultiesNode->addChild(lengthLabel); } if (object.star) { auto starSprite = CCSprite::createWithSpriteFrameName("GJ_starsIcon_001.png"); - starSprite->setScale(0.7f); + starSprite->setScale(0.35f); difficultiesNode->addChild(starSprite); } if (difficultiesNode->getChildrenCount() == 0) { difficultiesNode->removeFromParent(); - auto noDifficultiesLabel = CCLabelBMFont::create("(No Difficulties/Lengths)", "bigFont.fnt"); + auto noDifficultiesLabel = CCLabelBMFont::create(type < 1 ? "(No Difficulties/Lengths)" : "(No Difficulties)", "bigFont.fnt"); noDifficultiesLabel->setAnchorPoint({ 0.0f, 0.5f }); noDifficultiesLabel->setColor({ 127, 127, 127 }); - noDifficultiesLabel->setScale(0.5f); - noDifficultiesLabel->setPosition(100.0f, 35.0f + (type >= 1 ? 5.0f : 0.0f)); + noDifficultiesLabel->setScale(0.25f); + noDifficultiesLabel->setPosition(50.0f, 18.0f + (type > 0 ? 2.0f : 0.0f)); addChild(noDifficultiesLabel); } else difficultiesNode->updateLayout(); if (type < 1 && object.song) { auto songSprite = CCSprite::createWithSpriteFrameName("GJ_musicIcon_001.png"); - songSprite->setScale(0.65f); - songSprite->setPosition({ 100.0f + (songSprite->getScaledContentSize().width / 2), 15.0f }); + songSprite->setScale(0.325f); + songSprite->setPosition({ 50.0f + (songSprite->getScaledContentSize().width / 2), 7.0f }); addChild(songSprite); auto songLabel = CCLabelBMFont::create( - (object.customSong ? std::to_string(object.songID) : std::string(LevelTools::getAudioTitle(object.songID - 1).c_str())).c_str(), "bigFont.fnt"); - songLabel->setScale(0.4f); + object.customSong ? std::to_string(object.songID).c_str() : LevelTools::getAudioTitle(object.songID - 1).c_str(), "bigFont.fnt"); + songLabel->setScale(0.25f); songLabel->setAnchorPoint({ 0.0f, 0.5f }); - songLabel->setPosition(120.0f, 15.0f); - songLabel->limitLabelWidth(280.0f, 0.4f, 0.1f); + songLabel->setPosition(60.0f, 7.0f); + songLabel->limitLabelWidth(280.0f, 0.25f, 0.1f); addChild(songLabel); } else if (type < 1) { auto noSongLabel = CCLabelBMFont::create("(No Song)", "bigFont.fnt"); noSongLabel->setAnchorPoint({ 0.0f, 0.5f }); noSongLabel->setColor({ 127, 127, 127 }); - noSongLabel->setScale(0.5f); - noSongLabel->setPosition(100.0f, 15.0f); + noSongLabel->setScale(0.25f); + noSongLabel->setPosition(50.0f, 7.0f); addChild(noSongLabel); } } @@ -210,10 +218,17 @@ bool SearchHistoryNode::init(SearchHistoryObject const& object, int index, Searc auto timeLabel = CCLabelBMFont::create(ss.str().c_str(), "chatFont.fnt"); timeLabel->setColor(white ? ccColor3B { 255, 255, 255 } : ccColor3B { 51, 51, 51 }); timeLabel->setOpacity(white ? 200 : 152); - timeLabel->setScale(0.5f); + timeLabel->setScale(0.4f); timeLabel->setAnchorPoint({ 1.0f, 0.0f }); - timeLabel->setPosition(394.0f, 6.0f); + timeLabel->setPosition(399.0f, 1.0f); addChild(timeLabel); return true; } + +void SearchHistoryNode::draw() { + ccDrawColor4B(0, 0, 0, 75); + glLineWidth(2.0f); + if (m_index % 10 < 9 && m_index < m_count - 1) ccDrawLine({ 0.0f, 0.0f }, { 400.0f, 0.0f }); + if (m_index % 10 > 0) ccDrawLine({ 0.0f, 50.0f }, { 400.0f, 50.0f }); +} diff --git a/src/SearchHistoryNode.hpp b/src/SearchHistoryNode.hpp index 4de7498..a1bbce9 100644 --- a/src/SearchHistoryNode.hpp +++ b/src/SearchHistoryNode.hpp @@ -9,8 +9,10 @@ class SearchHistoryNode : public CCNode { SearchCallback m_searchCallback; RemoveCallback m_removeCallback; int m_index; - - bool init(SearchHistoryObject const&, int, SearchCallback, RemoveCallback, bool, bool); + int m_count; public: - static SearchHistoryNode* create(SearchHistoryObject const&, int, SearchCallback, RemoveCallback, bool, bool); + static SearchHistoryNode* create(SearchHistoryObject const&, int, int, SearchCallback, RemoveCallback, bool, bool, bool); + + bool init(SearchHistoryObject const&, int, int, SearchCallback, RemoveCallback, bool, bool, bool); + void draw() override; }; diff --git a/src/SearchHistoryPopup.cpp b/src/SearchHistoryPopup.cpp index 0d5b262..34913d3 100644 --- a/src/SearchHistoryPopup.cpp +++ b/src/SearchHistoryPopup.cpp @@ -28,7 +28,7 @@ bool SearchHistoryPopup::setup(SearchHistoryCallback callback) { ->setAxisReverse(true) ->setAxisAlignment(AxisAlignment::End) ->setAutoGrowAxis(195.0f) - ->setGap(2.5f) + ->setGap(0.0f) ); m_mainLayer->addChild(m_scrollLayer); @@ -86,24 +86,25 @@ void SearchHistoryPopup::page(int p) { auto count = history.size(); m_prevButton->setVisible(p > 0); - m_nextButton->setVisible(p < (count > 0 ? (count - 1) / 5 : 0)); + m_nextButton->setVisible(p < (count > 0 ? (count - 1) / 10 : 0)); auto h12 = Mod::get()->getSettingValue<bool>("12-hour-time"); auto white = Mod::get()->getSettingValue<bool>("white-time"); - for (int i = p * 5; i < (p + 1) * 5 && i < count; i++) { - m_scrollLayer->m_contentLayer->addChild(SearchHistoryNode::create(history[i], i, [this](SearchHistoryObject const& object) { + auto dark = Loader::get()->isModLoaded("bitz.darkmode_v4"); + for (int i = p * 10; i < (p + 1) * 10 && i < count; i++) { + m_scrollLayer->m_contentLayer->addChild(SearchHistoryNode::create(history[i], i, count, [this](SearchHistoryObject const& object) { m_searchCallback(object); onClose(nullptr); }, [this](int index) { SearchHistory::remove(index); page(m_page); - }, h12, white)); + }, h12, white, dark)); } m_scrollLayer->m_contentLayer->updateLayout(); m_scrollLayer->scrollToTop(); - m_countLabel->setString(fmt::format("{} to {} of {}", count > 0 ? p * 5 + 1 : 0, std::min((p + 1) * 5, (int)count), count).c_str()); + m_countLabel->setString(fmt::format("{} to {} of {}", count > 0 ? p * 10 + 1 : 0, std::min((p + 1) * 10, (int)count), count).c_str()); m_page = p; }