mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-22 15:37:53 -05:00
fully implement enable-if!!!!
This commit is contained in:
parent
4fb42754cb
commit
db9e2ccb48
7 changed files with 608 additions and 88 deletions
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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 --
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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*);
|
||||||
|
|
Loading…
Reference in a new issue