fully implement enable-if!!!!

This commit is contained in:
HJfod 2024-08-28 22:38:07 +03:00
parent 4fb42754cb
commit db9e2ccb48
7 changed files with 608 additions and 88 deletions

View file

@ -9,8 +9,9 @@
// this unfortunately has to be included because of C++ templates // this unfortunately has to be included because of C++ templates
#include "../utils/JsonValidation.hpp" #include "../utils/JsonValidation.hpp"
// todo in v4: these can be removed as well as the friend decl in LegacyCustomSettingV3 // todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
class LegacyCustomSettingToV3Node; class LegacyCustomSettingToV3Node;
class ModSettingsPopup;
namespace geode { namespace geode {
class ModSettingsManager; class ModSettingsManager;
@ -59,6 +60,11 @@ namespace geode {
* Get the "enable-if" scheme for this setting * Get the "enable-if" scheme for this setting
*/ */
std::optional<std::string> getEnableIf() const; std::optional<std::string> getEnableIf() const;
/**
* Check if this setting should be enabled based on the "enable-if" scheme
*/
bool shouldEnable() const;
std::optional<std::string> getEnableIfDescription() const;
/** /**
* Whether this setting requires a restart on change * Whether this setting requires a restart on change
*/ */
@ -403,6 +409,8 @@ namespace geode {
class Impl; class Impl;
std::shared_ptr<Impl> m_impl; std::shared_ptr<Impl> m_impl;
friend class ::ModSettingsPopup;
protected: protected:
bool init(std::shared_ptr<SettingV3> setting, float width); bool init(std::shared_ptr<SettingV3> setting, float width);
@ -453,7 +461,7 @@ namespace geode {
SettingNodeSizeChangeEventV3(SettingNodeV3* node); SettingNodeSizeChangeEventV3(SettingNodeV3* node);
virtual ~SettingNodeSizeChangeEventV3(); virtual ~SettingNodeSizeChangeEventV3();
SettingNodeV3* getTargetNode() const; SettingNodeV3* getNode() const;
}; };
class GEODE_DLL SettingNodeValueChangeEventV3 : public Event { class GEODE_DLL SettingNodeValueChangeEventV3 : public Event {
private: private:
@ -461,9 +469,10 @@ namespace geode {
std::shared_ptr<Impl> m_impl; std::shared_ptr<Impl> m_impl;
public: public:
SettingNodeValueChangeEventV3(bool commit); SettingNodeValueChangeEventV3(SettingNodeV3* node, bool commit);
virtual ~SettingNodeValueChangeEventV3(); virtual ~SettingNodeValueChangeEventV3();
SettingNodeV3* getNode() const;
bool isCommit() const; bool isCommit() const;
}; };

View file

@ -427,6 +427,19 @@ namespace geode {
} }
return *this; return *this;
} }
template <class T>
JsonExpectedValue& mustBe(std::string_view name, auto predicate) requires requires {
{ predicate(std::declval<T>()) } -> std::convertible_to<Result<>>;
} {
if (this->hasError()) return *this;
if (auto v = this->template tryGet<T>()) {
auto p = predicate(*v);
if (!p) {
this->setError("json value is not {}: {}", name, p.unwrapErr());
}
}
return *this;
}
// -- Dealing with objects -- // -- Dealing with objects --

View file

@ -15,22 +15,27 @@ SettingNodeSizeChangeEventV3::SettingNodeSizeChangeEventV3(SettingNodeV3* node)
} }
SettingNodeSizeChangeEventV3::~SettingNodeSizeChangeEventV3() = default; SettingNodeSizeChangeEventV3::~SettingNodeSizeChangeEventV3() = default;
SettingNodeV3* SettingNodeSizeChangeEventV3::getTargetNode() const { SettingNodeV3* SettingNodeSizeChangeEventV3::getNode() const {
return m_impl->node; return m_impl->node;
} }
class SettingNodeValueChangeEventV3::Impl final { class SettingNodeValueChangeEventV3::Impl final {
public: public:
SettingNodeV3* node;
bool commit = false; bool commit = false;
}; };
SettingNodeValueChangeEventV3::SettingNodeValueChangeEventV3(bool commit) SettingNodeValueChangeEventV3::SettingNodeValueChangeEventV3(SettingNodeV3* node, bool commit)
: m_impl(std::make_shared<Impl>()) : m_impl(std::make_shared<Impl>())
{ {
m_impl->node = node;
m_impl->commit = commit; m_impl->commit = commit;
} }
SettingNodeValueChangeEventV3::~SettingNodeValueChangeEventV3() = default; SettingNodeValueChangeEventV3::~SettingNodeValueChangeEventV3() = default;
SettingNodeV3* SettingNodeValueChangeEventV3::getNode() const {
return m_impl->node;
}
bool SettingNodeValueChangeEventV3::isCommit() const { bool SettingNodeValueChangeEventV3::isCommit() const {
return m_impl->commit; return m_impl->commit;
} }
@ -43,7 +48,7 @@ public:
CCMenu* nameMenu; CCMenu* nameMenu;
CCMenu* buttonMenu; CCMenu* buttonMenu;
CCMenuItemSpriteExtra* resetButton; CCMenuItemSpriteExtra* resetButton;
ButtonSprite* restartRequiredLabel; CCLabelBMFont* errorLabel;
ccColor4B bgColor = ccc4(0, 0, 0, 0); ccColor4B bgColor = ccc4(0, 0, 0, 0);
bool committed = false; bool committed = false;
}; };
@ -68,15 +73,9 @@ bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) {
m_impl->nameLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .4f)->setScalePriority(1)); m_impl->nameLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .4f)->setScalePriority(1));
m_impl->nameMenu->addChild(m_impl->nameLabel); m_impl->nameMenu->addChild(m_impl->nameLabel);
m_impl->restartRequiredLabel = createGeodeTagLabel( m_impl->errorLabel = CCLabelBMFont::create("", "bigFont.fnt");
"Restart Required", m_impl->errorLabel->setScale(.25f);
{{ this->addChildAtPosition(m_impl->errorLabel, Anchor::Left, ccp(10, -10), ccp(0, .5f));
to3B(ColorProvider::get()->color("mod-list-restart-required-label"_spr)),
to3B(ColorProvider::get()->color("mod-list-restart-required-label-bg"_spr))
}}
);
m_impl->restartRequiredLabel->setScale(.25f);
this->addChildAtPosition(m_impl->restartRequiredLabel, Anchor::Left, ccp(10, -10), ccp(0, .5f));
if (setting->getDescription()) { if (setting->getDescription()) {
auto descSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png"); auto descSpr = CCSprite::createWithSpriteFrameName("GJ_infoIcon_001.png");
@ -110,15 +109,26 @@ bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) {
} }
void SettingNodeV3::updateState() { void SettingNodeV3::updateState() {
m_impl->errorLabel->setVisible(false);
m_impl->nameLabel->setColor(this->hasUncommittedChanges() ? ccc3(17, 221, 0) : ccWHITE); m_impl->nameLabel->setColor(this->hasUncommittedChanges() ? ccc3(17, 221, 0) : ccWHITE);
m_impl->resetButton->setVisible(this->hasNonDefaultValue()); m_impl->resetButton->setVisible(this->hasNonDefaultValue());
m_impl->bg->setColor(to3B(m_impl->bgColor)); m_impl->bg->setColor(to3B(m_impl->bgColor));
m_impl->bg->setOpacity(m_impl->bgColor.a); m_impl->bg->setOpacity(m_impl->bgColor.a);
m_impl->restartRequiredLabel->setVisible(false); if (!m_impl->setting->shouldEnable()) {
if (auto desc = m_impl->setting->getEnableIfDescription()) {
m_impl->nameLabel->setColor(ccGRAY);
m_impl->errorLabel->setVisible(true);
m_impl->errorLabel->setColor("mod-list-errors-found"_cc3b);
m_impl->errorLabel->setString(desc->c_str());
}
}
if (m_impl->setting->requiresRestart() && (this->hasUncommittedChanges() || m_impl->committed)) { if (m_impl->setting->requiresRestart() && (this->hasUncommittedChanges() || m_impl->committed)) {
m_impl->restartRequiredLabel->setVisible(true); m_impl->errorLabel->setVisible(true);
m_impl->errorLabel->setColor("mod-list-restart-required-label"_cc3b);
m_impl->errorLabel->setString("Restart Required");
m_impl->bg->setColor("mod-list-restart-required-label-bg"_cc3b); m_impl->bg->setColor("mod-list-restart-required-label-bg"_cc3b);
m_impl->bg->setOpacity(75); m_impl->bg->setOpacity(75);
} }
@ -159,19 +169,19 @@ void SettingNodeV3::setBGColor(ccColor4B const& color) {
void SettingNodeV3::markChanged() { void SettingNodeV3::markChanged() {
this->updateState(); this->updateState();
SettingNodeValueChangeEventV3(false).post(); SettingNodeValueChangeEventV3(this, false).post();
} }
void SettingNodeV3::commit() { void SettingNodeV3::commit() {
this->onCommit(); this->onCommit();
m_impl->committed = true; m_impl->committed = true;
this->updateState(); this->updateState();
SettingNodeValueChangeEventV3(true).post(); SettingNodeValueChangeEventV3(this, true).post();
} }
void SettingNodeV3::resetToDefault() { void SettingNodeV3::resetToDefault() {
m_impl->setting->reset(); m_impl->setting->reset();
this->onResetToDefault(); this->onResetToDefault();
this->updateState(); this->updateState();
SettingNodeValueChangeEventV3(false).post(); SettingNodeValueChangeEventV3(this, false).post();
} }
void SettingNodeV3::setContentSize(CCSize const& size) { void SettingNodeV3::setContentSize(CCSize const& size) {
@ -259,6 +269,16 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
return true; return true;
} }
void BoolSettingNodeV3::updateState() {
SettingNodeV3::updateState();
auto enable = this->getSetting()->shouldEnable();
m_toggle->setCascadeColorEnabled(true);
m_toggle->setCascadeOpacityEnabled(true);
m_toggle->setEnabled(enable);
m_toggle->setColor(enable ? ccWHITE : ccGRAY);
m_toggle->setOpacity(enable ? 255 : 155);
}
void BoolSettingNodeV3::onCommit() { void BoolSettingNodeV3::onCommit() {
this->getSetting()->setValue(m_toggle->isToggled()); this->getSetting()->setValue(m_toggle->isToggled());
} }
@ -311,19 +331,19 @@ bool StringSettingNodeV3::init(std::shared_ptr<StringSettingV3> setting, float w
m_input->getInputNode()->m_placeholderLabel->setOpacity(255); m_input->getInputNode()->m_placeholderLabel->setOpacity(255);
m_input->getInputNode()->m_placeholderLabel->setColor(ccWHITE); m_input->getInputNode()->m_placeholderLabel->setColor(ccWHITE);
auto arrowLeftSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png"); m_arrowLeftSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png");
arrowLeftSpr->setFlipX(true); m_arrowLeftSpr->setFlipX(true);
arrowLeftSpr->setScale(.4f); m_arrowLeftSpr->setScale(.4f);
auto arrowLeftBtn = CCMenuItemSpriteExtra::create( auto arrowLeftBtn = CCMenuItemSpriteExtra::create(
arrowLeftSpr, this, menu_selector(StringSettingNodeV3::onArrow) m_arrowLeftSpr, this, menu_selector(StringSettingNodeV3::onArrow)
); );
arrowLeftBtn->setTag(-1); arrowLeftBtn->setTag(-1);
this->getButtonMenu()->addChildAtPosition(arrowLeftBtn, Anchor::Left, ccp(5, 0)); this->getButtonMenu()->addChildAtPosition(arrowLeftBtn, Anchor::Left, ccp(5, 0));
auto arrowRightSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png"); m_arrowRightSpr = CCSprite::createWithSpriteFrameName("navArrowBtn_001.png");
arrowRightSpr->setScale(.4f); m_arrowRightSpr->setScale(.4f);
auto arrowRightBtn = CCMenuItemSpriteExtra::create( auto arrowRightBtn = CCMenuItemSpriteExtra::create(
arrowRightSpr, this, menu_selector(StringSettingNodeV3::onArrow) m_arrowRightSpr, this, menu_selector(StringSettingNodeV3::onArrow)
); );
arrowRightBtn->setTag(1); arrowRightBtn->setTag(1);
this->getButtonMenu()->addChildAtPosition(arrowRightBtn, Anchor::Right, ccp(-5, 0)); this->getButtonMenu()->addChildAtPosition(arrowRightBtn, Anchor::Right, ccp(-5, 0));
@ -334,6 +354,20 @@ bool StringSettingNodeV3::init(std::shared_ptr<StringSettingV3> setting, float w
return true; return true;
} }
void StringSettingNodeV3::updateState() {
SettingNodeV3::updateState();
auto enable = this->getSetting()->shouldEnable();
if (!this->getSetting()->getEnumOptions()) {
m_input->setEnabled(enable);
}
else {
m_arrowRightSpr->setOpacity(enable ? 255 : 155);
m_arrowRightSpr->setColor(enable ? ccWHITE : ccGRAY);
m_arrowLeftSpr->setOpacity(enable ? 255 : 155);
m_arrowLeftSpr->setColor(enable ? ccWHITE : ccGRAY);
}
}
void StringSettingNodeV3::onArrow(CCObject* sender) { void StringSettingNodeV3::onArrow(CCObject* sender) {
auto options = *this->getSetting()->getEnumOptions(); auto options = *this->getSetting()->getEnumOptions();
auto index = ranges::indexOf(options, m_input->getString()).value_or(0); auto index = ranges::indexOf(options, m_input->getString()).value_or(0);
@ -350,7 +384,6 @@ void StringSettingNodeV3::onArrow(CCObject* sender) {
void StringSettingNodeV3::onCommit() { void StringSettingNodeV3::onCommit() {
this->getSetting()->setValue(m_input->getString()); this->getSetting()->setValue(m_input->getString());
} }
bool StringSettingNodeV3::hasUncommittedChanges() const { bool StringSettingNodeV3::hasUncommittedChanges() const {
return m_input->getString() != this->getSetting()->getValue(); return m_input->getString() != this->getSetting()->getValue();
} }
@ -396,12 +429,12 @@ bool FileSettingNodeV3::init(std::shared_ptr<FileSettingV3> setting, float width
m_nameLabel = CCLabelBMFont::create("", "bigFont.fnt"); m_nameLabel = CCLabelBMFont::create("", "bigFont.fnt");
this->getButtonMenu()->addChildAtPosition(m_nameLabel, Anchor::Left, ccp(13, 0), ccp(0, .5f)); this->getButtonMenu()->addChildAtPosition(m_nameLabel, Anchor::Left, ccp(13, 0), ccp(0, .5f));
auto selectSpr = CCSprite::createWithSpriteFrameName("GJ_plus2Btn_001.png"); m_selectBtnSpr = CCSprite::createWithSpriteFrameName("GJ_plus2Btn_001.png");
selectSpr->setScale(.7f); m_selectBtnSpr->setScale(.7f);
auto selectBtn = CCMenuItemSpriteExtra::create( m_selectBtn = CCMenuItemSpriteExtra::create(
selectSpr, this, menu_selector(FileSettingNodeV3::onPickFile) m_selectBtnSpr, this, menu_selector(FileSettingNodeV3::onPickFile)
); );
this->getButtonMenu()->addChildAtPosition(selectBtn, Anchor::Right, ccp(-5, 0)); this->getButtonMenu()->addChildAtPosition(m_selectBtn, Anchor::Right, ccp(-5, 0));
this->updateState(); this->updateState();
@ -425,6 +458,11 @@ void FileSettingNodeV3::updateState() {
m_nameLabel->setOpacity(255); m_nameLabel->setOpacity(255);
} }
m_nameLabel->limitLabelWidth(75, .35f, .1f); m_nameLabel->limitLabelWidth(75, .35f, .1f);
auto enable = this->getSetting()->shouldEnable();
m_selectBtnSpr->setOpacity(enable ? 255 : 155);
m_selectBtnSpr->setColor(enable ? ccWHITE : ccGRAY);
m_selectBtn->setEnabled(enable);
} }
void FileSettingNodeV3::onPickFile(CCObject*) { void FileSettingNodeV3::onPickFile(CCObject*) {
@ -496,10 +534,10 @@ bool Color3BSettingNodeV3::init(std::shared_ptr<Color3BSettingV3> setting, float
m_colorSprite = ColorChannelSprite::create(); m_colorSprite = ColorChannelSprite::create();
m_colorSprite->setScale(.65f); m_colorSprite->setScale(.65f);
auto button = CCMenuItemSpriteExtra::create( m_colorBtn = CCMenuItemSpriteExtra::create(
m_colorSprite, this, menu_selector(Color3BSettingNodeV3::onSelectColor) m_colorSprite, this, menu_selector(Color3BSettingNodeV3::onSelectColor)
); );
this->getButtonMenu()->addChildAtPosition(button, Anchor::Right, ccp(-10, 0)); this->getButtonMenu()->addChildAtPosition(m_colorBtn, Anchor::Right, ccp(-10, 0));
this->updateState(); this->updateState();
@ -509,6 +547,10 @@ bool Color3BSettingNodeV3::init(std::shared_ptr<Color3BSettingV3> setting, float
void Color3BSettingNodeV3::updateState() { void Color3BSettingNodeV3::updateState() {
SettingNodeV3::updateState(); SettingNodeV3::updateState();
m_colorSprite->setColor(m_value); m_colorSprite->setColor(m_value);
auto enable = this->getSetting()->shouldEnable();
m_colorSprite->setOpacity(enable ? 255 : 155);
m_colorBtn->setEnabled(enable);
} }
void Color3BSettingNodeV3::onSelectColor(CCObject*) { void Color3BSettingNodeV3::onSelectColor(CCObject*) {
@ -559,10 +601,10 @@ bool Color4BSettingNodeV3::init(std::shared_ptr<Color4BSettingV3> setting, float
m_colorSprite = ColorChannelSprite::create(); m_colorSprite = ColorChannelSprite::create();
m_colorSprite->setScale(.65f); m_colorSprite->setScale(.65f);
auto button = CCMenuItemSpriteExtra::create( m_colorBtn = CCMenuItemSpriteExtra::create(
m_colorSprite, this, menu_selector(Color4BSettingNodeV3::onSelectColor) m_colorSprite, this, menu_selector(Color4BSettingNodeV3::onSelectColor)
); );
this->getButtonMenu()->addChildAtPosition(button, Anchor::Right, ccp(-10, 0)); this->getButtonMenu()->addChildAtPosition(m_colorBtn, Anchor::Right, ccp(-10, 0));
this->updateState(); this->updateState();
@ -573,6 +615,10 @@ void Color4BSettingNodeV3::updateState() {
SettingNodeV3::updateState(); SettingNodeV3::updateState();
m_colorSprite->setColor(to3B(m_value)); m_colorSprite->setColor(to3B(m_value));
m_colorSprite->updateOpacity(m_value.a / 255.f); m_colorSprite->updateOpacity(m_value.a / 255.f);
auto enable = this->getSetting()->shouldEnable();
m_colorSprite->setOpacity(enable ? 255 : 155);
m_colorBtn->setEnabled(enable);
} }
void Color4BSettingNodeV3::onSelectColor(CCObject*) { void Color4BSettingNodeV3::onSelectColor(CCObject*) {
@ -672,10 +718,10 @@ bool LegacyCustomSettingToV3Node::init(std::shared_ptr<LegacyCustomSettingV3> or
} }
void LegacyCustomSettingToV3Node::settingValueChanged(SettingNode*) { void LegacyCustomSettingToV3Node::settingValueChanged(SettingNode*) {
SettingNodeValueChangeEventV3(false).post(); SettingNodeValueChangeEventV3(this, false).post();
} }
void LegacyCustomSettingToV3Node::settingValueCommitted(SettingNode*) { void LegacyCustomSettingToV3Node::settingValueCommitted(SettingNode*) {
SettingNodeValueChangeEventV3(true).post(); SettingNodeValueChangeEventV3(this, true).post();
} }
void LegacyCustomSettingToV3Node::onCommit() { void LegacyCustomSettingToV3Node::onCommit() {

View file

@ -33,6 +33,8 @@ protected:
bool init(std::shared_ptr<BoolSettingV3> setting, float width); bool init(std::shared_ptr<BoolSettingV3> setting, float width);
void updateState() override;
void onCommit() override; void onCommit() override;
void onToggle(CCObject*); void onToggle(CCObject*);
@ -57,6 +59,10 @@ protected:
CCMenuItemSpriteExtra* m_bigArrowLeftBtn; CCMenuItemSpriteExtra* m_bigArrowLeftBtn;
CCMenuItemSpriteExtra* m_arrowRightBtn; CCMenuItemSpriteExtra* m_arrowRightBtn;
CCMenuItemSpriteExtra* m_bigArrowRightBtn; CCMenuItemSpriteExtra* m_bigArrowRightBtn;
CCSprite* m_arrowLeftBtnSpr;
CCSprite* m_bigArrowLeftBtnSpr;
CCSprite* m_arrowRightBtnSpr;
CCSprite* m_bigArrowRightBtnSpr;
float valueToSlider(ValueType value) { float valueToSlider(ValueType value) {
auto min = this->getSetting()->getMinValue().value_or(-100); auto min = this->getSetting()->getMinValue().value_or(-100);
@ -79,28 +85,28 @@ protected:
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
auto bigArrowLeftSpr = CCSprite::create(); m_bigArrowLeftBtnSpr = CCSprite::create();
bigArrowLeftSpr->setCascadeColorEnabled(true); m_bigArrowLeftBtnSpr->setCascadeColorEnabled(true);
bigArrowLeftSpr->setCascadeOpacityEnabled(true); m_bigArrowLeftBtnSpr->setCascadeOpacityEnabled(true);
auto bigArrowLeftSpr1 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); auto bigArrowLeftSpr1 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png");
auto bigArrowLeftSpr2 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); auto bigArrowLeftSpr2 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png");
m_bigArrowLeftBtnSpr->setContentSize(bigArrowLeftSpr1->getContentSize() + ccp(20, 0));
bigArrowLeftSpr->setContentSize(bigArrowLeftSpr1->getContentSize() + ccp(20, 0)); m_bigArrowLeftBtnSpr->addChildAtPosition(bigArrowLeftSpr2, Anchor::Center, ccp(10, 0));
bigArrowLeftSpr->addChildAtPosition(bigArrowLeftSpr2, Anchor::Center, ccp(10, 0)); m_bigArrowLeftBtnSpr->addChildAtPosition(bigArrowLeftSpr1, Anchor::Center, ccp(-10, 0));
bigArrowLeftSpr->addChildAtPosition(bigArrowLeftSpr1, Anchor::Center, ccp(-10, 0)); m_bigArrowLeftBtnSpr->setScale(.3f);
bigArrowLeftSpr->setScale(.3f);
m_bigArrowLeftBtn = CCMenuItemSpriteExtra::create( m_bigArrowLeftBtn = CCMenuItemSpriteExtra::create(
bigArrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow) m_bigArrowLeftBtnSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
m_bigArrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getBigArrowStepSize())); m_bigArrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getBigArrowStepSize()));
m_bigArrowLeftBtn->setVisible(setting->isBigArrowsEnabled()); m_bigArrowLeftBtn->setVisible(setting->isBigArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(m_bigArrowLeftBtn, Anchor::Left, ccp(5, 0)); this->getButtonMenu()->addChildAtPosition(m_bigArrowLeftBtn, Anchor::Left, ccp(5, 0));
auto arrowLeftSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"); m_arrowLeftBtnSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png");
arrowLeftSpr->setScale(.5f); m_arrowLeftBtnSpr->setScale(.5f);
m_arrowLeftBtn = CCMenuItemSpriteExtra::create( m_arrowLeftBtn = CCMenuItemSpriteExtra::create(
arrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow) m_arrowLeftBtnSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
m_arrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getArrowStepSize())); m_arrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getArrowStepSize()));
m_arrowLeftBtn->setVisible(setting->isArrowsEnabled()); m_arrowLeftBtn->setVisible(setting->isArrowsEnabled());
@ -119,31 +125,31 @@ protected:
} }
this->getButtonMenu()->addChildAtPosition(m_input, Anchor::Center); this->getButtonMenu()->addChildAtPosition(m_input, Anchor::Center);
auto arrowRightSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"); m_arrowRightBtnSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png");
arrowRightSpr->setFlipX(true); m_arrowRightBtnSpr->setFlipX(true);
arrowRightSpr->setScale(.5f); m_arrowRightBtnSpr->setScale(.5f);
m_arrowRightBtn = CCMenuItemSpriteExtra::create( m_arrowRightBtn = CCMenuItemSpriteExtra::create(
arrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow) m_arrowRightBtnSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
m_arrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getArrowStepSize())); m_arrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getArrowStepSize()));
m_arrowRightBtn->setVisible(setting->isArrowsEnabled()); m_arrowRightBtn->setVisible(setting->isArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(m_arrowRightBtn, Anchor::Right, ccp(-22, 0)); this->getButtonMenu()->addChildAtPosition(m_arrowRightBtn, Anchor::Right, ccp(-22, 0));
auto bigArrowRightSpr = CCSprite::create(); m_bigArrowRightBtnSpr = CCSprite::create();
bigArrowRightSpr->setCascadeColorEnabled(true); m_bigArrowRightBtnSpr->setCascadeColorEnabled(true);
bigArrowRightSpr->setCascadeOpacityEnabled(true); m_bigArrowRightBtnSpr->setCascadeOpacityEnabled(true);
auto bigArrowRightSpr1 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); auto bigArrowRightSpr1 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png");
bigArrowRightSpr1->setFlipX(true); bigArrowRightSpr1->setFlipX(true);
auto bigArrowRightSpr2 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png"); auto bigArrowRightSpr2 = CCSprite::createWithSpriteFrameName("GJ_arrow_03_001.png");
bigArrowRightSpr2->setFlipX(true); bigArrowRightSpr2->setFlipX(true);
bigArrowRightSpr->setContentSize(bigArrowRightSpr1->getContentSize() + ccp(20, 0)); m_bigArrowRightBtnSpr->setContentSize(bigArrowRightSpr1->getContentSize() + ccp(20, 0));
bigArrowRightSpr->addChildAtPosition(bigArrowRightSpr1, Anchor::Center, ccp(-10, 0)); m_bigArrowRightBtnSpr->addChildAtPosition(bigArrowRightSpr1, Anchor::Center, ccp(-10, 0));
bigArrowRightSpr->addChildAtPosition(bigArrowRightSpr2, Anchor::Center, ccp(10, 0)); m_bigArrowRightBtnSpr->addChildAtPosition(bigArrowRightSpr2, Anchor::Center, ccp(10, 0));
bigArrowRightSpr->setScale(.3f); m_bigArrowRightBtnSpr->setScale(.3f);
m_bigArrowRightBtn = CCMenuItemSpriteExtra::create( m_bigArrowRightBtn = CCMenuItemSpriteExtra::create(
bigArrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow) m_bigArrowRightBtnSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
m_bigArrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getBigArrowStepSize())); m_bigArrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getBigArrowStepSize()));
m_bigArrowRightBtn->setVisible(setting->isBigArrowsEnabled()); m_bigArrowRightBtn->setVisible(setting->isBigArrowsEnabled());
@ -166,27 +172,35 @@ protected:
void updateState() override { void updateState() override {
SettingNodeV3::updateState(); SettingNodeV3::updateState();
auto enable = this->getSetting()->shouldEnable();
if (this->getSetting()->isInputEnabled()) {
m_input->setEnabled(enable);
}
auto min = this->getSetting()->getMinValue();
auto enableLeft = enable && (!min || this->getCurrentValue() > *min);
m_arrowLeftBtn->setEnabled(enableLeft);
m_bigArrowLeftBtn->setEnabled(enableLeft);
m_arrowLeftBtnSpr->setOpacity(enableLeft ? 255 : 155);
m_arrowLeftBtnSpr->setColor(enableLeft ? ccWHITE : ccGRAY);
m_bigArrowLeftBtnSpr->setOpacity(enableLeft ? 255 : 155);
m_bigArrowLeftBtnSpr->setColor(enableLeft ? ccWHITE : ccGRAY);
auto max = this->getSetting()->getMaxValue();
auto enableRight = enable && (!max || this->getCurrentValue() < *max);
m_arrowRightBtn->setEnabled(enableRight);
m_bigArrowRightBtn->setEnabled(enableRight);
m_arrowRightBtnSpr->setOpacity(enableRight ? 255 : 155);
m_arrowRightBtnSpr->setColor(enableRight ? ccWHITE : ccGRAY);
m_bigArrowRightBtnSpr->setOpacity(enableRight ? 255 : 155);
m_bigArrowRightBtnSpr->setColor(enableRight ? ccWHITE : ccGRAY);
if (m_slider) { if (m_slider) {
m_slider->m_touchLogic->m_thumb->setValue(this->valueToSlider(this->getCurrentValue())); m_slider->m_touchLogic->m_thumb->setValue(this->valueToSlider(this->getCurrentValue()));
m_slider->updateBar(); m_slider->updateBar();
} m_slider->m_sliderBar->setColor(enable ? ccWHITE : ccGRAY);
if (auto min = this->getSetting()->getMinValue()) { m_slider->m_touchLogic->m_thumb->setColor(enable ? ccWHITE : ccGRAY);
auto enable = this->getCurrentValue() > *min; m_slider->m_touchLogic->m_thumb->setEnabled(enable);
m_arrowLeftBtn->setEnabled(enable);
m_bigArrowLeftBtn->setEnabled(enable);
static_cast<CCSprite*>(m_arrowLeftBtn->getNormalImage())->setOpacity(enable ? 255 : 155);
static_cast<CCSprite*>(m_arrowLeftBtn->getNormalImage())->setColor(enable ? ccWHITE : ccGRAY);
static_cast<CCSprite*>(m_bigArrowLeftBtn->getNormalImage())->setOpacity(enable ? 255 : 155);
static_cast<CCSprite*>(m_bigArrowLeftBtn->getNormalImage())->setColor(enable ? ccWHITE : ccGRAY);
}
if (auto max = this->getSetting()->getMaxValue()) {
auto enable = this->getCurrentValue() < *max;
m_arrowRightBtn->setEnabled(enable);
m_bigArrowRightBtn->setEnabled(enable);
static_cast<CCSprite*>(m_arrowRightBtn->getNormalImage())->setOpacity(enable ? 255 : 155);
static_cast<CCSprite*>(m_arrowRightBtn->getNormalImage())->setColor(enable ? ccWHITE : ccGRAY);
static_cast<CCSprite*>(m_bigArrowRightBtn->getNormalImage())->setOpacity(enable ? 255 : 155);
static_cast<CCSprite*>(m_bigArrowRightBtn->getNormalImage())->setColor(enable ? ccWHITE : ccGRAY);
} }
} }
@ -250,13 +264,17 @@ using FloatSettingNodeV3 = NumberSettingNodeV3<FloatSettingV3>;
class StringSettingNodeV3 : public SettingNodeV3 { class StringSettingNodeV3 : public SettingNodeV3 {
protected: protected:
TextInput* m_input; TextInput* m_input;
CCSprite* m_arrowLeftSpr = nullptr;
CCSprite* m_arrowRightSpr = nullptr;
bool init(std::shared_ptr<StringSettingV3> setting, float width); bool init(std::shared_ptr<StringSettingV3> setting, float width);
void onCommit() override; void updateState() override;
void onArrow(CCObject* sender); void onArrow(CCObject* sender);
void onCommit() override;
public: public:
static StringSettingNodeV3* create(std::shared_ptr<StringSettingV3> setting, float width); static StringSettingNodeV3* create(std::shared_ptr<StringSettingV3> setting, float width);
@ -273,6 +291,8 @@ protected:
std::filesystem::path m_path; std::filesystem::path m_path;
CCLabelBMFont* m_nameLabel; CCLabelBMFont* m_nameLabel;
EventListener<Task<Result<std::filesystem::path>>> m_pickListener; EventListener<Task<Result<std::filesystem::path>>> m_pickListener;
CCMenuItemSpriteExtra* m_selectBtn;
CCSprite* m_selectBtnSpr;
bool init(std::shared_ptr<FileSettingV3> setting, float width); bool init(std::shared_ptr<FileSettingV3> setting, float width);
@ -294,6 +314,7 @@ public:
class Color3BSettingNodeV3 : public SettingNodeV3, public ColorPickPopupDelegate { class Color3BSettingNodeV3 : public SettingNodeV3, public ColorPickPopupDelegate {
protected: protected:
ccColor3B m_value; ccColor3B m_value;
CCMenuItemSpriteExtra* m_colorBtn;
ColorChannelSprite* m_colorSprite; ColorChannelSprite* m_colorSprite;
bool init(std::shared_ptr<Color3BSettingV3> setting, float width); bool init(std::shared_ptr<Color3BSettingV3> setting, float width);
@ -317,6 +338,7 @@ public:
class Color4BSettingNodeV3 : public SettingNodeV3, public ColorPickPopupDelegate { class Color4BSettingNodeV3 : public SettingNodeV3, public ColorPickPopupDelegate {
protected: protected:
ccColor4B m_value; ccColor4B m_value;
CCMenuItemSpriteExtra* m_colorBtn;
ColorChannelSprite* m_colorSprite; ColorChannelSprite* m_colorSprite;
bool init(std::shared_ptr<Color4BSettingV3> setting, float width); bool init(std::shared_ptr<Color4BSettingV3> setting, float width);

View file

@ -5,6 +5,398 @@
using namespace geode::prelude; using namespace geode::prelude;
namespace enable_if_parsing {
struct Component {
virtual ~Component() = default;
virtual Result<> check() const = 0;
virtual Result<> eval(std::string const& defaultModID) const = 0;
};
struct RequireModLoaded final : public Component {
std::string modID;
RequireModLoaded(std::string const& modID)
: modID(modID) {}
Result<> check() const override {
return Ok();
}
Result<> eval(std::string const& defaultModID) const override {
if (Loader::get()->getLoadedMod(modID)) {
return Ok();
}
auto modName = modID;
if (auto mod = Loader::get()->getInstalledMod(modID)) {
modName = mod->getName();
}
return Err("Enable the mod {}", modName);
}
};
struct RequireSettingEnabled final : public Component {
std::string modID;
std::string settingID;
RequireSettingEnabled(std::string const& modID, std::string const& settingID)
: modID(modID), settingID(settingID) {}
Result<> check() const override {
if (auto mod = Loader::get()->getInstalledMod(modID)) {
if (!mod->hasSetting(settingID)) {
return Err("Mod '{}' does not have setting '{}'", mod->getName(), settingID);
}
if (!typeinfo_pointer_cast<BoolSettingV3>(mod->getSettingV3(settingID))) {
return Err("Setting '{}' in mod '{}' is not a boolean setting", settingID, mod->getName());
}
}
return Ok();
}
Result<> eval(std::string const& defaultModID) const override {
if (auto mod = Loader::get()->getLoadedMod(modID)) {
if (mod->template getSettingValue<bool>(settingID)) {
return Ok();
}
// This is an if-check just in case, even though check() should already
// make sure that getSettingV3 is guaranteed to return true
auto name = settingID;
if (auto sett = mod->getSettingV3(settingID)) {
name = sett->getDisplayName();
}
if (modID == defaultModID) {
return Err("Enable the setting '{}'", name);
}
return Err("Enable the setting '{}' from the mod {}", name, mod->getName());
}
auto modName = modID;
if (auto mod = Loader::get()->getInstalledMod(modID)) {
modName = mod->getName();
}
return Err("Enable the mod {}", modName);
}
};
struct RequireSavedValueEnabled final : public Component {
std::string modID;
std::string savedValue;
RequireSavedValueEnabled(std::string const& modID, std::string const& savedValue)
: modID(modID), savedValue(savedValue) {}
Result<> check() const override {
return Ok();
}
Result<> eval(std::string const& defaultModID) const override {
if (auto mod = Loader::get()->getLoadedMod(modID)) {
if (mod->template getSavedValue<bool>(savedValue)) {
return Ok();
}
if (modID == defaultModID) {
return Err("Enable the value '{}'", savedValue);
}
return Err("Enable the value '{}' from the mod {}", savedValue, mod->getName());
}
auto modName = modID;
if (auto mod = Loader::get()->getInstalledMod(modID)) {
modName = mod->getName();
}
return Err("Enable the mod {}", modName);
}
};
struct RequireNot final : public Component {
std::unique_ptr<Component> component;
RequireNot(std::unique_ptr<Component>&& component)
: component(std::move(component)) {}
Result<> check() const override {
return component->check();
}
Result<> eval(std::string const& defaultModID) const override {
if (auto res = component->eval(defaultModID)) {
// Surely this will never break!
auto str = res.unwrapErr();
string::replaceIP(str, "Enable", "___TEMP");
string::replaceIP(str, "Disable", "Enable");
string::replaceIP(str, "___TEMP", "Disable");
return Err(str);
}
return Ok();
}
};
struct RequireAll final : public Component {
std::vector<std::unique_ptr<Component>> components;
RequireAll(std::vector<std::unique_ptr<Component>>&& components)
: components(std::move(components)) {}
Result<> check() const override {
for (auto& comp : components) {
GEODE_UNWRAP(comp->check());
}
return Ok();
}
Result<> eval(std::string const& defaultModID) const override {
// Only print out whatever the first erroring condition is to not shit out
// "Please enable X and Y and Z and Ö and Å and"
for (auto& comp : components) {
GEODE_UNWRAP(comp->eval(defaultModID));
}
return Ok();
}
};
struct RequireSome final : public Component {
std::vector<std::unique_ptr<Component>> components;
RequireSome(std::vector<std::unique_ptr<Component>>&& components)
: components(std::move(components)) {}
Result<> check() const override {
for (auto& comp : components) {
GEODE_UNWRAP(comp->check());
}
return Ok();
}
Result<> eval(std::string const& defaultModID) const override {
Result<> err = Ok();
for (auto& comp : components) {
auto res = comp->eval(defaultModID);
if (res) {
return Ok();
}
// Only show first condition that isn't met
if (err.isOk()) {
err = Err(res.unwrapErr());
}
}
return err;
}
};
static bool isComponentStartChar(char c) {
return
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
c == '_';
}
static bool isComponentContinueChar(char c) {
return
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') ||
c == '_' || c == '-' || c == '/' ||
c == '.' || c == ':';
}
class Parser final {
private:
std::string_view m_src;
size_t m_index = 0;
std::string m_defaultModID;
static bool isUnOpWord(std::string_view op) {
return op == "!";
}
static bool isBiOpWord(std::string_view op) {
return op == "&&" || op == "||";
}
Result<std::optional<std::string_view>> nextWord() {
// Skip whitespace
while (m_index < m_src.size() && std::isspace(m_src[m_index])) {
m_index += 1;
}
if (m_index == m_src.size()) {
return Ok(std::nullopt);
}
// Parentheses & single operators
if (m_src[m_index] == '(' || m_src[m_index] == ')' || m_src[m_index] == '!') {
m_index += 1;
return Ok(m_src.substr(m_index - 1, 1));
}
// Double-character operators
if (m_src[m_index] == '&' || m_src[m_index] == '|') {
// Consume first character
m_index += 1;
// Next character must be the same
if (m_index == m_src.size() || m_src[m_index - 1] != m_src[m_index]) {
return Err("Expected '{}' at index {}", m_src[m_index - 1], m_index - 1);
}
// Consume second character
m_index += 1;
return Ok(m_src.substr(m_index - 2, 2));
}
// Components
if (isComponentStartChar(m_src[m_index])) {
auto start = m_index;
m_index += 1;
while (m_index < m_src.size() && isComponentContinueChar(m_src[m_index])) {
m_index += 1;
}
return Ok(m_src.substr(start, m_index - start));
}
return Err("Unexpected character '{}' at index {}", m_src[m_index], m_index);
}
std::optional<std::string_view> peekWord() {
auto original = m_index;
auto ret = this->nextWord();
m_index = original;
return ret ? *ret : std::nullopt;
}
Result<std::unique_ptr<Component>> nextComponent() {
GEODE_UNWRAP_INTO(auto maybeWord, this->nextWord());
if (!maybeWord) {
return Err("Expected component, got end-of-enable-if-string");
}
const auto word = *maybeWord;
if (isUnOpWord(word) || isBiOpWord(word)) {
return Err("Expected component, got operator \"{}\" at index {}", word, m_index - word.size());
}
if (word == ")") {
return Err("Unexpected closing parenthesis at index {}", m_index - 1);
}
if (word == "(") {
GEODE_UNWRAP_INTO(auto op, this->next());
GEODE_UNWRAP_INTO(auto maybeClosing, this->nextWord());
if (!maybeClosing) {
return Err("Expected closing parenthesis, got end-of-enable-if-string");
}
if (maybeClosing != ")") {
return Err(
"Expected closing parenthesis, got \"{}\" at index {}",
*maybeClosing, m_index - maybeClosing->size()
);
}
return Ok(std::move(op));
}
std::string_view ty = "setting";
std::string_view value = word;
if (word.find(':') != std::string::npos) {
ty = word.substr(0, word.find(':'));
value = word.substr(word.find(':') + 1);
}
switch (hash(ty)) {
case hash("setting"): {
std::string modID = m_defaultModID;
std::string settingID = std::string(value);
// mod.id/setting-id
if (value.find('/') != std::string::npos) {
modID = value.substr(0, value.find('/'));
settingID = value.substr(value.find('/') + 1);
}
if (!ModMetadata::validateID(std::string(modID))) {
return Err("Invalid mod ID '{}'", modID);
}
return Ok(std::make_unique<RequireSettingEnabled>(modID, settingID));
} break;
case hash("saved"): {
std::string modID = m_defaultModID;
std::string savedValue = std::string(value);
// mod.id/setting-id
if (value.find('/') != std::string::npos) {
modID = value.substr(0, value.find('/'));
savedValue = value.substr(value.find('/') + 1);
}
if (!ModMetadata::validateID(std::string(modID))) {
return Err("Invalid mod ID '{}'", modID);
}
return Ok(std::make_unique<RequireSavedValueEnabled>(modID, savedValue));
} break;
case hash("loaded"): {
if (!ModMetadata::validateID(std::string(value))) {
return Err("Invalid mod ID '{}'", value);
}
return Ok(std::make_unique<RequireModLoaded>(std::string(value)));
} break;
default: {
return Err("Invalid designator '{}' at index {}", ty, m_index - word.size());
} break;
}
}
Result<std::unique_ptr<Component>> nextUnOp() {
std::string op;
if (auto peek = this->peekWord()) {
if (isUnOpWord(*peek)) {
op = *peek;
}
}
GEODE_UNWRAP_INTO(auto comp, this->nextComponent());
if (op.empty()) {
return Ok(std::move(comp));
}
switch (hash(op)) {
case hash("!"): {
return Ok(std::make_unique<RequireNot>(std::move(comp)));
} break;
default: {
return Err(
"THIS SHOULD BE UNREACHABLE!! \"{}\" was an unhandled "
"unary operator despite isUnOpWord claiming it's valid! "
"REPORT THIS BUG TO GEODE DEVELOPERS",
op
);
} break;
}
}
Result<std::unique_ptr<Component>> nextBiOp() {
GEODE_UNWRAP_INTO(auto first, this->nextUnOp());
std::string firstOp;
std::vector<std::unique_ptr<Component>> components;
while (auto peek = this->peekWord()) {
if (!isBiOpWord(*peek)) {
break;
}
GEODE_UNWRAP_INTO(auto word, this->nextWord());
auto op = *word;
if (firstOp.empty()) {
firstOp = op;
}
if (op != firstOp) {
return Err(
"Expected operator \"{}\", got operator \"{}\" - "
"parentheses are required to disambiguate operator chains",
firstOp, op
);
}
GEODE_UNWRAP_INTO(auto comp, this->nextUnOp());
components.emplace_back(std::move(comp));
}
if (components.size()) {
components.emplace(components.begin(), std::move(first));
switch (hash(firstOp)) {
case hash("&&"): {
return Ok(std::make_unique<RequireAll>(std::move(components)));
} break;
case hash("||"): {
return Ok(std::make_unique<RequireSome>(std::move(components)));
} break;
default: {
return Err(
"THIS SHOULD BE UNREACHABLE!! \"{}\" was an unhandled "
"binary operator despite isBiOpWord claiming it's valid! "
"REPORT THIS BUG TO GEODE DEVELOPERS",
firstOp
);
} break;
}
}
return Ok(std::move(first));
}
Result<std::unique_ptr<Component>> next() {
return this->nextBiOp();
}
public:
static Result<std::unique_ptr<Component>> parse(std::string_view str, std::string const& defaultModID) {
auto ret = Parser();
ret.m_src = str;
ret.m_defaultModID = defaultModID;
GEODE_UNWRAP_INTO(auto comp, ret.next());
GEODE_UNWRAP_INTO(auto shouldBeEOF, ret.nextWord());
if (shouldBeEOF) {
return Err(
"Expected end-of-enable-if-string, got \"{}\" at index {}",
*shouldBeEOF, ret.m_index - shouldBeEOF->size()
);
}
return Ok(std::move(comp));
}
};
}
class SettingV3::GeodeImpl { class SettingV3::GeodeImpl {
public: public:
std::string modID; std::string modID;
@ -12,6 +404,8 @@ public:
std::optional<std::string> name; std::optional<std::string> name;
std::optional<std::string> description; std::optional<std::string> description;
std::optional<std::string> enableIf; std::optional<std::string> enableIf;
std::unique_ptr<enable_if_parsing::Component> enableIfTree;
std::optional<std::string> enableIfDescription;
bool requiresRestart = false; bool requiresRestart = false;
}; };
@ -30,8 +424,16 @@ void SettingV3::parseSharedProperties(std::string const& key, std::string const&
value.has("name").into(m_impl->name); value.has("name").into(m_impl->name);
value.has("description").into(m_impl->description); value.has("description").into(m_impl->description);
if (!onlyNameAndDesc) { if (!onlyNameAndDesc) {
value.has("enable-if").into(m_impl->enableIf);
value.has("requires-restart").into(m_impl->requiresRestart); value.has("requires-restart").into(m_impl->requiresRestart);
value.has("enable-if")
.template mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID));
GEODE_UNWRAP(tree->check());
m_impl->enableIfTree = std::move(tree);
return Ok();
})
.into(m_impl->enableIf);
value.has("enable-if-description").into(m_impl->enableIfDescription);
} }
} }
void SettingV3::init(std::string const& key, std::string const& modID) { void SettingV3::init(std::string const& key, std::string const& modID) {
@ -57,6 +459,25 @@ std::optional<std::string> SettingV3::getDescription() const {
std::optional<std::string> SettingV3::getEnableIf() const { std::optional<std::string> SettingV3::getEnableIf() const {
return m_impl->enableIf; return m_impl->enableIf;
} }
bool SettingV3::shouldEnable() const {
if (m_impl->enableIfTree) {
return m_impl->enableIfTree->eval(m_impl->modID).isOk();
}
return true;
}
std::optional<std::string> SettingV3::getEnableIfDescription() const {
if (m_impl->enableIfDescription) {
return *m_impl->enableIfDescription;
}
if (!m_impl->enableIfTree) {
return std::nullopt;
}
auto res = m_impl->enableIfTree->eval(m_impl->modID);
if (res) {
return std::nullopt;
}
return res.unwrapErr();
}
bool SettingV3::requiresRestart() const { bool SettingV3::requiresRestart() const {
return m_impl->requiresRestart; return m_impl->requiresRestart;
} }

View file

@ -87,8 +87,8 @@ bool ModSettingsPopup::setup(Mod* mod) {
); );
m_buttonMenu->addChildAtPosition(openDirBtn, Anchor::BottomRight, ccp(-53, 20)); m_buttonMenu->addChildAtPosition(openDirBtn, Anchor::BottomRight, ccp(-53, 20));
m_changeListener.bind([this](auto) { m_changeListener.bind([this](auto* ev) {
this->updateState(); this->updateState(ev->getNode());
return ListenerResult::Propagate; return ListenerResult::Propagate;
}); });
this->updateState(); this->updateState();
@ -125,7 +125,17 @@ void ModSettingsPopup::onResetAll(CCObject*) {
); );
} }
void ModSettingsPopup::updateState() { void ModSettingsPopup::updateState(SettingNodeV3* invoker) {
// Update all settings with "enable-if" schemes
for (auto& sett : m_settings) {
// Avoid infinite loops
if (sett == invoker) {
continue;
}
if (sett->getSetting()->getEnableIf()) {
sett->updateState();
}
}
m_applyBtnSpr->setCascadeColorEnabled(true); m_applyBtnSpr->setCascadeColorEnabled(true);
m_applyBtnSpr->setCascadeOpacityEnabled(true); m_applyBtnSpr->setCascadeOpacityEnabled(true);
if (this->hasUncommitted()) { if (this->hasUncommitted()) {

View file

@ -16,8 +16,7 @@ protected:
EventListener<EventFilter<SettingNodeValueChangeEventV3>> m_changeListener; EventListener<EventFilter<SettingNodeValueChangeEventV3>> m_changeListener;
bool setup(Mod* mod) override; bool setup(Mod* mod) override;
void updateState(); void updateState(SettingNodeV3* invoker = nullptr);
void onChangeEvent(SettingNodeValueChangeEventV3* event);
bool hasUncommitted() const; bool hasUncommitted() const;
void onClose(CCObject*) override; void onClose(CCObject*) override;
void onApply(CCObject*); void onApply(CCObject*);