path settings done

This commit is contained in:
HJfod 2024-08-24 00:33:38 +03:00
parent 3f3b52104f
commit 766b71dfbd
4 changed files with 252 additions and 84 deletions

View file

@ -267,8 +267,8 @@ namespace geode {
bool isArrowsEnabled() const; bool isArrowsEnabled() const;
bool isBigArrowsEnabled() const; bool isBigArrowsEnabled() const;
size_t getArrowStepSize() const; double getArrowStepSize() const;
size_t getBigArrowStepSize() const; double getBigArrowStepSize() const;
bool isSliderEnabled() const; bool isSliderEnabled() const;
std::optional<double> getSliderSnap() const; std::optional<double> getSliderSnap() const;
bool isInputEnabled() const; bool isInputEnabled() const;
@ -328,9 +328,17 @@ namespace geode {
FileSettingV3(PrivateMarker); FileSettingV3(PrivateMarker);
static Result<std::shared_ptr<FileSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json); static Result<std::shared_ptr<FileSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
enum class FileType {
Any = 0,
File = 1,
Folder = 2,
};
std::filesystem::path getDefaultValue() const override; std::filesystem::path getDefaultValue() const override;
Result<> isValid(std::filesystem::path const& value) const override; Result<> isValid(std::filesystem::path const& value) const override;
FileType getFileType() const;
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const; std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
bool load(matjson::Value const& json) override; bool load(matjson::Value const& json) override;

View file

@ -73,6 +73,7 @@ bool SettingNodeV3::init(std::shared_ptr<SettingV3> setting, float width) {
m_impl->nameMenu->addChild(m_impl->resetButton); m_impl->nameMenu->addChild(m_impl->resetButton);
m_impl->nameMenu->setLayout(RowLayout::create()->setAxisAlignment(AxisAlignment::Start)); m_impl->nameMenu->setLayout(RowLayout::create()->setAxisAlignment(AxisAlignment::Start));
m_impl->nameMenu->getLayout()->ignoreInvisibleChildren(true);
this->addChildAtPosition(m_impl->nameMenu, Anchor::Left, ccp(10, 0), ccp(0, .5f)); this->addChildAtPosition(m_impl->nameMenu, Anchor::Left, ccp(10, 0), ccp(0, .5f));
m_impl->buttonMenu = CCMenu::create(); m_impl->buttonMenu = CCMenu::create();
@ -198,6 +199,10 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
this->getButtonMenu()->setContentWidth(20);
this->getNameMenu()->setContentWidth(width - 50);
this->getNameMenu()->updateLayout();
m_toggle = CCMenuItemToggler::createWithStandardSprites( m_toggle = CCMenuItemToggler::createWithStandardSprites(
this, menu_selector(BoolSettingNodeV3::onToggle), .55f this, menu_selector(BoolSettingNodeV3::onToggle), .55f
); );
@ -209,9 +214,6 @@ bool BoolSettingNodeV3::init(std::shared_ptr<BoolSettingV3> setting, float width
m_toggle->toggle(setting->getValue()); m_toggle->toggle(setting->getValue());
this->getButtonMenu()->addChildAtPosition(m_toggle, Anchor::Right, ccp(-10, 0)); this->getButtonMenu()->addChildAtPosition(m_toggle, Anchor::Right, ccp(-10, 0));
this->getNameMenu()->setContentWidth(width - 50);
this->getNameMenu()->updateLayout();
this->updateState(); this->updateState();
return true; return true;
@ -255,7 +257,7 @@ bool StringSettingNodeV3::init(std::shared_ptr<StringSettingV3> setting, float w
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
m_input = TextInput::create(width / 2 - 50, "Num"); m_input = TextInput::create(setting->getEnumOptions() ? width / 2 - 50 : width / 2, "Text");
m_input->setCallback([this](auto const&) { m_input->setCallback([this](auto const&) {
this->markChanged(); this->markChanged();
}); });
@ -339,20 +341,91 @@ bool FileSettingNodeV3::init(std::shared_ptr<FileSettingV3> setting, float width
if (!SettingNodeV3::init(setting, width)) if (!SettingNodeV3::init(setting, width))
return false; return false;
// todo m_path = setting->getValue();
auto labelBG = extension::CCScale9Sprite::create("square02b_001.png", { 0, 0, 80, 80 });
labelBG->setScale(.25f);
labelBG->setColor({ 0, 0, 0 });
labelBG->setOpacity(90);
labelBG->setContentSize({ 400, 80 });
this->getButtonMenu()->addChildAtPosition(labelBG, Anchor::Center, ccp(-15, 0));
m_fileIcon = CCSprite::create();
this->getButtonMenu()->addChildAtPosition(m_fileIcon, Anchor::Left, ccp(3, 0));
m_nameLabel = CCLabelBMFont::create("", "bigFont.fnt");
this->getButtonMenu()->addChildAtPosition(m_nameLabel, Anchor::Left, ccp(11, 0), ccp(0, .5f));
auto selectSpr = CCSprite::createWithSpriteFrameName("GJ_plus2Btn_001.png");
selectSpr->setScale(.75f);
auto selectBtn = CCMenuItemSpriteExtra::create(
selectSpr, this, menu_selector(FileSettingNodeV3::onPickFile)
);
this->getButtonMenu()->addChildAtPosition(selectBtn, Anchor::Right, ccp(-5, 0));
this->updateState();
return true; return true;
} }
void FileSettingNodeV3::onCommit() {} void FileSettingNodeV3::updateState() {
SettingNodeV3::updateState();
auto ty = this->getSetting()->getFileType();
if (ty == FileSettingV3::FileType::Any) {
ty = std::filesystem::is_directory(m_path) ?
FileSettingV3::FileType::Folder :
FileSettingV3::FileType::File;
}
m_fileIcon->setDisplayFrame(CCSpriteFrameCache::get()->spriteFrameByName(
ty == FileSettingV3::FileType::File ? "file.png"_spr : "folderIcon_001.png"
));
limitNodeSize(m_fileIcon, ccp(10, 10), 1.f, .1f);
m_nameLabel->setString(m_path.filename().string().c_str());
m_nameLabel->limitLabelWidth(75, .4f, .1f);
}
void FileSettingNodeV3::onCommit() {
this->getSetting()->setValue(m_path);
}
void FileSettingNodeV3::onPickFile(CCObject*) {
m_pickListener.bind([this](auto* event) {
auto value = event->getValue();
if (!value) {
return;
}
if (value->isOk()) {
m_path = value->unwrap().string();
this->markChanged();
}
else {
FLAlertLayer::create(
"Failed",
fmt::format("Failed to pick file: {}", value->unwrapErr()),
"Ok"
)->show();
}
});
m_pickListener.setFilter(file::pick(
this->getSetting()->getFileType() == FileSettingV3::FileType::Folder ?
file::PickMode::OpenFolder :
file::PickMode::OpenFile,
{
dirs::getGameDir(),
this->getSetting()->getFilters().value_or(std::vector<file::FilePickOptions::Filter>())
}
));
}
bool FileSettingNodeV3::hasUncommittedChanges() const { bool FileSettingNodeV3::hasUncommittedChanges() const {
return false; return m_path != this->getSetting()->getValue();
} }
bool FileSettingNodeV3::hasNonDefaultValue() const { bool FileSettingNodeV3::hasNonDefaultValue() const {
return false; return m_path != this->getSetting()->getDefaultValue();
}
void FileSettingNodeV3::onResetToDefault() {
m_path = this->getSetting()->getDefaultValue();
} }
void FileSettingNodeV3::onResetToDefault() {}
std::shared_ptr<FileSettingV3> FileSettingNodeV3::getSetting() const { std::shared_ptr<FileSettingV3> FileSettingNodeV3::getSetting() const {
return std::static_pointer_cast<FileSettingV3>(SettingNodeV3::getSetting()); return std::static_pointer_cast<FileSettingV3>(SettingNodeV3::getSetting());

View file

@ -52,6 +52,10 @@ protected:
TextInput* m_input; TextInput* m_input;
Slider* m_slider; Slider* m_slider;
CCMenuItemSpriteExtra* m_arrowLeftBtn;
CCMenuItemSpriteExtra* m_bigArrowLeftBtn;
CCMenuItemSpriteExtra* m_arrowRightBtn;
CCMenuItemSpriteExtra* m_bigArrowRightBtn;
float valueToSlider(ValueType value) { float valueToSlider(ValueType value) {
auto min = this->getSetting()->getMinValue().value_or(-100); auto min = this->getSetting()->getMinValue().value_or(-100);
@ -75,6 +79,8 @@ protected:
return false; return false;
auto bigArrowLeftSpr = CCSprite::create(); auto bigArrowLeftSpr = CCSprite::create();
bigArrowLeftSpr->setCascadeColorEnabled(true);
bigArrowLeftSpr->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");
@ -83,21 +89,21 @@ protected:
bigArrowLeftSpr->addChildAtPosition(bigArrowLeftSpr1, Anchor::Center, ccp(-10, 0)); bigArrowLeftSpr->addChildAtPosition(bigArrowLeftSpr1, Anchor::Center, ccp(-10, 0));
bigArrowLeftSpr->setScale(.3f); bigArrowLeftSpr->setScale(.3f);
auto bigArrowLeftBtn = CCMenuItemSpriteExtra::create( m_bigArrowLeftBtn = CCMenuItemSpriteExtra::create(
bigArrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow) bigArrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
bigArrowLeftBtn->setTag(-setting->getBigArrowStepSize()); m_bigArrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getBigArrowStepSize()));
bigArrowLeftBtn->setVisible(setting->isBigArrowsEnabled()); m_bigArrowLeftBtn->setVisible(setting->isBigArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(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"); auto arrowLeftSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png");
arrowLeftSpr->setScale(.5f); arrowLeftSpr->setScale(.5f);
auto arrowLeftBtn = CCMenuItemSpriteExtra::create( m_arrowLeftBtn = CCMenuItemSpriteExtra::create(
arrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow) arrowLeftSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
arrowLeftBtn->setTag(-setting->getArrowStepSize()); m_arrowLeftBtn->setUserObject(ObjWrapper<ValueType>::create(-setting->getArrowStepSize()));
arrowLeftBtn->setVisible(setting->isArrowsEnabled()); m_arrowLeftBtn->setVisible(setting->isArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(arrowLeftBtn, Anchor::Left, ccp(22, 0)); this->getButtonMenu()->addChildAtPosition(m_arrowLeftBtn, Anchor::Left, ccp(22, 0));
m_input = TextInput::create(this->getButtonMenu()->getContentWidth() - 40, "Num"); m_input = TextInput::create(this->getButtonMenu()->getContentWidth() - 40, "Num");
m_input->setScale(.7f); m_input->setScale(.7f);
@ -115,14 +121,16 @@ protected:
auto arrowRightSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png"); auto arrowRightSpr = CCSprite::createWithSpriteFrameName("GJ_arrow_01_001.png");
arrowRightSpr->setFlipX(true); arrowRightSpr->setFlipX(true);
arrowRightSpr->setScale(.5f); arrowRightSpr->setScale(.5f);
auto arrowRightBtn = CCMenuItemSpriteExtra::create( m_arrowRightBtn = CCMenuItemSpriteExtra::create(
arrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow) arrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
arrowRightBtn->setTag(setting->getArrowStepSize()); m_arrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getArrowStepSize()));
arrowRightBtn->setVisible(setting->isArrowsEnabled()); m_arrowRightBtn->setVisible(setting->isArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(arrowRightBtn, Anchor::Right, ccp(-22, 0)); this->getButtonMenu()->addChildAtPosition(m_arrowRightBtn, Anchor::Right, ccp(-22, 0));
auto bigArrowRightSpr = CCSprite::create(); auto bigArrowRightSpr = CCSprite::create();
bigArrowRightSpr->setCascadeColorEnabled(true);
bigArrowRightSpr->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");
@ -133,12 +141,12 @@ protected:
bigArrowRightSpr->addChildAtPosition(bigArrowRightSpr2, Anchor::Center, ccp(10, 0)); bigArrowRightSpr->addChildAtPosition(bigArrowRightSpr2, Anchor::Center, ccp(10, 0));
bigArrowRightSpr->setScale(.3f); bigArrowRightSpr->setScale(.3f);
auto bigArrowRightBtn = CCMenuItemSpriteExtra::create( m_bigArrowRightBtn = CCMenuItemSpriteExtra::create(
bigArrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow) bigArrowRightSpr, this, menu_selector(NumberSettingNodeV3::onArrow)
); );
bigArrowRightBtn->setTag(setting->getBigArrowStepSize()); m_bigArrowRightBtn->setUserObject(ObjWrapper<ValueType>::create(setting->getBigArrowStepSize()));
bigArrowRightBtn->setVisible(setting->isBigArrowsEnabled()); m_bigArrowRightBtn->setVisible(setting->isBigArrowsEnabled());
this->getButtonMenu()->addChildAtPosition(bigArrowRightBtn, Anchor::Right, ccp(-5, 0)); this->getButtonMenu()->addChildAtPosition(m_bigArrowRightBtn, Anchor::Right, ccp(-5, 0));
if (setting->isSliderEnabled()) { if (setting->isSliderEnabled()) {
this->setContentHeight(45); this->setContentHeight(45);
@ -161,13 +169,40 @@ protected:
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();
} }
if (auto min = this->getSetting()->getMinValue()) {
auto enable = this->getCurrentValue() > *min;
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);
}
} }
void onCommit() override { void onCommit() override {
this->getSetting()->setValue(this->getCurrentValue()); this->getSetting()->setValue(this->getCurrentValue());
} }
void onArrow(CCObject* sender) { void onArrow(CCObject* sender) {
this->setCurrentValue(this->getCurrentValue() + sender->getTag()); auto value = this->getCurrentValue() + static_cast<ObjWrapper<ValueType>*>(
static_cast<CCNode*>(sender)->getUserObject()
)->getValue();
if (auto min = this->getSetting()->getMinValue()) {
value = std::max(*min, value);
}
if (auto max = this->getSetting()->getMaxValue()) {
value = std::min(*max, value);
}
this->setCurrentValue(value);
} }
void onSlider(CCObject*) { void onSlider(CCObject*) {
this->setCurrentValue(this->valueFromSlider(m_slider->m_touchLogic->m_thumb->getValue())); this->setCurrentValue(this->valueFromSlider(m_slider->m_touchLogic->m_thumb->getValue()));
@ -233,9 +268,17 @@ public:
class FileSettingNodeV3 : public SettingNodeV3 { class FileSettingNodeV3 : public SettingNodeV3 {
protected: protected:
CCSprite* m_fileIcon;
std::filesystem::path m_path;
CCLabelBMFont* m_nameLabel;
EventListener<Task<Result<std::filesystem::path>>> m_pickListener;
bool init(std::shared_ptr<FileSettingV3> setting, float width); bool init(std::shared_ptr<FileSettingV3> setting, float width);
void updateState() override;
void onCommit() override; void onCommit() override;
void onPickFile(CCObject*);
public: public:
static FileSettingNodeV3* create(std::shared_ptr<FileSettingV3> setting, float width); static FileSettingNodeV3* create(std::shared_ptr<FileSettingV3> setting, float width);

View file

@ -26,6 +26,7 @@ Result<> SettingV3::parseSharedProperties(std::string const& key, std::string co
void SettingV3::parseSharedProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc) { void SettingV3::parseSharedProperties(std::string const& key, std::string const& modID, JsonExpectedValue& value, bool onlyNameAndDesc) {
this->init(key, modID); this->init(key, modID);
value.needs("type"); value.needs("type");
value.has("platforms");
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) {
@ -239,32 +240,40 @@ Result<std::shared_ptr<IntSettingV3>> IntSettingV3::parse(std::string const& key
root.has("min").into(ret->m_impl->minValue); root.has("min").into(ret->m_impl->minValue);
root.has("max").into(ret->m_impl->maxValue); root.has("max").into(ret->m_impl->maxValue);
if (auto controls = root.has("control")) { if (auto controls = root.has("control")) {
controls.has("arrows");
controls.has("big-arrows");
controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize); controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
ret->m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize); controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(ret->m_impl->controls.sliderEnabled); controls.has("slider").into(ret->m_impl->controls.sliderEnabled);
controls.has("slider-step").into(ret->m_impl->controls.sliderSnap); controls.has("slider-step").into(ret->m_impl->controls.sliderSnap);
controls.has("input").into(ret->m_impl->controls.textInputEnabled); controls.has("input").into(ret->m_impl->controls.textInputEnabled);
// Without "min" or "max" slider makes no sense
if (!ret->m_impl->minValue || !ret->m_impl->maxValue) {
if (ret->m_impl->controls.sliderEnabled) {
log::warn(
"Setting '{}' has \"controls.slider\" enabled but doesn't "
"have both \"min\" and \"max\" defined - the slider has "
"been force-disabled!",
key
);
}
ret->m_impl->controls.sliderEnabled = false;
}
controls.checkUnknownKeys(); controls.checkUnknownKeys();
} }
// Disable arrows if they aren't enabled
// This silly code is because step size being 0 is what defines if they are enabled
// Small arrows are enabled by default
if (!root.has("control").has("arrows").template get<bool>(true)) {
ret->m_impl->controls.arrowStepSize = 0;
}
if (!root.has("control").has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
// Without "min" or "max" slider makes no sense
if (!ret->m_impl->minValue || !ret->m_impl->maxValue) {
if (ret->m_impl->controls.sliderEnabled && root.has("control").has("slider")) {
log::warn(
"Setting '{}' has \"controls.slider\" enabled but doesn't "
"have both \"min\" and \"max\" defined - the slider has "
"been force-disabled!",
key
);
}
ret->m_impl->controls.sliderEnabled = false;
}
root.checkUnknownKeys(); root.checkUnknownKeys();
return root.ok(ret); return root.ok(ret);
} }
@ -279,10 +288,10 @@ int64_t IntSettingV3::getDefaultValue() const {
} }
Result<> IntSettingV3::isValid(int64_t value) const { Result<> IntSettingV3::isValid(int64_t value) const {
if (m_impl->minValue && value < *m_impl->minValue) { if (m_impl->minValue && value < *m_impl->minValue) {
return Err("value must be at least {}", *m_impl->minValue); return Err("Value must be at least {}", *m_impl->minValue);
} }
if (m_impl->maxValue && value > *m_impl->maxValue) { if (m_impl->maxValue && value > *m_impl->maxValue) {
return Err("value must be at most {}", *m_impl->maxValue); return Err("Value must be at most {}", *m_impl->maxValue);
} }
return Ok(); return Ok();
} }
@ -364,8 +373,8 @@ public:
struct { struct {
// 0 means not enabled // 0 means not enabled
size_t arrowStepSize = 1; double arrowStepSize = 1;
size_t bigArrowStepSize = 5; double bigArrowStepSize = 5;
bool sliderEnabled = true; bool sliderEnabled = true;
std::optional<double> sliderSnap; std::optional<double> sliderSnap;
bool textInputEnabled = true; bool textInputEnabled = true;
@ -385,32 +394,38 @@ Result<std::shared_ptr<FloatSettingV3>> FloatSettingV3::parse(std::string const&
root.has("min").into(ret->m_impl->minValue); root.has("min").into(ret->m_impl->minValue);
root.has("max").into(ret->m_impl->maxValue); root.has("max").into(ret->m_impl->maxValue);
if (auto controls = root.has("control")) { if (auto controls = root.has("control")) {
controls.has("arrows");
controls.has("big-arrows");
controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize); controls.has("arrow-step").into(ret->m_impl->controls.arrowStepSize);
if (!controls.has("arrows").template get<bool>()) {
ret->m_impl->controls.arrowStepSize = 0;
}
controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize); controls.has("big-arrow-step").into(ret->m_impl->controls.bigArrowStepSize);
if (!controls.has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
controls.has("slider").into(ret->m_impl->controls.sliderEnabled); controls.has("slider").into(ret->m_impl->controls.sliderEnabled);
controls.has("slider-step").into(ret->m_impl->controls.sliderSnap); controls.has("slider-step").into(ret->m_impl->controls.sliderSnap);
controls.has("input").into(ret->m_impl->controls.textInputEnabled); controls.has("input").into(ret->m_impl->controls.textInputEnabled);
// Without "min" or "max" slider makes no sense
if (!ret->m_impl->minValue || !ret->m_impl->maxValue) {
if (ret->m_impl->controls.sliderEnabled) {
log::warn(
"Setting '{}' has \"controls.slider\" enabled but doesn't "
"have both \"min\" and \"max\" defined - the slider has "
"been force-disabled!",
key
);
}
ret->m_impl->controls.sliderEnabled = false;
}
controls.checkUnknownKeys(); controls.checkUnknownKeys();
} }
// Disable arrows if they aren't enabled
// Small arrows are enabled by default
if (!root.has("control").has("arrows").template get<bool>(true)) {
ret->m_impl->controls.arrowStepSize = 0;
}
if (!root.has("control").has("big-arrows").template get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
// Without "min" or "max" slider makes no sense
if (!ret->m_impl->minValue || !ret->m_impl->maxValue) {
if (ret->m_impl->controls.sliderEnabled && root.has("control").has("slider")) {
log::warn(
"Setting '{}' has \"controls.slider\" enabled but doesn't "
"have both \"min\" and \"max\" defined - the slider has "
"been force-disabled!",
key
);
}
ret->m_impl->controls.sliderEnabled = false;
}
root.checkUnknownKeys(); root.checkUnknownKeys();
return root.ok(ret); return root.ok(ret);
} }
@ -423,10 +438,10 @@ double FloatSettingV3::getDefaultValue() const {
} }
Result<> FloatSettingV3::isValid(double value) const { Result<> FloatSettingV3::isValid(double value) const {
if (m_impl->minValue && value < *m_impl->minValue) { if (m_impl->minValue && value < *m_impl->minValue) {
return Err("value must be at least {}", *m_impl->minValue); return Err("Value must be at least {}", *m_impl->minValue);
} }
if (m_impl->maxValue && value > *m_impl->maxValue) { if (m_impl->maxValue && value > *m_impl->maxValue) {
return Err("value must be at most {}", *m_impl->maxValue); return Err("Value must be at most {}", *m_impl->maxValue);
} }
return Ok(); return Ok();
} }
@ -444,10 +459,10 @@ bool FloatSettingV3::isArrowsEnabled() const {
bool FloatSettingV3::isBigArrowsEnabled() const { bool FloatSettingV3::isBigArrowsEnabled() const {
return m_impl->controls.bigArrowStepSize > 0; return m_impl->controls.bigArrowStepSize > 0;
} }
size_t FloatSettingV3::getArrowStepSize() const { double FloatSettingV3::getArrowStepSize() const {
return m_impl->controls.arrowStepSize; return m_impl->controls.arrowStepSize;
} }
size_t FloatSettingV3::getBigArrowStepSize() const { double FloatSettingV3::getBigArrowStepSize() const {
return m_impl->controls.bigArrowStepSize; return m_impl->controls.bigArrowStepSize;
} }
bool FloatSettingV3::isSliderEnabled() const { bool FloatSettingV3::isSliderEnabled() const {
@ -487,8 +502,8 @@ std::optional<Setting> FloatSettingV3::convertToLegacy() const {
.controls = { .controls = {
.arrows = this->isArrowsEnabled(), .arrows = this->isArrowsEnabled(),
.bigArrows = this->isBigArrowsEnabled(), .bigArrows = this->isBigArrowsEnabled(),
.arrowStep = this->getArrowStepSize(), .arrowStep = static_cast<size_t>(this->getArrowStepSize()),
.bigArrowStep = this->getBigArrowStepSize(), .bigArrowStep = static_cast<size_t>(this->getBigArrowStepSize()),
.slider = this->isSliderEnabled(), .slider = this->isSliderEnabled(),
.sliderStep = this->getSliderSnap(), .sliderStep = this->getSliderSnap(),
.input = this->isInputEnabled(), .input = this->isInputEnabled(),
@ -538,12 +553,12 @@ std::string StringSettingV3::getDefaultValue() const {
Result<> StringSettingV3::isValid(std::string_view value) const { Result<> StringSettingV3::isValid(std::string_view value) const {
if (m_impl->match) { if (m_impl->match) {
if (!std::regex_match(std::string(value), std::regex(*m_impl->match))) { if (!std::regex_match(std::string(value), std::regex(*m_impl->match))) {
return Err("value must match regex {}", *m_impl->match); return Err("Value must match regex {}", *m_impl->match);
} }
} }
else if (m_impl->oneOf) { else if (m_impl->oneOf) {
if (!ranges::contains(*m_impl->oneOf, std::string(value))) { if (!ranges::contains(*m_impl->oneOf, std::string(value))) {
return Err("value must be one of {}", fmt::join(*m_impl->oneOf, ", ")); return Err("Value must be one of {}", fmt::join(*m_impl->oneOf, ", "));
} }
} }
return Ok(); return Ok();
@ -594,6 +609,7 @@ class FileSettingV3::Impl final {
public: public:
std::filesystem::path value; std::filesystem::path value;
std::filesystem::path defaultValue; std::filesystem::path defaultValue;
FileType fileType;
std::optional<std::vector<utils::file::FilePickOptions::Filter>> filters; std::optional<std::vector<utils::file::FilePickOptions::Filter>> filters;
}; };
@ -611,18 +627,31 @@ Result<std::shared_ptr<FileSettingV3>> FileSettingV3::parse(std::string const& k
try { try {
ret->m_impl->defaultValue = fmt::format( ret->m_impl->defaultValue = fmt::format(
fmt::runtime(ret->m_impl->defaultValue.string()), fmt::runtime(ret->m_impl->defaultValue.string()),
fmt::arg("gd-save-dir", dirs::getSaveDir()), fmt::arg("gd_dir", dirs::getGameDir()),
fmt::arg("gd-game-dir", dirs::getGameDir()), fmt::arg("gd_save_dir", dirs::getSaveDir()),
fmt::arg("mod-config-dir", dirs::getModConfigDir() / modID), fmt::arg("mod_config_dir", dirs::getModConfigDir() / modID),
fmt::arg("mod-save-dir", dirs::getModsSaveDir() / modID), fmt::arg("mod_save_dir", dirs::getModsSaveDir() / modID),
fmt::arg("temp-dir", dirs::getTempDir()) fmt::arg("temp_dir", dirs::getTempDir())
); );
} }
catch(fmt::format_error const&) { catch(fmt::format_error const& e) {
return Err("Invalid format string for file setting path"); return Err("Invalid format string for file setting path: {}", e.what());
} }
ret->m_impl->value = ret->m_impl->defaultValue; ret->m_impl->value = ret->m_impl->defaultValue;
if (auto ty = root.has("filetype")) {
ty.assertIsString();
switch (hash(ty.template get<std::string>())) {
case hash("any"): ret->m_impl->fileType = FileType::Any; break;
case hash("file"): ret->m_impl->fileType = FileType::File; break;
case hash("folder"): ret->m_impl->fileType = FileType::Folder; break;
default: return Err(
"Setting '{}' in mod {}: Invalid filetype \"{}\"",
key, modID, ty.template get<std::string>()
);
}
}
if (auto controls = root.has("control")) { if (auto controls = root.has("control")) {
auto filters = std::vector<file::FilePickOptions::Filter>(); auto filters = std::vector<file::FilePickOptions::Filter>();
for (auto& item : controls.has("filters").items()) { for (auto& item : controls.has("filters").items()) {
@ -647,9 +676,24 @@ std::filesystem::path FileSettingV3::getDefaultValue() const {
return m_impl->defaultValue; return m_impl->defaultValue;
} }
Result<> FileSettingV3::isValid(std::filesystem::path const& value) const { Result<> FileSettingV3::isValid(std::filesystem::path const& value) const {
if (m_impl->fileType != FileType::Any) {
if (!std::filesystem::exists(value)) {
return Err("{} must exist", m_impl->fileType == FileType::File ? "File" : "Folder");
}
if (m_impl->fileType == FileType::File && !std::filesystem::is_regular_file(value)) {
return Err("Value must be a file");
}
if (m_impl->fileType == FileType::Folder && !std::filesystem::is_directory(value)) {
return Err("Value must be a folder");
}
}
return Ok(); return Ok();
} }
FileSettingV3::FileType FileSettingV3::getFileType() const {
return m_impl->fileType;
}
std::optional<std::vector<utils::file::FilePickOptions::Filter>> FileSettingV3::getFilters() const { std::optional<std::vector<utils::file::FilePickOptions::Filter>> FileSettingV3::getFilters() const {
return m_impl->filters; return m_impl->filters;
} }