Merge pull request #476 from geode-sdk/anchor-layout

Anchor layout
This commit is contained in:
HJfod 2024-02-01 15:58:58 +02:00 committed by GitHub
commit 255c12af3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1475 additions and 1164 deletions

View file

@ -994,6 +994,18 @@ public:
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL LayoutOptions* getLayoutOptions(); GEODE_DLL LayoutOptions* getLayoutOptions();
/**
* Adds a child at an anchored position with an offset. The node is placed
* in its parent where the anchor specifies, and then the offset is used to
* relatively adjust the node's position
* @param child The child to add
* @param anchor Where the place the child relative to this node
* @param offset Where to place the child relative to the anchor
* @param useAnchorLayout If true, sets this node's layout to `AnchorLayout`
* if no other layout is already specified
* @note Geode addition
*/
GEODE_DLL void addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
/** /**
* Swap two children * Swap two children

View file

@ -371,6 +371,90 @@ public:
static ColumnLayout* create(); static ColumnLayout* create();
}; };
/**
* The relative position of a node to its parent in an AnchorLayout
*/
enum class Anchor {
Center,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft,
Left,
};
/**
* Options for customizing a node's position in an AnchorLayout
*/
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
protected:
Anchor m_anchor = Anchor::Center;
CCPoint m_offset = CCPointZero;
public:
static AnchorLayoutOptions* create();
Anchor getAnchor() const;
CCPoint getOffset() const;
AnchorLayoutOptions* setAnchor(Anchor anchor);
AnchorLayoutOptions* setOffset(CCPoint const& offset);
};
/**
* A layout for positioning nodes at specific positions relative to their
* parent's content size. See `Anchor` for available anchoring options. Useful
* for example for popups, where a popup using `AnchorLayout` can be
* automatically resized without needing to manually shuffle nodes around
*/
class GEODE_DLL AnchorLayout : public Layout {
public:
static AnchorLayout* create();
void apply(CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override;
/**
* Get a position according to anchoring rules, with the same algorithm as
* `AnchorLayout` uses to position its nodes
* @param in The node whose content size to use as a reference
* @param anchor The anchor position
* @param offset Offset from the anchor
* @returns A position in `in` for the anchored and offsetted location
*/
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset);
};
/**
* A layout for automatically copying the content size of a node to other nodes.
* Basically main use case is for FLAlertLayers (setting the size of the
* background and `m_buttonMenu` based on `m_mainLayer`)
*/
class CopySizeLayout : public cocos2d::AnchorLayout {
protected:
cocos2d::CCArray* m_targets;
public:
static CopySizeLayout* create();
virtual ~CopySizeLayout();
/**
* Add a target to be automatically resized. Any targets' layouts will
* also be updated when this layout is updated
*/
CopySizeLayout* add(cocos2d::CCNode* target);
/**
* Remove a target from being automatically resized
*/
CopySizeLayout* remove(cocos2d::CCNode* target);
void apply(cocos2d::CCNode* in) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
};
#pragma warning(pop) #pragma warning(pop)
NS_CC_END NS_CC_END

View file

