diff --git a/loader/include/Geode/loader/SettingV3.hpp b/loader/include/Geode/loader/SettingV3.hpp index 8f17c193..a5a09128 100644 --- a/loader/include/Geode/loader/SettingV3.hpp +++ b/loader/include/Geode/loader/SettingV3.hpp @@ -328,16 +328,11 @@ namespace geode { FileSettingV3(PrivateMarker); static Result> 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; Result<> isValid(std::filesystem::path const& value) const override; - FileType getFileType() const; + bool isFolder() const; + bool useSaveDialog() const; std::optional> getFilters() const; diff --git a/loader/src/loader/ModSettingsManager.cpp b/loader/src/loader/ModSettingsManager.cpp index 96412fec..dc625ac7 100644 --- a/loader/src/loader/ModSettingsManager.cpp +++ b/loader/src/loader/ModSettingsManager.cpp @@ -21,6 +21,7 @@ private: { "float", &FloatSettingV3::parse }, { "string", &StringSettingV3::parse }, { "file", &FileSettingV3::parse }, + { "folder", &FileSettingV3::parse }, { "path", &FileSettingV3::parse }, { "rgb", &Color3BSettingV3::parse }, { "color", &Color3BSettingV3::parse }, diff --git a/loader/src/loader/SettingNodeV3.cpp b/loader/src/loader/SettingNodeV3.cpp index c9b80726..93e2ad94 100644 --- a/loader/src/loader/SettingNodeV3.cpp +++ b/loader/src/loader/SettingNodeV3.cpp @@ -347,17 +347,17 @@ bool FileSettingNodeV3::init(std::shared_ptr setting, float width labelBG->setScale(.25f); labelBG->setColor({ 0, 0, 0 }); labelBG->setOpacity(90); - labelBG->setContentSize({ 400, 80 }); - this->getButtonMenu()->addChildAtPosition(labelBG, Anchor::Center, ccp(-15, 0)); + labelBG->setContentSize({ 420, 80 }); + this->getButtonMenu()->addChildAtPosition(labelBG, Anchor::Center, ccp(-10, 0)); m_fileIcon = CCSprite::create(); - this->getButtonMenu()->addChildAtPosition(m_fileIcon, Anchor::Left, ccp(3, 0)); + this->getButtonMenu()->addChildAtPosition(m_fileIcon, Anchor::Left, ccp(5, 0)); m_nameLabel = CCLabelBMFont::create("", "bigFont.fnt"); - this->getButtonMenu()->addChildAtPosition(m_nameLabel, Anchor::Left, ccp(11, 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"); - selectSpr->setScale(.75f); + selectSpr->setScale(.7f); auto selectBtn = CCMenuItemSpriteExtra::create( selectSpr, this, menu_selector(FileSettingNodeV3::onPickFile) ); @@ -370,18 +370,21 @@ bool FileSettingNodeV3::init(std::shared_ptr setting, float width 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" + this->getSetting()->isFolder() ? "folderIcon_001.png" : "file.png"_spr )); limitNodeSize(m_fileIcon, ccp(10, 10), 1.f, .1f); - m_nameLabel->setString(m_path.filename().string().c_str()); - m_nameLabel->limitLabelWidth(75, .4f, .1f); + if (m_path.empty()) { + m_nameLabel->setString(this->getSetting()->isFolder() ? "No Folder Selected" : "No File Selected"); + m_nameLabel->setColor(ccGRAY); + m_nameLabel->setOpacity(155); + } + else { + m_nameLabel->setString(m_path.filename().string().c_str()); + m_nameLabel->setColor(ccWHITE); + m_nameLabel->setOpacity(255); + } + m_nameLabel->limitLabelWidth(75, .35f, .1f); } void FileSettingNodeV3::onCommit() { @@ -407,9 +410,9 @@ void FileSettingNodeV3::onPickFile(CCObject*) { } }); m_pickListener.setFilter(file::pick( - this->getSetting()->getFileType() == FileSettingV3::FileType::Folder ? - file::PickMode::OpenFolder : - file::PickMode::OpenFile, + this->getSetting()->isFolder() ? + file::PickMode::OpenFolder : + (this->getSetting()->useSaveDialog() ? file::PickMode::SaveFile : file::PickMode::OpenFile), { dirs::getGameDir(), this->getSetting()->getFilters().value_or(std::vector()) diff --git a/loader/src/loader/SettingV3.cpp b/loader/src/loader/SettingV3.cpp index b3b096c5..8f74da61 100644 --- a/loader/src/loader/SettingV3.cpp +++ b/loader/src/loader/SettingV3.cpp @@ -609,7 +609,8 @@ class FileSettingV3::Impl final { public: std::filesystem::path value; std::filesystem::path defaultValue; - FileType fileType; + bool folder = false; + bool useSaveDialog = false; // this option makes no sense if folder = true std::optional> filters; }; @@ -623,7 +624,7 @@ Result> FileSettingV3::parse(std::string const& k ret->parseDefaultValue(root, ret->m_impl->defaultValue); ret->m_impl->value = ret->m_impl->defaultValue; - // Replace known paths like `{gd-save-dir}/` + // Replace known paths like `{gd-save-dir}/` try { ret->m_impl->defaultValue = fmt::format( fmt::runtime(ret->m_impl->defaultValue.string()), @@ -639,29 +640,41 @@ Result> FileSettingV3::parse(std::string const& k } ret->m_impl->value = ret->m_impl->defaultValue; - if (auto ty = root.has("filetype")) { - ty.assertIsString(); - switch (hash(ty.template get())) { - 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 type; + root.needs("type").into(type); + if (type == "folder") { + ret->m_impl->folder = true; + // folder-specific stuff if they ever exist + } + else if (type == "file" || type == "path") { + if (type == "path") { + log::warn( + "Setting '{}' in mod {}: the \"path\" type has been " + "deprecated, use \"type\": \"file\" or \"type\": \"folder\" instead", + key, modID ); } - } - - if (auto controls = root.has("control")) { - auto filters = std::vector(); - for (auto& item : controls.has("filters").items()) { - utils::file::FilePickOptions::Filter filter; - item.has("description").into(filter.description); - item.has("files").into(filter.files); - filters.push_back(filter); + std::string dialogType; + root.has("dialog").into(dialogType); + switch (hash(dialogType)) { + case hash("save"): ret->m_impl->useSaveDialog = true; break; + case hash("open"): ret->m_impl->useSaveDialog = false; break; + case hash(""): break; + default: return Err("Setting '{}' in mod {}: unknown \"dialog\" type \"{}\"", key, modID, dialogType); } - if (!filters.empty()) { - ret->m_impl->filters.emplace(filters); + + // Filter controls only make sense for files but not for folders + if (auto controls = root.has("control")) { + auto filters = std::vector(); + for (auto& item : controls.has("filters").items()) { + utils::file::FilePickOptions::Filter filter; + item.has("description").into(filter.description); + item.has("files").into(filter.files); + filters.push_back(filter); + } + if (!filters.empty()) { + ret->m_impl->filters.emplace(filters); + } } } @@ -676,22 +689,25 @@ std::filesystem::path FileSettingV3::getDefaultValue() const { return m_impl->defaultValue; } 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)) { + std::error_code ec; + if (m_impl->folder) { + if (!std::filesystem::is_directory(value, ec)) { return Err("Value must be a folder"); } } + else { + if (!std::filesystem::is_regular_file(value, ec)) { + return Err("Value must be a file"); + } + } return Ok(); } -FileSettingV3::FileType FileSettingV3::getFileType() const { - return m_impl->fileType; +bool FileSettingV3::isFolder() const { + return m_impl->folder; +} +bool FileSettingV3::useSaveDialog() const { + return m_impl->useSaveDialog; } std::optional> FileSettingV3::getFilters() const {