diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3ca02b..42441b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,10 @@ jobs: os: ubuntu-latest target: Android64 + - name: iOS + os: macos-latest + target: iOS + name: ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} diff --git a/about.md b/about.md index d4e2b8d..efd6440 100644 --- a/about.md +++ b/about.md @@ -2,7 +2,7 @@ A texture pack manager for Geode. -The UI for the mod can be accessed in the main menu. There you can open the packs folder by clicking on the folder icon. +The UI for the mod can be accessed in **Graphics Settings** on desktop and regular **Settings** on mobile. There you can open the packs folder by clicking on the **Textures** button. You can **drag** on the small plus icon to move packs from available to applied, and back and forth. diff --git a/mod.json b/mod.json index 9960279..234413a 100644 --- a/mod.json +++ b/mod.json @@ -22,5 +22,8 @@ }, "api": { "include": ["include/*.hpp"] + }, + "dependencies": { + "geode.node-ids": "1.20.1" } } diff --git a/resources/dragIcon.png b/resources/dragIcon.png index 9fbc218..be2e779 100644 Binary files a/resources/dragIcon.png and b/resources/dragIcon.png differ diff --git a/src/BoundedScrollLayer.cpp b/src/BoundedScrollLayer.cpp new file mode 100644 index 0000000..2903892 --- /dev/null +++ b/src/BoundedScrollLayer.cpp @@ -0,0 +1,41 @@ +#include "BoundedScrollLayer.hpp" +#include + +#ifndef GEODE_IS_IOS +// a bit hacky but allows for multiple scroll layers at once +class $modify(CCMouseDispatcher) { + bool dispatchScrollMSG(float x, float y) { + for (CCMouseHandler* handler : CCArrayExt(m_pMouseHandlers)) { + if (BoundedScrollLayer* scroll = typeinfo_cast(handler->getDelegate())) { + scroll->m_doScroll = true; + scroll->scrollWheel(x, y); + scroll->m_doScroll = false; + } + } + return CCMouseDispatcher::dispatchScrollMSG(x, y); + } +}; +#endif + + +BoundedScrollLayer* BoundedScrollLayer::create(CCSize const& size) { + auto ret = new BoundedScrollLayer({ 0, 0, size.width, size.height }); + ret->autorelease(); + return ret; +} + +bool BoundedScrollLayer::testLocation(CCPoint point) { + CCPoint mousePoint = convertToNodeSpace({point.x + getPositionX(), point.y + getPositionY()}); + + if (boundingBox().containsPoint(mousePoint)) { + return true; + } + + return false; +} + +void BoundedScrollLayer::scrollWheel(float y, float x) { + if (!m_doScroll || !testLocation(getMousePos())) return; + + ScrollLayer::scrollWheel(y, x); +} diff --git a/src/BoundedScrollLayer.hpp b/src/BoundedScrollLayer.hpp new file mode 100644 index 0000000..12d32f8 --- /dev/null +++ b/src/BoundedScrollLayer.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include + +using namespace geode::prelude; + +class BoundedScrollLayer : public ScrollLayer { +protected: + BoundedScrollLayer(CCRect const& rect) : ScrollLayer(rect, true, true) {} + +public: + bool m_doScroll = false; + static BoundedScrollLayer* create( + cocos2d::CCSize const& size + ); + + bool testLocation(CCPoint point); + void scrollWheel(float y, float) override; +}; \ No newline at end of file diff --git a/src/PackNode.cpp b/src/PackNode.cpp index 9a511ee..0b806f4 100644 --- a/src/PackNode.cpp +++ b/src/PackNode.cpp @@ -1,19 +1,20 @@ #include "PackNode.hpp" #include +#include #include "PackManager.hpp" -#include "PackSelectLayer.hpp" +#include "PackSelectPopup.hpp" #include "PackInfoPopup.hpp" #include "DragThingy.hpp" bool PackNode::init( - PackSelectLayer* layer, + PackSelectPopup* layer, const std::shared_ptr& pack, float width ) { if (!CCNode::init()) return false; - constexpr float HEIGHT = PackNode::HEIGHT; + constexpr float HEIGHT = PackNode::HEIGHT / .88f; constexpr float SPACE_FOR_MENU = 50.f; constexpr float MOVE_OFFSET = 20.f; constexpr float SPACE_FOR_LOGO = HEIGHT; @@ -32,24 +33,31 @@ bool PackNode::init( auto menu = CCMenu::create(); menu->setPosition(menuPosX, HEIGHT / 2); menu->setID("pack-button-menu"); - - auto logo = CCSprite::create((pack->getResourcesPath() / "pack.png").string().c_str()); - - if (!logo || logo->getUserObject("fallback"_spr)) { - logo = CCSprite::create("noLogo.png"_spr); - if (logo) + menu->setContentSize(this->getContentSize()); + menu->setPosition({0, 0}); + + auto logoSize = CCSize { HEIGHT - PADDING * 2, HEIGHT - PADDING * 2 }; + auto logo = LazySprite::create(logoSize, true); + logo->setLoadCallback([this, logo, logoSize](Result<> res) { + if (res.isErr()) { + logo->CCSprite::initWithFile("noLogo.png"_spr); logo->setOpacity(100); - } - if (logo) { - logo->setPosition({ SPACE_FOR_LOGO / 2 + PADDING, HEIGHT / 2 }); - limitNodeSize(logo, { HEIGHT - PADDING * 2, HEIGHT - PADDING * 2 }, 1.f, .1f); - this->addChild(logo); - } + } + limitNodeSize(logo, logoSize, 1.f, .1f); + }); + logo->loadFromFile((pack->getResourcesPath() / "pack.png")); + logo->setPosition({ SPACE_FOR_LOGO / 2 + PADDING, HEIGHT / 2 }); + this->addChild(logo); + + logo->setID("pack-logo"); auto nameLabel = CCLabelBMFont::create( m_pack->getDisplayName().c_str(), "bigFont.fnt" ); - nameLabel->limitLabelWidth(labelWidth, .65f, .1f); + nameLabel->limitLabelWidth(125.f, 0.40f, 0.1f); + nameLabel->setPositionX(0); + nameLabel->setAnchorPoint({0, 0.5f}); + nameLabel->setID("pack-name-text"); auto nameButton = CCMenuItemSpriteExtra::create( nameLabel, this, menu_selector(PackNode::onView) @@ -58,7 +66,10 @@ bool PackNode::init( PADDING + SPACE_FOR_LOGO + nameLabel->getScaledContentSize().width / 2 - menuPosX, 0 ); + nameButton->setID("pack-name-button"); + nameButton->setContentWidth(nameLabel->getScaledContentWidth()); + nameButton->setEnabled(false); menu->addChild(nameButton); auto applyArrowSpr = CCSprite::create("dragIcon.png"_spr); @@ -75,27 +86,57 @@ bool PackNode::init( m_layer->stopDrag(); } ); + + if (!m_pack->getInfo().has_value()) { + nameButton->setPosition({40 + nameButton->getContentWidth()/2, this->getContentHeight()/2}); + } + else { + PackInfo packInfo = m_pack->getInfo().value(); + CCLabelBMFont* extraInfoLabel = CCLabelBMFont::create(fmt::format("{} | {}", packInfo.m_version.toNonVString(), packInfo.m_id).c_str(), "bigFont.fnt"); + extraInfoLabel->setColor({165, 165, 165}); + extraInfoLabel->limitLabelWidth(125.f, 0.2f, 0.1f); + extraInfoLabel->setScale(0.2f); + extraInfoLabel->setAnchorPoint({0, 0.5f}); + extraInfoLabel->setOpacity(165); + extraInfoLabel->setPosition({40, 8}); + extraInfoLabel->setZOrder(-1); + extraInfoLabel->setID("extra-info-text"); + + this->addChild(extraInfoLabel); + + CCLabelBMFont* authorLabel = CCLabelBMFont::create(packInfo.m_authors.at(0).c_str(), "goldFont.fnt"); + authorLabel->limitLabelWidth(125.f, 0.3f, 0.1f); + authorLabel->setAnchorPoint({0, 0.5f}); + authorLabel->setPosition({40.2, 16}); + authorLabel->setZOrder(-1); + authorLabel->setScale(0.3f); + authorLabel->setID("author-text"); + this->addChild(authorLabel); + + nameButton->setPosition({40 + nameButton->getContentWidth()/2, this->getContentHeight() - 9.5f}); + } + applyArrowSpr->setAnchorPoint(ccp(0, 0)); dragHandle->addChild(applyArrowSpr); dragHandle->setContentSize(applyArrowSpr->getScaledContentSize()); dragHandle->setID("apply-pack-button"); dragHandle->setPosition(width - MOVE_OFFSET, HEIGHT / 2.f); dragHandle->setTouchPriority(-130); + this->addChild(dragHandle); - auto dragBg = CCScale9Sprite::create( - "square02b_001.png", { 0, 0, 80, 80 } + m_draggingBg = CCScale9Sprite::create( + "square02b_001.png" ); - dragBg->setColor({ 0, 0, 0 }); - dragBg->setOpacity(90); - const float bgScaling = 0.5f; - dragBg->setContentSize(this->getContentSize() / bgScaling); - dragBg->setScale(bgScaling); - dragBg->setPosition({ width / 2.f, HEIGHT / 2.f }); - dragBg->setZOrder(-10); - this->addChild(dragBg); - m_draggingBg = dragBg; - dragBg->setVisible(false); + m_draggingBg->setCapInsets({10, 10, 50, 50}); + m_draggingBg->setColor({ 0, 0, 0 }); + m_draggingBg->setOpacity(90); + m_draggingBg->setContentSize(this->getContentSize()); + m_draggingBg->setPosition({ width / 2.f, HEIGHT / 2.f }); + m_draggingBg->setZOrder(-10); + m_draggingBg->setVisible(false); + + this->addChild(m_draggingBg); this->addChild(menu); @@ -107,7 +148,7 @@ void PackNode::onView(CCObject*) { } PackNode* PackNode::create( - PackSelectLayer* layer, + PackSelectPopup* layer, const std::shared_ptr& pack, float width ) { diff --git a/src/PackNode.hpp b/src/PackNode.hpp index 4867d9d..053df52 100644 --- a/src/PackNode.hpp +++ b/src/PackNode.hpp @@ -6,16 +6,16 @@ using namespace geode::prelude; -class PackSelectLayer; +class PackSelectPopup; class PackNode : public CCNode { protected: - PackSelectLayer* m_layer; + PackSelectPopup* m_layer; std::shared_ptr m_pack; - CCNode* m_draggingBg; + CCScale9Sprite* m_draggingBg; bool init( - PackSelectLayer* layer, + PackSelectPopup* layer, const std::shared_ptr& pack, float width ); @@ -24,12 +24,12 @@ class PackNode : public CCNode { public: static PackNode* create( - PackSelectLayer* layer, + PackSelectPopup* layer, const std::shared_ptr& pack, float width ); std::shared_ptr getPack() { return m_pack; } - static constexpr float HEIGHT = 35.f; + static constexpr float HEIGHT = 35.f * .88f; }; diff --git a/src/PackSelectLayer.cpp b/src/PackSelectLayer.cpp deleted file mode 100644 index fe118d0..0000000 --- a/src/PackSelectLayer.cpp +++ /dev/null @@ -1,315 +0,0 @@ -#include "PackSelectLayer.hpp" -#include -#include -#include -#include -#include -#include -#include "PackManager.hpp" -#include "PackNode.hpp" - -static CCSize LAYER_SIZE = { 230.f, 190.f }; - -bool PackSelectLayer::init() { - if (!CCLayer::init()) - return false; - - GameManager::get()->fadeInMenuMusic(); - - this->setID("PackSelectLayer"); - - this->setKeypadEnabled(true); - - auto background = createLayerBG(); - background->setID("background"); - this->addChild(background); - - auto winSize = CCDirector::get()->getWinSize(); - - auto title = CCLabelBMFont::create("Texture Packs", "goldFont.fnt"); - title->setPosition(winSize.width / 2, winSize.height - 20.f); - title->setID("texture-packs-text"); - this->addChild(title); - - auto menu = CCMenu::create(); - menu->setID("menu"); - menu->setZOrder(10); - - auto backBtn = CCMenuItemSpriteExtra::create( - CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"), - this, menu_selector(PackSelectLayer::onGoBack) - ); - backBtn->setPosition(-winSize.width / 2 + 25.f, winSize.height / 2 - 25.f); - backBtn->setID("back-button"); - menu->addChild(backBtn); - - auto applyBtn = CCMenuItemSpriteExtra::create( - ButtonSprite::create("Apply", "goldFont.fnt", "GJ_button_01.png", .8f), - this, menu_selector(PackSelectLayer::onApply) - ); - applyBtn->setPosition(0.f, -winSize.height / 2 + 25.f); - applyBtn->setID("apply-button"); - menu->addChild(applyBtn); - - auto folderBtn = CCMenuItemSpriteExtra::create( - CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png"), - this, menu_selector(PackSelectLayer::onOpenFolder) - ); - folderBtn->setPosition(winSize.width / 2.f - 25.f, -winSize.height / 2.f + 25.f); - folderBtn->setID("folder-button"); - menu->addChild(folderBtn); - - auto reloadSpr = CCSprite::createWithSpriteFrameName("GJ_updateBtn_001.png"); - reloadSpr->setScale(.8f); - auto reloadBtn = CCMenuItemSpriteExtra::create(reloadSpr, this, menu_selector(PackSelectLayer::onReloadPacks)); - reloadBtn->setPosition(-winSize.width / 2.f + 25.f, -winSize.height / 2.f + 25.f); - reloadBtn->setID("reload-button"); - menu->addChild(reloadBtn); - - this->addChild(menu); - - // available packs list - - auto availableTitle = CCLabelBMFont::create("Available", "goldFont.fnt"); - availableTitle->setPosition( - winSize / 2 + CCPoint { -120, LAYER_SIZE.height / 2 + 15.f } - ); - availableTitle->setScale(.65f); - availableTitle->setID("available-text"); - this->addChild(availableTitle); - - auto availableListBG = CCScale9Sprite::create( - "square02b_001.png", { 0, 0, 80, 80 } - ); - availableListBG->setColor({ 0, 0, 0 }); - availableListBG->setOpacity(90); - availableListBG->setContentSize(LAYER_SIZE); - availableListBG->setPosition(winSize / 2 + CCPoint { -120, 0 }); - availableListBG->setID("available-list-background"); - this->addChild(availableListBG); - - m_availableList = ScrollLayer::create(LAYER_SIZE); - m_availableList->setPosition( - winSize / 2 + CCPoint { -120, 0 } - LAYER_SIZE / 2 - ); - m_availableList->setID("available-list"); - this->addChild(m_availableList); - - // applied packs list - - auto appliedTitle = CCLabelBMFont::create("Applied", "goldFont.fnt"); - appliedTitle->setPosition( - winSize / 2 + CCPoint { 120, LAYER_SIZE.height / 2 + 15.f } - ); - appliedTitle->setScale(.65f); - appliedTitle->setID("applied-text"); - this->addChild(appliedTitle); - - auto appliedListBG = CCScale9Sprite::create( - "square02b_001.png", { 0, 0, 80, 80 } - ); - appliedListBG->setColor({ 0, 0, 0 }); - appliedListBG->setOpacity(90); - appliedListBG->setContentSize(LAYER_SIZE); - appliedListBG->setPosition(winSize / 2 + CCPoint { 120, 0 }); - appliedListBG->setID("applied-list-background"); - this->addChild(appliedListBG); - - m_appliedList = ScrollLayer::create(LAYER_SIZE); - m_appliedList->setPosition( - winSize / 2 + CCPoint { 120, 0 } - LAYER_SIZE / 2 - ); - m_appliedList->setID("applied-list"); - this->addChild(m_appliedList); - - this->updateLists(); - - return true; -} - -void PackSelectLayer::updateList( - ScrollLayer* list, - std::vector> const& packs, - bool resetPos -) { - list->m_contentLayer->removeAllChildren(); - float totalHeight = .0f; - std::vector rendered; - for (auto& pack : packs) { - auto node = PackNode::create(this, pack, LAYER_SIZE.width); - totalHeight += node->getScaledContentSize().height; - node->setPosition(0.f, -totalHeight); - list->m_contentLayer->addChild(node); - - rendered.push_back(node); - } - if (totalHeight < LAYER_SIZE.height) { - totalHeight = LAYER_SIZE.height; - } - for (auto& node : rendered) { - node->setPositionY(node->getPositionY() + totalHeight); - } - list->m_contentLayer->setContentSize({ LAYER_SIZE.width, totalHeight }); - if (resetPos) { - list->moveToTop(); - } -} - -void PackSelectLayer::updateLists(bool resetPos) { - this->updateList(m_availableList, PackManager::get()->getAvailablePacks(), resetPos); - this->updateList(m_appliedList, PackManager::get()->getAppliedPacks(), resetPos); -} - -void PackSelectLayer::keyBackClicked() { - this->onGoBack(nullptr); -} - -void PackSelectLayer::onGoBack(CCObject*) { - CCDirector::get()->replaceScene(CCTransitionFade::create(0.5, MenuLayer::scene(false))); -} - -void PackSelectLayer::onApply(CCObject*) { - PackManager::get()->applyPacks(+[]() -> CCLayer* { - return PackSelectLayer::create(); - }); -} - -void PackSelectLayer::onOpenFolder(CCObject*) { - utils::file::openFolder(PackManager::get()->getPackDir()); -} - -void PackSelectLayer::onReloadPacks(CCObject*) { - size_t count = PackManager::get()->loadPacks(); - this->updateLists(); - Notification::create(fmt::format("Loaded {} packs", count), NotificationIcon::Success, 0.5f)->show(); -} - -void PackSelectLayer::startDragging(PackNode* node) { - m_draggingNode = node; - m_lastDragIdx = -1; - auto const pos = node->getParent()->convertToWorldSpace(node->getPosition()); - - // remove from the scroll layer - node->retain(); - node->removeFromParentAndCleanup(false); - this->addChild(node); - node->release(); - - node->setPosition(this->convertToNodeSpace(pos)); - - m_dragListFrom = this->whereDragList(); -} - -PackListType PackSelectLayer::whereDragList() { - if (!m_draggingNode) return PackListType::Available; - // it has the anchor point on bot left for some reason - auto x = m_draggingNode->getPosition().x + m_draggingNode->getContentSize().width / 2.f; - if (x > CCDirector::get()->getWinSize().width / 2.f) { - return PackListType::Applied; - } else { - return PackListType::Available; - } -} - -void PackSelectLayer::moveDrag(const CCPoint& offset) { - m_draggingNode->setPosition(m_draggingNode->getPosition() + offset); - this->reorderDragging(); -} - -void PackSelectLayer::reorderDragging() { - auto const listTypeTo = this->whereDragList(); - - auto appliedList = std::make_pair(m_appliedList, PackManager::get()->getAppliedPacks()); - auto availableList = std::make_pair(m_availableList, PackManager::get()->getAvailablePacks()); - - auto& listTo = listTypeTo == PackListType::Applied ? appliedList : availableList; - auto& listFrom = listTypeTo != PackListType::Applied ? appliedList : availableList; - - const auto listTop = listTo.first->convertToWorldSpace( - listTo.first->m_contentLayer->getPosition() + listTo.first->m_contentLayer->getContentSize()).y; - const auto nodeY = m_draggingNode->getPosition().y + m_draggingNode->getScaledContentSize().height / 2.f; - - auto targetIdx = static_cast(std::max((listTop - nodeY) / PackNode::HEIGHT, 0.f)); - - if (targetIdx == m_lastDragIdx && listTypeTo == m_dragListTo) return; - - m_lastDragIdx = targetIdx; - - if (listTypeTo != m_dragListTo) { - this->reorderList(listFrom.first, listFrom.second, -1); - } - this->reorderList(listTo.first, listTo.second, targetIdx); - - m_dragListTo = listTypeTo; -} - -void PackSelectLayer::reorderList(ScrollLayer* list, std::vector> const& packs, size_t skipIdx) { - const auto childForPack = [list] (const std::shared_ptr& pack) -> PackNode* { - for (auto* child : CCArrayExt(list->m_contentLayer->getChildren())) { - if (child->getPack() == pack) return child; - } - return nullptr; - }; - - auto const totalHeight = list->m_contentLayer->getContentSize().height; - - float y = totalHeight; - int visualIdx = 0; - for (const auto& pack : packs) { - if (visualIdx == skipIdx) { - y -= PackNode::HEIGHT; - ++visualIdx; - } - - if (pack == m_draggingNode->getPack()) { - continue; - } - - auto* child = childForPack(pack); - - y -= PackNode::HEIGHT; - - child->setPositionY(y); - - ++visualIdx; - } -} - -void PackSelectLayer::stopDrag() { - PackManager::get()->movePackToIdx(m_draggingNode->getPack(), m_dragListTo, m_lastDragIdx); - - m_draggingNode->removeFromParent(); - - m_draggingNode = nullptr; - - this->updateLists(false); - - if (m_dragListFrom != m_dragListTo) { - auto* from = m_dragListFrom == PackListType::Applied ? m_appliedList : m_availableList; - auto* to = m_dragListTo == PackListType::Applied ? m_appliedList : m_availableList; - // scroll source list up - if (from->m_contentLayer->getPositionY() < 0.f) { - from->m_contentLayer->setPositionY(from->m_contentLayer->getPositionY() + PackNode::HEIGHT); - } - // scroll destination list down - if (to->m_contentLayer->getPositionY() + to->m_contentLayer->getContentSize().height > to->getContentSize().height + 1.f) { - to->m_contentLayer->setPositionY(to->m_contentLayer->getPositionY() - PackNode::HEIGHT); - } - } -} - -PackSelectLayer* PackSelectLayer::create() { - auto ret = new PackSelectLayer; - if (ret->init()) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; -} - -PackSelectLayer* PackSelectLayer::scene() { - auto layer = PackSelectLayer::create(); - switchToScene(layer); - return layer; -} diff --git a/src/PackSelectPopup.cpp b/src/PackSelectPopup.cpp new file mode 100644 index 0000000..af9dae0 --- /dev/null +++ b/src/PackSelectPopup.cpp @@ -0,0 +1,400 @@ +#include "PackSelectPopup.hpp" +#include +#include +#include +#include +#include +#include +#include +#include "PackManager.hpp" +#include "PackNode.hpp" +#include "BoundedScrollLayer.hpp" + +// go back to options after apply +class $modify(ReloadMenuLayer, MenuLayer) { + + struct Fields { + bool m_openPackSelectPopup = false; + }; + + static CCScene* scene(bool isVideoOptionsOpen) { + auto ret = MenuLayer::scene(false); + auto menuLayer = ret->getChildByType(0); + + // the robtop delay + if (isVideoOptionsOpen) { + CCCallFunc* callback = cocos2d::CCCallFunc::create(menuLayer, callfunc_selector(ReloadMenuLayer::doOpenOptions)); + CCDelayTime* delay = cocos2d::CCDelayTime::create(0.0f); + CCSequence* sequence = cocos2d::CCSequence::create(delay, callback, nullptr); + menuLayer->runAction(sequence); + } + return ret; + } + + void doOpenOptions() { + auto optionsLayer = OptionsLayer::create(); + optionsLayer->showLayer(true); + bool isDesktop = false; + #ifdef GEODE_IS_DESKTOP + isDesktop = true; + #endif + if (isDesktop || Loader::get()->isModLoaded("weebify.high-graphics-android")) { + optionsLayer->onVideo(nullptr); + } + PackSelectPopup::create()->show(); + } +}; + +static CCSize LAYER_SIZE = { 230.f, 210.f }; + +bool PackSelectPopup::init() { + if (!Popup<>::initAnchored(440.f, 290.f, "GJ_square01.png")) return false; + + GameManager::get()->fadeInMenuMusic(); + + this->setID("PackSelectPopup"); + + this->setKeypadEnabled(true); + + auto size = m_mainLayer->getContentSize(); + + auto title = CCLabelBMFont::create("Texture Packs", "goldFont.fnt"); + title->setPosition(size.width / 2, size.height - 20.f); + title->setID("texture-packs-text"); + title->setScale(.8f); + m_mainLayer->addChild(title); + + int availCount = PackManager::get()->getAvailablePacks().size(); + int appliedCount = PackManager::get()->getAppliedPacks().size(); + + m_infoLabel = CCLabelBMFont::create(fmt::format("Available: {}\nApplied: {}\nTotal: {}", availCount, appliedCount, availCount + appliedCount).c_str(), "chatFont.fnt"); + m_infoLabel->setPosition({70, 25}); + m_infoLabel->setID("info-text"); + m_infoLabel->setScale(.45f); + m_infoLabel->setOpacity(200.f); + m_mainLayer->addChild(m_infoLabel); + + auto reloadSpr = CCSprite::createWithSpriteFrameName("GJ_updateBtn_001.png"); + reloadSpr->setScale(.7f); + auto reloadBtn = CCMenuItemSpriteExtra::create(reloadSpr, this, menu_selector(PackSelectPopup::onReloadPacks)); + reloadBtn->setID("reload-button"); + reloadBtn->setPosition({30, 25}); + + m_buttonMenu->addChild(reloadBtn); + auto applySpr = ButtonSprite::create("Apply", "goldFont.fnt", "GJ_button_01.png", .8f); + applySpr->setScale(0.9f); + auto applyBtn = CCMenuItemSpriteExtra::create( + applySpr, + this, menu_selector(PackSelectPopup::onApply) + ); + applyBtn->setID("apply-button"); + applyBtn->setPosition({size.width/2, 25}); + m_buttonMenu->addChild(applyBtn); + + auto folderBtn = CCMenuItemSpriteExtra::create( + CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png"), + this, menu_selector(PackSelectPopup::onOpenFolder) + ); + folderBtn->setID("folder-button"); + folderBtn->setPosition({size.width - 30, 25}); + m_buttonMenu->addChild(folderBtn); + + // available packs list + + float distanceFromCenter = 105.f; + float heightOffset = -5.f; + float scale = .88f; + + auto availableTitle = CCLabelBMFont::create("Available", "goldFont.fnt"); + availableTitle->setPosition( + size / 2 + CCPoint { -distanceFromCenter, LAYER_SIZE.height / 2 + heightOffset } + ); + availableTitle->setScale(.65f); + availableTitle->setID("available-text"); + m_mainLayer->addChild(availableTitle); + + auto availableListBG = CCScale9Sprite::create( + "square02b_001.png", { 0, 0, 80, 80 } + ); + availableListBG->setColor({ 0, 0, 0 }); + availableListBG->setOpacity(90); + availableListBG->setScale(scale); + availableListBG->setContentSize(LAYER_SIZE); + availableListBG->setPosition(size / 2 + CCPoint { -distanceFromCenter, heightOffset }); + availableListBG->setID("available-list-background"); + m_mainLayer->addChild(availableListBG); + + m_availableList = BoundedScrollLayer::create(LAYER_SIZE * scale); + m_availableList->m_contentLayer->setLayout( + SimpleColumnLayout::create() + ->setMainAxisDirection(AxisDirection::TopToBottom) + ->setMainAxisAlignment(MainAxisAlignment::Start) + ->setMainAxisScaling(AxisScaling::Grow) + ); + m_availableList->setPosition( + size / 2 + CCPoint { -distanceFromCenter, heightOffset } - LAYER_SIZE * scale / 2 + ); + m_availableList->setID("available-list"); + m_mainLayer->addChild(m_availableList); + + // applied packs list + + auto appliedTitle = CCLabelBMFont::create("Applied", "goldFont.fnt"); + appliedTitle->setPosition( + size / 2 + CCPoint { distanceFromCenter, LAYER_SIZE.height / 2 + heightOffset } + ); + appliedTitle->setScale(.65f); + appliedTitle->setID("applied-text"); + m_mainLayer->addChild(appliedTitle); + + auto appliedListBG = CCScale9Sprite::create( + "square02b_001.png", { 0, 0, 80, 80 } + ); + appliedListBG->setColor({ 0, 0, 0 }); + appliedListBG->setOpacity(90); + appliedListBG->setScale(scale); + appliedListBG->setContentSize(LAYER_SIZE); + appliedListBG->setPosition(size / 2 + CCPoint { distanceFromCenter, + heightOffset }); + appliedListBG->setID("applied-list-background"); + m_mainLayer->addChild(appliedListBG); + + m_appliedList = BoundedScrollLayer::create(LAYER_SIZE * scale); + m_appliedList->m_contentLayer->setLayout( + SimpleColumnLayout::create() + ->setMainAxisDirection(AxisDirection::TopToBottom) + ->setMainAxisAlignment(MainAxisAlignment::Start) + ->setMainAxisScaling(AxisScaling::Grow) + ); + m_appliedList->setPosition( + size / 2 + CCPoint { distanceFromCenter, heightOffset } - LAYER_SIZE * scale / 2 + ); + m_appliedList->setID("applied-list"); + m_mainLayer->addChild(m_appliedList); + + this->updateLists(); + + return true; +} + +void PackSelectPopup::updateList( + ScrollLayer* list, + std::vector> const& packs, + bool resetPos +) { + list->m_contentLayer->removeAllChildren(); + for (auto& pack : packs) { + auto node = PackNode::create(this, pack, LAYER_SIZE.width); + node->setScale(.88f); + list->m_contentLayer->addChild(node); + } + + list->m_contentLayer->setContentHeight(list->getContentHeight()); + list->m_contentLayer->updateLayout(); + if (resetPos) { + list->moveToTop(); + } + + int availCount = PackManager::get()->getAvailablePacks().size(); + int appliedCount = PackManager::get()->getAppliedPacks().size(); + m_infoLabel->setString(fmt::format("Available: {}\nApplied: {}\nTotal: {}", availCount, appliedCount, availCount + appliedCount).c_str()); +} + +void PackSelectPopup::updateLists(bool resetPos) { + this->updateList(m_availableList, PackManager::get()->getAvailablePacks(), resetPos); + this->updateList(m_appliedList, PackManager::get()->getAppliedPacks(), resetPos); +} + +void PackSelectPopup::onApply(CCObject*) { + PackManager::get()->applyPacks(+[]() -> CCLayer* { + CCScene* scene = MenuLayer::scene(true); + ReloadMenuLayer* menuLayer = static_cast(scene->getChildByType(0)); + menuLayer->m_fields->m_openPackSelectPopup = true; + return menuLayer; + }); +} + +void PackSelectPopup::onOpenFolder(CCObject*) { + utils::file::openFolder(PackManager::get()->getPackDir()); +} + +void PackSelectPopup::onReloadPacks(CCObject*) { + size_t count = PackManager::get()->loadPacks(); + this->updateLists(); + Notification::create(fmt::format("Loaded {} packs", count), NotificationIcon::Success, 0.5f)->show(); +} + +std::pair PackSelectPopup::getPackListTypeAndIndex(const std::shared_ptr& pack) { + auto manager = PackManager::get(); + const auto& applied = manager->getAppliedPacks(); + const auto& available = manager->getAvailablePacks(); + + auto it = std::find(applied.begin(), applied.end(), pack); + if (it != applied.end()) { + return { PackListType::Applied, static_cast(std::distance(applied.begin(), it)) }; + } + + it = std::find(available.begin(), available.end(), pack); + return { PackListType::Available, static_cast(std::distance(available.begin(), it)) }; +} +void PackSelectPopup::startDragging(PackNode* node) { + m_draggingNode = node; + + // set initial information, else clicking with no drag can cause it to move to available + auto packData = getPackListTypeAndIndex(node->getPack()); + m_dragListTo = packData.first; + m_lastDragIdx = packData.second; + auto const pos = node->getParent()->convertToWorldSpace(node->getPosition()); + + // remove from the scroll layer + node->retain(); + node->removeFromParentAndCleanup(false); + this->addChild(node); + node->release(); + + node->setPosition(this->convertToNodeSpace(pos)); + this->schedule(schedule_selector(PackSelectPopup::scrollSchedule), 0.15f); + + m_dragListFrom = this->whereDragList(); +} + +PackListType PackSelectPopup::whereDragList() { + if (!m_draggingNode) return PackListType::Available; + auto x = m_draggingNode->getPosition().x; + if (x > getContentWidth() / 2.f) { + return PackListType::Applied; + } else { + return PackListType::Available; + } +} + +void PackSelectPopup::moveDrag(const CCPoint& offset) { + m_draggingNode->setPosition(m_draggingNode->getPosition() + offset); + this->reorderDragging(); +} + +void PackSelectPopup::scrollSchedule(float dt) { + auto x = m_draggingNode->getPosition().x; + auto centerY = m_draggingNode->getPosition().y - PackNode::HEIGHT / 2; + + float offset = 10; + + bool up = centerY + offset > m_availableList->getPositionY() + m_availableList->getContentHeight(); + bool down = centerY - offset < m_availableList->getPositionY(); + + if (up || down) { + if (x > getContentWidth() / 2.f) { + scrollOnDrag(PackListType::Applied, up); + } else { + scrollOnDrag(PackListType::Available, up); + } + } +} + +void PackSelectPopup::scrollOnDrag(PackListType type, bool up) { + auto list = type == PackListType::Available ? m_availableList : m_appliedList; + int direction = up ? -1 : 1; + float nextY = list->m_contentLayer->getPositionY() + direction * PackNode::HEIGHT; + + float minY = -list->m_contentLayer->getContentHeight() + list->getContentHeight(); + nextY = std::min(0.0f, std::max(minY, nextY)); + + auto action = CCMoveTo::create(0.15f, {list->m_contentLayer->getPositionX(), nextY}); + list->m_contentLayer->stopAllActions(); + list->m_contentLayer->runAction(action); + this->reorderDragging(); +} + +void PackSelectPopup::reorderDragging() { + auto const listTypeTo = this->whereDragList(); + + auto appliedList = std::make_pair(m_appliedList, PackManager::get()->getAppliedPacks()); + auto availableList = std::make_pair(m_availableList, PackManager::get()->getAvailablePacks()); + + auto& listTo = listTypeTo == PackListType::Applied ? appliedList : availableList; + auto& listFrom = listTypeTo != PackListType::Applied ? appliedList : availableList; + + const auto listTop = listTo.first->convertToWorldSpace( + listTo.first->m_contentLayer->getPosition() + listTo.first->m_contentLayer->getContentSize()).y; + const auto nodeY = m_draggingNode->getPosition().y; + + auto count = static_cast(listTo.first->m_contentLayer->getChildrenCount()); + auto targetIdx = static_cast(std::clamp((listTop - nodeY) / PackNode::HEIGHT, 0.f, count)); + + if (targetIdx == m_lastDragIdx && listTypeTo == m_dragListTo) return; + + m_lastDragIdx = targetIdx; + + if (listTypeTo != m_dragListTo) { + this->reorderList(listFrom.first, listFrom.second, -1); + } + this->reorderList(listTo.first, listTo.second, targetIdx); + + m_dragListTo = listTypeTo; +} + +void PackSelectPopup::reorderList(ScrollLayer* list, std::vector> const& packs, size_t skipIdx) { + const auto childForPack = [list] (const std::shared_ptr& pack) -> PackNode* { + for (auto* child : CCArrayExt(list->m_contentLayer->getChildren())) { + if (child->getPack() == pack) return child; + } + return nullptr; + }; + + auto const totalHeight = list->m_contentLayer->getContentSize().height; + + float y = totalHeight; + int visualIdx = 0; + for (const auto& pack : packs) { + if (visualIdx == skipIdx) { + y -= PackNode::HEIGHT; + ++visualIdx; + } + + if (pack == m_draggingNode->getPack()) { + continue; + } + + auto* child = childForPack(pack); + + y -= PackNode::HEIGHT; + + child->stopAllActions(); + child->runAction(CCEaseInOut::create(CCMoveTo::create(0.3, {child->getPositionX(), y + PackNode::HEIGHT / 2}), 2.f)); + + ++visualIdx; + } +} + +void PackSelectPopup::stopDrag() { + PackManager::get()->movePackToIdx(m_draggingNode->getPack(), m_dragListTo, m_lastDragIdx); + this->unschedule(schedule_selector(PackSelectPopup::scrollSchedule)); + + m_draggingNode->removeFromParent(); + + m_draggingNode = nullptr; + + this->updateLists(false); + + if (m_dragListFrom != m_dragListTo) { + auto* from = m_dragListFrom == PackListType::Applied ? m_appliedList : m_availableList; + auto* to = m_dragListTo == PackListType::Applied ? m_appliedList : m_availableList; + // scroll source list up + if (from->m_contentLayer->getPositionY() < 0.f) { + from->m_contentLayer->setPositionY(from->m_contentLayer->getPositionY() + PackNode::HEIGHT); + } + // scroll destination list down + if (to->m_contentLayer->getPositionY() + to->m_contentLayer->getContentSize().height > to->getContentSize().height + 1.f) { + to->m_contentLayer->setPositionY(to->m_contentLayer->getPositionY() - PackNode::HEIGHT); + } + } +} + +PackSelectPopup* PackSelectPopup::create() { + auto ret = new PackSelectPopup(); + if (ret->init()) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} diff --git a/src/PackSelectLayer.hpp b/src/PackSelectPopup.hpp similarity index 75% rename from src/PackSelectLayer.hpp rename to src/PackSelectPopup.hpp index 96f5b07..c66311b 100644 --- a/src/PackSelectLayer.hpp +++ b/src/PackSelectPopup.hpp @@ -1,55 +1,55 @@ -#pragma once - -#include -#include -#include -#include "Pack.hpp" -#include "PackManager.hpp" - -class PackNode; - -using namespace geode::prelude; - -class PackSelectLayer : public CCLayer { -protected: - ScrollLayer* m_availableList = nullptr; - ScrollLayer* m_appliedList = nullptr; - PackNode* m_draggingNode = nullptr; - size_t m_lastDragIdx = size_t(-1); - PackListType m_dragListFrom, m_dragListTo; - - bool init() override; - - void keyBackClicked() override; - - void updateList( - ScrollLayer* list, - std::vector> const& packs, - bool resetPos - ); - - void reorderDragging(); - void reorderList( - ScrollLayer* list, - std::vector> const& packs, - size_t skipIdx - ); - - PackListType whereDragList(); - -public: - static PackSelectLayer* create(); - static PackSelectLayer* scene(); - - void updateLists(bool resetPos = true); - - void onGoBack(CCObject*); - void onApply(CCObject*); - - void onOpenFolder(CCObject*); - void onReloadPacks(CCObject*); - - void startDragging(PackNode* node); - void moveDrag(const CCPoint& offset); - void stopDrag(); -}; +#pragma once + +#include +#include +#include +#include "Pack.hpp" +#include "PackManager.hpp" + +class PackNode; + +using namespace geode::prelude; + +class PackSelectPopup : public Popup<> { +protected: + ScrollLayer* m_availableList = nullptr; + ScrollLayer* m_appliedList = nullptr; + CCLabelBMFont* m_infoLabel = nullptr; + PackNode* m_draggingNode = nullptr; + size_t m_lastDragIdx = size_t(-1); + PackListType m_dragListFrom, m_dragListTo; + + bool setup() override { return true; } + bool init() override; + + void updateList( + ScrollLayer* list, + std::vector> const& packs, + bool resetPos + ); + + void reorderDragging(); + void reorderList( + ScrollLayer* list, + std::vector> const& packs, + size_t skipIdx + ); + + PackListType whereDragList(); + std::pair getPackListTypeAndIndex(const std::shared_ptr& pack); + +public: + static PackSelectPopup* create(); + + void updateLists(bool resetPos = true); + void scrollSchedule(float dt); + void scrollOnDrag(PackListType type, bool up); + void onApply(CCObject*); + + void onOpenFolder(CCObject*); + void onReloadPacks(CCObject*); + + void startDragging(PackNode* node); + void moveDrag(const CCPoint& offset); + void stopDrag(); +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8d88740..7c50c9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,74 @@ #include -#include "PackSelectLayer.hpp" +#include "PackSelectPopup.hpp" #include +#include +#include #include #include using namespace geode::prelude; +// Mobile support +class $modify(MyOptionsLayer, OptionsLayer) { + void customSetup() { + OptionsLayer::customSetup(); + if (CCNode* optionsMenu = m_mainLayer->getChildByID("options-menu")) { + if (optionsMenu->getChildByID("graphics-button")) return; + + ButtonSprite* buttonSprite = ButtonSprite::create("Textures", 130, true, "goldFont.fnt", "GJ_button_01.png", 32, 1); + + CCMenuItemSpriteExtra* textureLoaderBtn = CCMenuItemSpriteExtra::create(buttonSprite, this, menu_selector(MyOptionsLayer::onTextureLdr)); + + if (CCNode* optionsButton = optionsMenu->getChildByID("options-button")) { + optionsMenu->insertAfter(textureLoaderBtn, optionsButton); + } + + optionsMenu->updateLayout(); + } + } + + void onTextureLdr(CCObject*) { + PackSelectPopup::create()->show(); + } +}; + + +class $modify(MyVideoOptionsLayer, VideoOptionsLayer) { + bool init() { + if (!VideoOptionsLayer::init()) return false; + + for (CCNode* child : CCArrayExt(m_buttonMenu->getChildren())) { + if (CCMenuItemSpriteExtra* btn = typeinfo_cast(child)) { + if (ButtonSprite* btnSpr = btn->getChildByType(0)) { + if (std::string_view(btnSpr->m_label->getString()) == "Advanced") { + ButtonSprite* buttonSprite = ButtonSprite::create("Textures", 60, true, "goldFont.fnt", "GJ_button_04.png", 25, 0.5f); + + CCMenuItemSpriteExtra* textureLoaderBtn = CCMenuItemSpriteExtra::create(buttonSprite, this, menu_selector(MyVideoOptionsLayer::onTextureLdr)); + textureLoaderBtn->setID("texture-loader-button"_spr); + + textureLoaderBtn->setPositionX(btn->getPositionX()); + textureLoaderBtn->setPositionY(btn->getPositionY() - 30); + + m_buttonMenu->addChild(textureLoaderBtn); + } + } + } + } + return true; + } + + void onTextureLdr(CCObject*) { + PackSelectPopup::create()->show(); + } +}; + class $modify(MyMenuLayer, MenuLayer) { bool init() { if (!MenuLayer::init()) return false; + if (Mod::get()->getSavedValue("shown-moved-alert")) return true; + NodeIDs::provideFor(this); auto menu = this->getChildByID("right-side-menu"); @@ -27,7 +85,21 @@ class $modify(MyMenuLayer, MenuLayer) { } void onTextureLdr(CCObject*) { - PackSelectLayer::scene(); + bool isDesktop = false; + #ifdef GEODE_IS_DESKTOP + isDesktop = true; + #endif + // Prompt where the button is relocated, since many mobile users use the high graphics mod, we want to direct them to the right place still. + if (isDesktop || Loader::get()->isModLoaded("weebify.high-graphics-android")) { + createQuickPopup("Woah!", "Texture Loader has been moved into Graphics Settings, go to graphics settings and click the Textures button to manage your Texture Packs!", "I UNDERSTAND", nullptr, [] (FLAlertLayer*, bool selected) { + Mod::get()->setSavedValue("shown-moved-alert", true); + }, true); + } + else { + createQuickPopup("Woah!", "Texture Loader has been moved into Settings, go to settings and click the Textures button to manage your Texture Packs!", "I UNDERSTAND", nullptr, [] (FLAlertLayer*, bool selected) { + Mod::get()->setSavedValue("shown-moved-alert", true); + }, true); + } } };