@ -13,6 +13,7 @@ namespace geode {
cocos2d::extension::CCScale9Sprite* m_bgSprite; cocos2d::extension::CCScale9Sprite* m_bgSprite;
cocos2d::CCLabelBMFont* m_title = nullptr; cocos2d::CCLabelBMFont* m_title = nullptr;
CCMenuItemSpriteExtra* m_closeBtn; CCMenuItemSpriteExtra* m_closeBtn;
bool m_dynamic;
~Popup() override { ~Popup() override {
cocos2d::CCTouchDispatcher::get()->unregisterForcePrio(this); cocos2d::CCTouchDispatcher::get()->unregisterForcePrio(this);
@ -22,10 +23,13 @@ namespace geode {
cocos2d::CCTouchDispatcher::get()->addTargetedDelegate(this, -500, true); cocos2d::CCTouchDispatcher::get()->addTargetedDelegate(this, -500, true);
} }
bool init( private:
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png", bool initBase(
cocos2d::CCRect bgRect = { 0, 0, 80, 80 } float width, float height, InitArgs... args, char const* bg,
cocos2d::CCRect bgRect, bool dynamic
) { ) {
m_dynamic = dynamic;
auto winSize = cocos2d::CCDirector::get()->getWinSize(); auto winSize = cocos2d::CCDirector::get()->getWinSize();
m_size = cocos2d::CCSize { width, height }; m_size = cocos2d::CCSize { width, height };
@ -44,6 +48,17 @@ namespace geode {
m_buttonMenu->setZOrder(100); m_buttonMenu->setZOrder(100);
m_mainLayer->addChild(m_buttonMenu); m_mainLayer->addChild(m_buttonMenu);
if (dynamic) {
m_mainLayer->ignoreAnchorPointForPosition(false);
m_mainLayer->setPosition(winSize / 2);
m_mainLayer->setContentSize(m_size);
m_mainLayer->setLayout(
cocos2d::CopySizeLayout::create()
->add(m_buttonMenu)
->add(m_bgSprite)
);
}
this->setTouchEnabled(true); this->setTouchEnabled(true);
auto closeSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_closeBtn_001.png"); auto closeSpr = cocos2d::CCSprite::createWithSpriteFrameName("GJ_closeBtn_001.png");
@ -52,8 +67,13 @@ namespace geode {
m_closeBtn = CCMenuItemSpriteExtra::create( m_closeBtn = CCMenuItemSpriteExtra::create(
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose) closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
); );
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f); if (dynamic) {
m_buttonMenu->addChild(m_closeBtn); m_buttonMenu->addChildAtPosition(m_closeBtn, cocos2d::Anchor::TopLeft, { 3.f, -3.f });
}
else {
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
m_buttonMenu->addChild(m_closeBtn);
}
if (!setup(std::forward<InitArgs>(args)...)) { if (!setup(std::forward<InitArgs>(args)...)) {
return false; return false;
@ -65,6 +85,27 @@ namespace geode {
return true; return true;
} }
protected:
[[deprecated("Use Popup::initAnchored instead, as it has more reasonable menu and layer content sizes")]]
bool init(
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
) {
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, false);
}
/**
* Init with AnchorLayout and the content size of `m_buttonMenu` and
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
* being the size of the window)
*/
bool initAnchored(
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
) {
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, true);
}
virtual bool setup(InitArgs... args) = 0; virtual bool setup(InitArgs... args) = 0;
void keyDown(cocos2d::enumKeyCodes key) { void keyDown(cocos2d::enumKeyCodes key) {
@ -86,13 +127,17 @@ namespace geode {
) { ) {
if (m_title) { if (m_title) {
m_title->setString(title.c_str()); m_title->setString(title.c_str());
} else { }
auto winSize = cocos2d::CCDirector::sharedDirector()->getWinSize(); else {
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font); m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
m_title->setPosition( m_title->setZOrder(2);
winSize.width / 2, winSize.height / 2 + m_size.height / 2 - offset if (m_dynamic) {
); m_mainLayer->addChildAtPosition(m_title, cocos2d::Anchor::Top, ccp(0, -offset));
m_mainLayer->addChild(m_title, 2); }
else {
auto winSize = cocos2d::CCDirector::get()->getWinSize();
m_title->setPosition(winSize / 2 + ccp(0, m_size.height / 2 - offset));
}
} }
m_title->limitLabelWidth(m_size.width - 20.f, scale, .1f); m_title->limitLabelWidth(m_size.width - 20.f, scale, .1f);
} }

View file

@ -0,0 +1,82 @@
#include <cocos2d.h>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
using namespace geode::prelude;
AnchorLayoutOptions* AnchorLayoutOptions::create() {
return new AnchorLayoutOptions();
}
Anchor AnchorLayoutOptions::getAnchor() const {
return m_anchor;
}
CCPoint AnchorLayoutOptions::getOffset() const {
return m_offset;
}
AnchorLayoutOptions* AnchorLayoutOptions::setAnchor(Anchor anchor) {
m_anchor = anchor;
return this;
}
AnchorLayoutOptions* AnchorLayoutOptions::setOffset(CCPoint const& offset) {
m_offset = offset;
return this;
}
AnchorLayout* AnchorLayout::create() {
auto ret = new AnchorLayout();
ret->autorelease();
return ret;
}
void AnchorLayout::apply(CCNode* on) {
on->ignoreAnchorPointForPosition(false);
for (auto node : CCArrayExt<CCNode*>(this->getNodesToPosition(on))) {
if (auto opts = typeinfo_cast<AnchorLayoutOptions*>(node->getLayoutOptions())) {
auto pos = opts->getOffset();
auto size = on->getContentSize();
switch (opts->getAnchor()) {
default:
case Anchor::Center: pos += size / 2; break;
case Anchor::TopLeft: pos += ccp(0, size.height); break;
case Anchor::Top: pos += ccp(size.width / 2, size.height); break;
case Anchor::TopRight: pos += ccp(size.width, size.height); break;
case Anchor::Right: pos += ccp(size.width, size.height / 2); break;
case Anchor::BottomRight: pos += ccp(size.width, 0); break;
case Anchor::Bottom: pos += ccp(size.width / 2, 0); break;
case Anchor::BottomLeft: pos += ccp(0, 0); break;
case Anchor::Left: pos += ccp(0, size.height / 2); break;
}
node->ignoreAnchorPointForPosition(false);
node->setPosition(pos);
}
}
}
CCSize AnchorLayout::getSizeHint(CCNode* on) const {
return on->getContentSize();
}
CCPoint AnchorLayout::getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset) {
auto pos = offset;
auto size = in->getContentSize();
switch (anchor) {
default:
case Anchor::Center: pos += size / 2; break;
case Anchor::TopLeft: pos += ccp(0, size.height); break;
case Anchor::Top: pos += ccp(size.width / 2, size.height); break;
case Anchor::TopRight: pos += ccp(size.width, size.height); break;
case Anchor::Right: pos += ccp(size.width, size.height / 2); break;
case Anchor::BottomRight: pos += ccp(size.width, 0); break;
case Anchor::Bottom: pos += ccp(size.width / 2, 0); break;
case Anchor::BottomLeft: pos += ccp(0, 0); break;
case Anchor::Left: pos += ccp(0, size.height / 2); break;
}
return pos;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
#include <cocos2d.h>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
using namespace geode::prelude;
CopySizeLayout* CopySizeLayout::create() {
auto ret = new CopySizeLayout();
ret->m_targets = CCArray::create();
ret->m_targets->retain();
ret->autorelease();
return ret;
}
CopySizeLayout::~CopySizeLayout() {
m_targets->release();
}
CopySizeLayout* CopySizeLayout::add(CCNode* target) {
m_targets->addObject(target);
return this;
}
CopySizeLayout* CopySizeLayout::remove(CCNode* target) {
m_targets->removeObject(target);
return this;
}
void CopySizeLayout::apply(CCNode* in) {
AnchorLayout::apply(in);
for (auto& node : CCArrayExt<CCNode*>(m_targets)) {
// Prevent accidental infinite loop
if (node == in) continue;
node->ignoreAnchorPointForPosition(false);
node->setContentSize(in->getContentSize());
node->setPosition(in->getContentSize() / 2);
node->updateLayout();
}
}
CCSize CopySizeLayout::getSizeHint(CCNode* in) const {
return in->getContentSize();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,66 @@
#include <cocos2d.h>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Log.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
using namespace geode::prelude;
bool SpacerNode::init(size_t grow) {
if (!CCNode::init())
return false;
m_grow = grow;
return true;
}
SpacerNode* SpacerNode::create(size_t grow) {
auto ret = new SpacerNode;
if (ret && ret->init(grow)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void SpacerNode::setGrow(size_t grow) {
m_grow = grow;
}
size_t SpacerNode::getGrow() const {
return m_grow;
}
bool SpacerNodeChild::init(CCNode* child, size_t grow) {
if (!SpacerNode::init(grow))
return false;
if (child) {
this->addChild(child);
m_child = child;
}
return true;
}
SpacerNodeChild* SpacerNodeChild::create(CCNode* child, size_t grow) {
auto ret = new SpacerNodeChild;
if (ret && ret->init(child, grow)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void SpacerNodeChild::setContentSize(CCSize const& size) {
CCNode::setContentSize(size);
if (m_child) {
m_child->setPosition(CCPointZero);
m_child->setContentSize(size);
m_child->setAnchorPoint(CCPointZero);
}
}

View file

@ -237,4 +237,16 @@ size_t CCNode::getEventListenerCount() {
GeodeNodeMetadata::set(this)->m_eventListeners.size(); GeodeNodeMetadata::set(this)->m_eventListeners.size();
} }
void CCNode::addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset, bool useAnchorLayout) {
auto layout = this->getLayout();
if (!layout && useAnchorLayout) {
this->setLayout(AnchorLayout::create());
}
child->setPosition(AnchorLayout::getAnchoredPosition(this, anchor, offset));
if (useAnchorLayout) {
child->setLayoutOptions(AnchorLayoutOptions::create()->setAnchor(anchor)->setOffset(offset));
}
this->addChild(child);
}
#pragma warning(pop) #pragma warning(pop)

View file

@ -37,16 +37,15 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
constexpr float logoOffset = 10.f; constexpr float logoOffset = 10.f;
auto topNode = CCNode::create(); auto topNode = CCNode::create();
topNode->setContentSize({350.f, 80.f}); topNode->setAnchorPoint({ .5f, .5f });
topNode->setContentSize({ 350.f, 80.f });
topNode->setLayout( topNode->setLayout(
RowLayout::create() RowLayout::create()
->setAxisAlignment(AxisAlignment::Center) ->setAxisAlignment(AxisAlignment::Center)
->setAutoScale(false) ->setAutoScale(false)
->setCrossAxisOverflow(true) ->setCrossAxisOverflow(true)
); );
m_mainLayer->addChild(topNode); m_mainLayer->addChildAtPosition(topNode, Anchor::Top, ccp(0, -30));
topNode->setAnchorPoint({.5f, .5f});
topNode->setPosition(winSize.width / 2, winSize.height / 2 + 115.f);
auto logoSpr = this->createLogo({logoSize, logoSize}); auto logoSpr = this->createLogo({logoSize, logoSize});
topNode->addChild(logoSpr); topNode->addChild(logoSpr);
@ -88,18 +87,13 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
(metadata.getDetails() ? metadata.getDetails().value() : "### No description provided."), (metadata.getDetails() ? metadata.getDetails().value() : "### No description provided."),
{ 350.f, 137.5f } { 350.f, 137.5f }
); );
m_detailsArea->setPosition( m_mainLayer->addChildAtPosition(m_detailsArea, Anchor::Center, ccp(0, -20));
winSize.width / 2 - m_detailsArea->getScaledContentSize().width / 2,
winSize.height / 2 - m_detailsArea->getScaledContentSize().height / 2 - 20.f
);
m_mainLayer->addChild(m_detailsArea);
m_scrollbar = Scrollbar::create(m_detailsArea->getScrollLayer()); m_scrollbar = Scrollbar::create(m_detailsArea->getScrollLayer());
m_scrollbar->setPosition( m_mainLayer->addChildAtPosition(
winSize.width / 2 + m_detailsArea->getScaledContentSize().width / 2 + 20.f, m_scrollbar, Anchor::Center,
winSize.height / 2 - 20.f ccp(m_detailsArea->getScaledContentSize().width / 2 + 20.f, -20)
); );
m_mainLayer->addChild(m_scrollbar);
// changelog // changelog
if (metadata.getChangelog()) { if (metadata.getChangelog()) {
@ -127,10 +121,10 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
changelogBtnOnSpr->setScale(.65f); changelogBtnOnSpr->setScale(.65f);
auto changelogBtn = CCMenuItemToggler::create( auto changelogBtn = CCMenuItemToggler::create(
changelogBtnOffSpr, changelogBtnOnSpr, this, menu_selector(ModInfoPopup::onChangelog) changelogBtnOffSpr, changelogBtnOnSpr,
this, menu_selector(ModInfoPopup::onChangelog)
); );
changelogBtn->setPosition(-LAYER_SIZE.width / 2 + 21.5f, .0f); m_buttonMenu->addChildAtPosition(changelogBtn, Anchor::Left, ccp(21.5f, 0));
m_buttonMenu->addChild(changelogBtn);
} }
// mod metadata // mod metadata
@ -138,8 +132,7 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
infoSpr->setScale(.85f); infoSpr->setScale(.85f);
m_infoBtn = CCMenuItemSpriteExtra::create(infoSpr, this, menu_selector(ModInfoPopup::onInfo)); m_infoBtn = CCMenuItemSpriteExtra::create(infoSpr, this, menu_selector(ModInfoPopup::onInfo));
m_infoBtn->setPosition(LAYER_SIZE.width / 2 - 25.f, LAYER_SIZE.height / 2 - 25.f); m_buttonMenu->addChildAtPosition(m_infoBtn, Anchor::TopRight, ccp(-25, -25));
m_buttonMenu->addChild(m_infoBtn);
// repo button // repo button
if (metadata.getRepository()) { if (metadata.getRepository()) {
@ -148,8 +141,7 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
this, this,
menu_selector(ModInfoPopup::onRepository) menu_selector(ModInfoPopup::onRepository)
); );
repoBtn->setPosition(LAYER_SIZE.width / 2 - 25.f, -LAYER_SIZE.height / 2 + 25.f); m_buttonMenu->addChildAtPosition(repoBtn, Anchor::BottomRight, ccp(-25, 25));
m_buttonMenu->addChild(repoBtn);
} }
// support button // support button
@ -159,8 +151,7 @@ bool ModInfoPopup::setup(ModMetadata const& metadata, ModListLayer* list) {
this, this,
menu_selector(ModInfoPopup::onSupport) menu_selector(ModInfoPopup::onSupport)
); );
supportBtn->setPosition(LAYER_SIZE.width / 2 - 60.f, -LAYER_SIZE.height / 2 + 25.f); m_buttonMenu->addChildAtPosition(supportBtn, Anchor::BottomRight, ccp(-60, 25));
m_buttonMenu->addChild(supportBtn);
} }
return true; return true;
@ -204,40 +195,12 @@ void ModInfoPopup::onInfo(CCObject*) {
} }
void ModInfoPopup::onChangelog(CCObject* sender) { void ModInfoPopup::onChangelog(CCObject* sender) {
auto winSize = CCDirector::get()->getWinSize();
if (!m_changelogArea) {
m_changelogArea = MDTextArea::create(this->getMetadata().getChangelog().value(), { 350.f, 137.5f });
m_changelogArea->setPosition(
-5000.f, winSize.height / 2 - m_changelogArea->getScaledContentSize().height / 2 - 20.f
);
m_changelogArea->setVisible(false);
m_mainLayer->addChild(m_changelogArea);
}
auto toggle = static_cast<CCMenuItemToggler*>(sender); auto toggle = static_cast<CCMenuItemToggler*>(sender);
m_detailsArea->setString((
m_detailsArea->setVisible(toggle->isToggled());
// as it turns out, cocos2d is stupid and still passes touch
// events to invisible nodes
m_detailsArea->setPositionX(
toggle->isToggled() ? winSize.width / 2 - m_detailsArea->getScaledContentSize().width / 2 :
-5000.f
);
m_changelogArea->setVisible(!toggle->isToggled());
// as it turns out, cocos2d is stupid and still passes touch
// events to invisible nodes
m_changelogArea->setPositionX(
!toggle->isToggled() ? winSize.width / 2 - m_changelogArea->getScaledContentSize().width / 2 :
-5000.f
);
m_scrollbar->setTarget(
toggle->isToggled() ? toggle->isToggled() ?
m_detailsArea->getScrollLayer() : this->getMetadata().getDetails().value() :
m_changelogArea->getScrollLayer() this->getMetadata().getChangelog().value()
); ).c_str());
} }
void ModInfoPopup::setInstallStatus(std::optional<UpdateProgress> const& progress) { void ModInfoPopup::setInstallStatus(std::optional<UpdateProgress> const& progress) {
@ -349,14 +312,15 @@ LocalModInfoPopup::LocalModInfoPopup()
ModInstallFilter("") ModInstallFilter("")
) {} ) {}
bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) { bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_item = Index::get()->getMajorItem(mod->getMetadata().getID()); m_item = Index::get()->getMajorItem(mod->getMetadata().getID());
if (m_item) if (m_item) {
m_installListener.setFilter(m_item->getMetadata().getID()); m_installListener.setFilter(m_item->getMetadata().getID());
}
m_mod = mod; m_mod = mod;
if (!ModInfoPopup::init(LAYER_SIZE.width, LAYER_SIZE.height, mod->getMetadata(), list)) return false; if (!ModInfoPopup::initAnchored(LAYER_SIZE.width, LAYER_SIZE.height, mod->getMetadata(), list)) return false;
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
@ -364,10 +328,10 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
auto settingsSpr = CCSprite::createWithSpriteFrameName("GJ_optionsBtn_001.png"); auto settingsSpr = CCSprite::createWithSpriteFrameName("GJ_optionsBtn_001.png");
settingsSpr->setScale(.65f); settingsSpr->setScale(.65f);
auto settingsBtn = auto settingsBtn = CCMenuItemSpriteExtra::create(
CCMenuItemSpriteExtra::create(settingsSpr, this, menu_selector(LocalModInfoPopup::onSettings)); settingsSpr, this, menu_selector(LocalModInfoPopup::onSettings)
settingsBtn->setPosition(-LAYER_SIZE.width / 2 + 25.f, -LAYER_SIZE.height / 2 + 25.f); );
m_buttonMenu->addChild(settingsBtn); m_buttonMenu->addChildAtPosition(settingsBtn, Anchor::BottomLeft, ccp(25, 25));
// Check if a config directory for the mod exists // Check if a config directory for the mod exists
if (ghc::filesystem::exists(mod->getConfigDir(false))) { if (ghc::filesystem::exists(mod->getConfigDir(false))) {
@ -379,8 +343,7 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
auto configBtn = CCMenuItemSpriteExtra::create( auto configBtn = CCMenuItemSpriteExtra::create(
configSpr, this, menu_selector(LocalModInfoPopup::onOpenConfigDir) configSpr, this, menu_selector(LocalModInfoPopup::onOpenConfigDir)
); );
configBtn->setPosition(-LAYER_SIZE.width / 2 + 65.f, -LAYER_SIZE.height / 2 + 25.f); m_buttonMenu->addChildAtPosition(configBtn, Anchor::BottomLeft, ccp(65, 25));
m_buttonMenu->addChild(configBtn);
} }
if (!mod->hasSettings()) { if (!mod->hasSettings()) {
@ -397,9 +360,8 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
auto enableBtn = CCMenuItemToggler::create( auto enableBtn = CCMenuItemToggler::create(
disableBtnSpr, enableBtnSpr, this, menu_selector(LocalModInfoPopup::onEnableMod) disableBtnSpr, enableBtnSpr, this, menu_selector(LocalModInfoPopup::onEnableMod)
); );
enableBtn->setPosition(-155.f, 75.f);
enableBtn->toggle(!mod->shouldLoad()); enableBtn->toggle(!mod->shouldLoad());
m_buttonMenu->addChild(enableBtn); m_buttonMenu->addChildAtPosition(enableBtn, Anchor::Center, ccp(-155, 75));
if (mod->isInternal()) { if (mod->isInternal()) {
enableBtn->setTarget(this, menu_selector(LocalModInfoPopup::onDisablingNotSupported)); enableBtn->setTarget(this, menu_selector(LocalModInfoPopup::onDisablingNotSupported));
@ -420,8 +382,7 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
auto uninstallBtn = CCMenuItemSpriteExtra::create( auto uninstallBtn = CCMenuItemSpriteExtra::create(
uninstallBtnSpr, this, menu_selector(LocalModInfoPopup::onUninstall) uninstallBtnSpr, this, menu_selector(LocalModInfoPopup::onUninstall)
); );
uninstallBtn->setPosition(-85.f, 75.f); m_buttonMenu->addChildAtPosition(uninstallBtn, Anchor::Center, ccp(-85, 75));
m_buttonMenu->addChild(uninstallBtn);
// todo: show update button on loader that invokes the installer // todo: show update button on loader that invokes the installer
if (m_item && Index::get()->isUpdateAvailable(m_item)) { if (m_item && Index::get()->isUpdateAvailable(m_item)) {
@ -434,20 +395,21 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_installBtnSpr->setScale(.6f); m_installBtnSpr->setScale(.6f);
m_installBtn = CCMenuItemSpriteExtra::create(m_installBtnSpr, this, menu_selector(LocalModInfoPopup::onUpdate)); m_installBtn = CCMenuItemSpriteExtra::create(m_installBtnSpr, this, menu_selector(LocalModInfoPopup::onUpdate));
m_installBtn->setPosition(-8.0f, 75.f); m_buttonMenu->addChildAtPosition(m_installBtn, Anchor::Center, ccp(-8, 75));
m_buttonMenu->addChild(m_installBtn);
m_installStatus = DownloadStatusNode::create(); m_installStatus = DownloadStatusNode::create();
m_installStatus->setPosition(winSize.width / 2 + 105.f, winSize.height / 2 + 75.f);
m_installStatus->setVisible(false); m_installStatus->setVisible(false);
m_mainLayer->addChild(m_installStatus); m_mainLayer->addChildAtPosition(m_installStatus, Anchor::Center, ccp(105, 75));
auto minorIndexItem = Index::get()->getItem( auto minorIndexItem = Index::get()->getItem(
mod->getMetadata().getID(), mod->getMetadata().getID(),
ComparableVersionInfo(mod->getMetadata().getVersion(), VersionCompare::MoreEq) ComparableVersionInfo(mod->getMetadata().getVersion(), VersionCompare::MoreEq)
); );
// TODO: use column layout here? auto availableContainer = CCNode::create();
availableContainer->setLayout(ColumnLayout::create()->setGap(2));
availableContainer->setAnchorPoint({ .0f, .5f });
availableContainer->setContentSize({ 200.f, 45.f });
if (m_item->getMetadata().getVersion().getMajor() > minorIndexItem->getMetadata().getVersion().getMajor()) { if (m_item->getMetadata().getVersion().getMajor() > minorIndexItem->getMetadata().getVersion().getMajor()) {
// has major update // has major update
@ -458,8 +420,7 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_latestVersionLabel->setScale(.35f); m_latestVersionLabel->setScale(.35f);
m_latestVersionLabel->setAnchorPoint({.0f, .5f}); m_latestVersionLabel->setAnchorPoint({.0f, .5f});
m_latestVersionLabel->setColor({94, 219, 255}); m_latestVersionLabel->setColor({94, 219, 255});
m_latestVersionLabel->setPosition(winSize.width / 2 + 35.f, winSize.height / 2 + 75.f); availableContainer->addChild(m_latestVersionLabel);
m_mainLayer->addChild(m_latestVersionLabel);
} }
if (minorIndexItem->getMetadata().getVersion() > mod->getMetadata().getVersion()) { if (minorIndexItem->getMetadata().getVersion() > mod->getMetadata().getVersion()) {
@ -471,20 +432,11 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
m_minorVersionLabel->setScale(.35f); m_minorVersionLabel->setScale(.35f);
m_minorVersionLabel->setAnchorPoint({.0f, .5f}); m_minorVersionLabel->setAnchorPoint({.0f, .5f});
m_minorVersionLabel->setColor({94, 219, 255}); m_minorVersionLabel->setColor({94, 219, 255});
if (m_latestVersionLabel) { availableContainer->addChild(m_minorVersionLabel);
m_latestVersionLabel->setPosition(
winSize.width / 2 + 35.f, winSize.height / 2 + 81.f
);
m_minorVersionLabel->setPosition(
winSize.width / 2 + 35.f, winSize.height / 2 + 69.f
);
} else {
m_minorVersionLabel->setPosition(
winSize.width / 2 + 35.f, winSize.height / 2 + 75.f
);
}
m_mainLayer->addChild(m_minorVersionLabel);
} }
availableContainer->updateLayout();
m_mainLayer->addChildAtPosition(availableContainer, Anchor::Center, ccp(35, 75));
} }
} }
if (mod == Mod::get()) { if (mod == Mod::get()) {
@ -494,11 +446,10 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
"chatFont.fnt" "chatFont.fnt"
); );
label->setAlignment(kCCTextAlignmentRight); label->setAlignment(kCCTextAlignmentRight);
label->setAnchorPoint(ccp(1, 0)); label->setAnchorPoint({ .0f, .5f });
label->setScale(0.6f); label->setScale(.5f);
label->setPosition(winSize.width - 3.f, 3.f);
label->setOpacity(89); label->setOpacity(89);
m_mainLayer->addChild(label); m_mainLayer->addChildAtPosition(label, Anchor::BottomRight, ccp(5, 0));
} }
// issue report button // issue report button
@ -511,8 +462,7 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
auto issuesBtn = CCMenuItemSpriteExtra::create( auto issuesBtn = CCMenuItemSpriteExtra::create(
issuesBtnSpr, this, menu_selector(LocalModInfoPopup::onIssues) issuesBtnSpr, this, menu_selector(LocalModInfoPopup::onIssues)
); );
issuesBtn->setPosition(0.f, -LAYER_SIZE.height / 2 + 25.f); m_buttonMenu->addChildAtPosition(issuesBtn, Anchor::Bottom, ccp(0, 25));
m_buttonMenu->addChild(issuesBtn);
} }
return true; return true;
@ -685,7 +635,7 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
if (!ModInfoPopup::init(LAYER_SIZE.width, LAYER_SIZE.height, item->getMetadata(), list)) return false; if (!ModInfoPopup::initAnchored(LAYER_SIZE.width, LAYER_SIZE.height, item->getMetadata(), list)) return false;
// bruh why is this here if we are allowing for browsing already installed mods // bruh why is this here if we are allowing for browsing already installed mods
// if (item->isInstalled()) return true; // if (item->isInstalled()) return true;
@ -701,13 +651,11 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
m_installBtn = CCMenuItemSpriteExtra::create( m_installBtn = CCMenuItemSpriteExtra::create(
m_installBtnSpr, this, menu_selector(IndexItemInfoPopup::onInstall) m_installBtnSpr, this, menu_selector(IndexItemInfoPopup::onInstall)
); );
m_installBtn->setPosition(-143.0f, 75.f); m_buttonMenu->addChildAtPosition(m_installBtn, Anchor::Center, ccp(-143, 75));
m_buttonMenu->addChild(m_installBtn);
m_installStatus = DownloadStatusNode::create(); m_installStatus = DownloadStatusNode::create();
m_installStatus->setPosition(winSize.width / 2 - 25.f, winSize.height / 2 + 75.f);
m_installStatus->setVisible(false); m_installStatus->setVisible(false);
m_mainLayer->addChild(m_installStatus); m_mainLayer->addChildAtPosition(m_installStatus, Anchor::Center, ccp(-25, 75));
return true; return true;
} }

View file

@ -36,7 +36,6 @@ protected:
CCLabelBMFont* m_latestVersionLabel = nullptr; CCLabelBMFont* m_latestVersionLabel = nullptr;
CCLabelBMFont* m_minorVersionLabel = nullptr; CCLabelBMFont* m_minorVersionLabel = nullptr;
MDTextArea* m_detailsArea; MDTextArea* m_detailsArea;
MDTextArea* m_changelogArea = nullptr;
Scrollbar* m_scrollbar; Scrollbar* m_scrollbar;
IndexItemHandle m_item; IndexItemHandle m_item;

View file

@ -19,7 +19,7 @@ bool MDPopup::setup(
m_size.height - 120.f, m_size.height - 120.f,
}; };
auto content = MDTextArea::create(info, contentSize); auto content = MDTextArea::create(info, contentSize);
content->setPosition(winSize / 2 - contentSize / 2); content->setPosition(winSize / 2);
m_mainLayer->addChild(content); m_mainLayer->addChild(content);
auto btnSpr = ButtonSprite::create(btn1Text); auto btnSpr = ButtonSprite::create(btn1Text);

View file

@ -108,6 +108,9 @@ Result<ccColor3B> colorForIdentifier(std::string const& tag) {
bool MDTextArea::init(std::string const& str, CCSize const& size) { bool MDTextArea::init(std::string const& str, CCSize const& size) {
if (!CCLayer::init()) return false; if (!CCLayer::init()) return false;
this->ignoreAnchorPointForPosition(false);
this->setAnchorPoint({ .5f, .5f });
m_text = str; m_text = str;
m_size = size - CCSize { 15.f, 0.f }; m_size = size - CCSize { 15.f, 0.f };
this->setContentSize(m_size); this->setContentSize(m_size);
@ -726,7 +729,6 @@ void MDTextArea::updateLabel() {
m_content->setPositionY(-2.5f); m_content->setPositionY(-2.5f);
} }
m_scrollLayer->moveToTop(); m_scrollLayer->moveToTop();
} }

View file

@ -2,6 +2,71 @@
using namespace geode::prelude; using namespace geode::prelude;
// static void fixChildPositions(CCNode* in, CCSize const& size) {
// auto winSize = CCDirector::get()->getWinSize();
// auto offset = size / 2 - in->getContentSize() / 2;
// for (auto node : CCArrayExt<CCNode*>(in->getChildren())) {
// node->setPosition(node->getPosition() + offset);
// if (node->isIgnoreAnchorPointForPosition()) {
// node->setPosition(node->getPosition() + node->getScaledContentSize() * node->getAnchorPoint());
// node->ignoreAnchorPointForPosition(false);
// }
// constexpr int LEFT = 0b0001;
// constexpr int RIGHT = 0b0010;
// constexpr int BOTTOM = 0b0100;
// constexpr int TOP = 0b1000;
// int p = 0b0000;
// if (node->getPositionX() <= winSize.width / 2 - size.width * 0.25) {
// p |= LEFT;
// }
// else if (node->getPositionX() >= winSize.width / 2 + size.width * 0.25) {
// p |= RIGHT;
// }
// if (node->getPositionY() <= winSize.height / 2 - size.height * 0.25) {
// p |= BOTTOM;
// }
// else if (node->getPositionY() >= winSize.height / 2 + size.height * 0.25) {
// p |= TOP;
// }
// Anchor anchor = Anchor::Center;
// switch (p) {
// case LEFT | BOTTOM: anchor = Anchor::BottomLeft; break;
// case LEFT | TOP: anchor = Anchor::TopLeft; break;
// case LEFT: anchor = Anchor::Left; break;
// case RIGHT | BOTTOM: anchor = Anchor::BottomRight; break;
// case RIGHT | TOP: anchor = Anchor::TopRight; break;
// case RIGHT: anchor = Anchor::Right; break;
// case TOP: anchor = Anchor::Top; break;
// case BOTTOM: anchor = Anchor::Bottom; break;
// }
// auto anchorPos = AnchorLayout::getAnchoredPosition(in, anchor, ccp(0, 0));
// node->setLayoutOptions(
// AnchorLayoutOptions::create()
// ->setAnchor(anchor)
// ->setOffset(node->getPosition() - anchorPos)
// );
// }
// in->ignoreAnchorPointForPosition(false);
// }
// void geode::enableDynamicLayoutForPopup(FLAlertLayer* alert, CCNode* bg) {
// auto winSize = CCDirector::get()->getWinSize();
// auto size = bg->getContentSize();
// alert->m_mainLayer->ignoreAnchorPointForPosition(false);
// alert->m_mainLayer->setContentSize(size);
// alert->m_mainLayer->setPosition(winSize / 2);
// alert->m_mainLayer->setLayout(AutoPopupLayout::create(alert->m_buttonMenu, bg));
// }
class QuickPopup : public FLAlertLayer, public FLAlertLayerProtocol { class QuickPopup : public FLAlertLayer, public FLAlertLayerProtocol {
protected: protected:
MiniFunction<void(FLAlertLayer*, bool)> m_selected; MiniFunction<void(FLAlertLayer*, bool)> m_selected;