diff --git a/CMakeLists.txt b/CMakeLists.txt index 553f96ec..218f26d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,14 +15,32 @@ string(STRIP "${GEODE_VERSION}" GEODE_VERSION) # Check if version has a tag like v1.0.0-alpha string(FIND ${GEODE_VERSION} "-" GEODE_VERSION_HAS_TAG) -if (GEODE_VERSION_HAS_TAG) - string(REGEX MATCH "[a-z]+[0-9]?$" GEODE_VERSION_TAG ${GEODE_VERSION}) +if (NOT ${GEODE_VERSION_HAS_TAG} EQUAL "-1") + string(REGEX MATCH "[a-z]+(\.[0-9]+)?$" GEODE_VERSION_TAG ${GEODE_VERSION}) string(SUBSTRING "${GEODE_VERSION}" 0 ${GEODE_VERSION_HAS_TAG} GEODE_VERSION) + string(FIND ${GEODE_VERSION_TAG} "." GEODE_VERSION_TAG_HAS_NUMBER) + + # Extract tag type and number from tag + if (NOT ${GEODE_VERSION_TAG_HAS_NUMBER} EQUAL "-1") + string(SUBSTRING "${GEODE_VERSION_TAG}" 0 ${GEODE_VERSION_TAG_HAS_NUMBER} GEODE_VERSION_TAG_TYPE) + math(EXPR GEODE_VERSION_TAG_HAS_NUMBER "${GEODE_VERSION_TAG_HAS_NUMBER} + 1") + string(SUBSTRING "${GEODE_VERSION_TAG}" ${GEODE_VERSION_TAG_HAS_NUMBER} -1 GEODE_VERSION_TAG_NUMBER) + else() + set(GEODE_VERSION_TAG_TYPE "${GEODE_VERSION_TAG}") + set(GEODE_VERSION_TAG_NUMBER "") + endif() + + # Capitalize first letter of tag type + string(SUBSTRING ${GEODE_VERSION_TAG_TYPE} 0 1 FIRST_LETTER) + string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) + string(REGEX REPLACE "^.(.*)" "${FIRST_LETTER}\\1" GEODE_VERSION_TAG_TYPE "${GEODE_VERSION_TAG_TYPE}") else() set(GEODE_VERSION_TAG "") + set(GEODE_VERSION_TAG_TYPE "") + set(GEODE_VERSION_TAG_NUMBER "") endif() -message(STATUS "Version: ${GEODE_VERSION}, tag: ${GEODE_VERSION_TAG}") +message(STATUS "Version: ${GEODE_VERSION}, tag: ${GEODE_VERSION_TAG} (type: ${GEODE_VERSION_TAG_TYPE}, number: ${GEODE_VERSION_TAG_NUMBER})") project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C) diff --git a/VERSION b/VERSION index 537aabf7..844dc4b5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-beta \ No newline at end of file +1.0.0-beta.6 \ No newline at end of file diff --git a/loader/CMakeLists.txt b/loader/CMakeLists.txt index cced6f1a..8470206f 100644 --- a/loader/CMakeLists.txt +++ b/loader/CMakeLists.txt @@ -1,8 +1,21 @@ cmake_minimum_required(VERSION 3.21 FATAL_ERROR) project(geode-loader VERSION ${GEODE_VERSION} LANGUAGES C CXX) -set(PROJECT_VERSION_TYPE geode::VersionTag::Beta) -set(PROJECT_VERSION_SUFFIX -beta) +if (GEODE_VERSION_TAG_TYPE) + if (GEODE_VERSION_TAG_NUMBER) + set(PROJECT_VERSION_TAG_CONSTR "geode::VersionTag(geode::VersionTag::${GEODE_VERSION_TAG_TYPE}, ${GEODE_VERSION_TAG_NUMBER})") + else() + set(PROJECT_VERSION_TAG_CONSTR "geode::VersionTag::${GEODE_VERSION_TAG_TYPE}") + endif() +else() + set(PROJECT_VERSION_TAG_CONSTR "std::nullopt") +endif() + +if (GEODE_VERSION_TAG) + set(PROJECT_VERSION_SUFFIX "-${GEODE_VERSION_TAG}") +else() + set(PROJECT_VERSION_SUFFIX "") +endif() # Package info file for internal representation configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json) diff --git a/loader/include/Geode/utils/VersionInfo.hpp b/loader/include/Geode/utils/VersionInfo.hpp index f94472ca..d9df8c88 100644 --- a/loader/include/Geode/utils/VersionInfo.hpp +++ b/loader/include/Geode/utils/VersionInfo.hpp @@ -14,24 +14,73 @@ namespace geode { }; /** - * A version label, like v1.0.0-alpha or v2.3.4-prerelease. Purely semantic, - * and not used in comparisons; so for example v1.0.0-alpha == v1.0.0. + * A version label, like v1.0.0-alpha or v2.3.4-prerelease. Limited to these + * options; arbitary identifiers are not supported. Additional numbering + * may be added after the identifier, such as v1.0.0-beta.1 */ - enum class VersionTag { - Alpha, - Beta, - Prerelease, + struct VersionTag { + enum { + Alpha, + Beta, + Prerelease, + } value; + std::optional number; + + using Type = decltype(value); + + constexpr VersionTag(Type const& value) : value(value) {} + constexpr VersionTag(Type const& value, std::optional number) + : value(value), number(number) {} + + constexpr bool operator==(VersionTag const& other) const { + return value == other.value && number == other.number; + } + constexpr bool operator<(VersionTag const& other) const { + if (value == other.value) { + if (number && other.number) return number < other.number; + if (number) return true; + if (other.number) return false; + return false; + } + return value < other.value; + } + constexpr bool operator<=(VersionTag const& other) const { + if (value == other.value) { + if (number && other.number) return number <= other.number; + if (number) return true; + if (other.number) return false; + return true; + } + return value <= other.value; + } + constexpr bool operator>(VersionTag const& other) const { + if (value == other.value) { + if (number && other.number) return number > other.number; + if (number) return true; + if (other.number) return false; + return false; + } + return value > other.value; + } + constexpr bool operator>=(VersionTag const& other) const { + if (value == other.value) { + if (number && other.number) return number >= other.number; + if (number) return true; + if (other.number) return false; + return true; + } + return value >= other.value; + } + + static Result parse(std::stringstream& str); + std::string toSuffixString() const; + std::string toString() const; }; - GEODE_DLL std::optional versionTagFromString(std::string const& str); - GEODE_DLL std::string versionTagToSuffixString(VersionTag tag); - GEODE_DLL std::string versionTagToString(VersionTag tag); /** - * Class representing version information. Not strictly semver, notably in - * regard to identifiers; identifiers are restricted to a few common ones, - * and are purely semantic, i.e. not used in comparisons. See VersionTag - * for details - * @class VersionInfo + * Class representing version information. Uses a limited subset of SemVer; + * identifiers are restricted to a few predefined ones, and only one + * identifier is allowed. See VersionTag for details */ class GEODE_DLL VersionInfo final { protected: @@ -78,24 +127,24 @@ namespace geode { // Apple clang does not support operator<=>! Yippee! constexpr bool operator==(VersionInfo const& other) const { - return std::tie(m_major, m_minor, m_patch) == - std::tie(other.m_major, other.m_minor, other.m_patch); + return std::tie(m_major, m_minor, m_patch, m_tag) == + std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); } constexpr bool operator<(VersionInfo const& other) const { - return std::tie(m_major, m_minor, m_patch) < - std::tie(other.m_major, other.m_minor, other.m_patch); + return std::tie(m_major, m_minor, m_patch, m_tag) < + std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); } constexpr bool operator<=(VersionInfo const& other) const { - return std::tie(m_major, m_minor, m_patch) <= - std::tie(other.m_major, other.m_minor, other.m_patch); + return std::tie(m_major, m_minor, m_patch, m_tag) <= + std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); } constexpr bool operator>(VersionInfo const& other) const { - return std::tie(m_major, m_minor, m_patch) > - std::tie(other.m_major, other.m_minor, other.m_patch); + return std::tie(m_major, m_minor, m_patch, m_tag) > + std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); } constexpr bool operator>=(VersionInfo const& other) const { - return std::tie(m_major, m_minor, m_patch) >= - std::tie(other.m_major, other.m_minor, other.m_patch); + return std::tie(m_major, m_minor, m_patch, m_tag) >= + std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); } std::string toString(bool includeTag = true) const; diff --git a/loader/resources/mod.json.in b/loader/resources/mod.json.in index b4baa56d..5953f0c6 100644 --- a/loader/resources/mod.json.in +++ b/loader/resources/mod.json.in @@ -1,5 +1,5 @@ { - "geode": "@PROJECT_VERSION@", + "geode": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "id": "geode.loader", "version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "name": "Geode", diff --git a/loader/src/cocos2d-ext/Layout.cpp b/loader/src/cocos2d-ext/Layout.cpp index 65ae40fe..496c28da 100644 --- a/loader/src/cocos2d-ext/Layout.cpp +++ b/loader/src/cocos2d-ext/Layout.cpp @@ -269,8 +269,9 @@ AxisLayout::Row* AxisLayout::fitInRow( } } axisLength += pos.axisLength; - if (pos.crossLength > crossLength) { - crossLength = pos.crossLength; + // squishing doesn't affect cross length, that's done separately + if (pos.crossLength / squish > crossLength) { + crossLength = pos.crossLength / squish; } prev = opts; ix++; @@ -303,7 +304,7 @@ AxisLayout::Row* AxisLayout::fitInRow( available.axisLength / nextAxisLength * scale * squish, // how much should the nodes be squished to fit the next item in this // row - available.axisLength / nextAxisLength * scale * squish, + available.axisLength / nextAxisLength, axisLength, crossLength, axisEndsLength, @@ -322,6 +323,8 @@ void AxisLayout::tryFitLayout( // like i genuinely have no clue fr why some of these work tho, // i just threw in random equations and numbers until it worked + log::debug("tryFitLayout -> scale: {}, squish: {}, prio: {}", scale, squish, prio); + auto rows = CCArray::create(); float totalRowCrossLength = 0.f; float crossScaleDownFactor = 0.f; @@ -358,16 +361,32 @@ void AxisLayout::tryFitLayout( } newNodes->release(); + if (!rows->count()) { + return; + } + + log::debug("crossScaleDownFactor: {}, crossSquishFactor: {}", crossScaleDownFactor, crossSquishFactor); + auto available = nodeAxis(on, m_axis, 1.f); + if (available.axisLength <= 0.f || available.crossLength <= 0.f) { + return; + } + // if cross axis overflow not allowed and it's overflowing, try to scale // down layout if there are any nodes with auto-scale enabled (or // auto-scale is enabled by default) if ( - !m_allowCrossAxisOverflow && - doAutoScale && - totalRowCrossLength > available.crossLength + ( + !m_allowCrossAxisOverflow && + doAutoScale && + totalRowCrossLength > available.crossLength + ) || ( + !m_growCrossAxis && + static_cast(rows->firstObject())->axisLength > available.axisLength + ) ) { + log::debug("scaling"); bool attemptRescale = false; auto minScaleForPrio = this->minScaleForPrio(nodes, prio); if ( @@ -379,7 +398,9 @@ void AxisLayout::tryFitLayout( crossScaleDownFactor == scale ) { // is there still some lower priority nodes we could try scaling? + log::debug("prio: {} :: scale: {}", prio, scale); if (prio > minMaxPrios.first) { + log::debug("scale to max"); while (true) { prio -= 1; auto scale = this->maxScaleForPrio(nodes, prio); @@ -391,13 +412,17 @@ void AxisLayout::tryFitLayout( } attemptRescale = true; } - // otherwise we're just gonna squish + // otherwise set scale to min and squish + else { + scale = minScaleForPrio; + } } // otherwise scale as usual else { attemptRescale = true; } if (attemptRescale) { + log::debug("gonna try with scale {} from {}", crossScaleDownFactor, scale); rows->release(); return this->tryFitLayout( on, nodes, @@ -408,10 +433,21 @@ void AxisLayout::tryFitLayout( } // if we're still overflowing, squeeze nodes closer together - if (!m_allowCrossAxisOverflow && totalRowCrossLength > available.crossLength) { + if ( + ( + !m_allowCrossAxisOverflow && + totalRowCrossLength > available.crossLength + ) || ( + !m_growCrossAxis && + static_cast(rows->firstObject())->axisLength > available.axisLength + ) + ) { + log::debug("gonna try with squish {} from {}", crossSquishFactor, squish); // if squishing rows would take less squishing that squishing columns, // then squish rows - if (totalRowCrossLength / available.crossLength < crossSquishFactor) { + if ( + !m_growCrossAxis || totalRowCrossLength / available.crossLength < crossSquishFactor + ) { rows->release(); return this->tryFitLayout( on, nodes, @@ -489,25 +525,6 @@ void AxisLayout::tryFitLayout( rowCrossPos -= row->crossLength * columnSquish; } - // scale down & squish row if it overflows main axis - float rowScale = scale; - float rowSquish = squish; - if (row->axisLength > available.axisLength) { - row->axisLength /= scale * squish; - if (m_autoScale) { - rowScale = available.axisLength / row->axisLength; - if (rowScale < AXISLAYOUT_DEFAULT_MIN_SCALE) { - rowScale = AXISLAYOUT_DEFAULT_MIN_SCALE; - } - row->axisLength *= rowScale; - } - // squishing needs to take into account the row ends - if (row->axisLength > available.axisLength) { - rowSquish = available.axisLength / row->axisLength; - } - row->axisLength *= rowSquish; - } - float rowAxisPos; switch (m_axisAlignment) { case AxisAlignment::Start: { @@ -532,34 +549,33 @@ void AxisLayout::tryFitLayout( size_t ix = 0; AxisLayoutOptions const* prev = nullptr; for (auto& node : CCArrayExt(row->nodes)) { - auto scale = rowScale; auto opts = axisOpts(node); // rescale node if overflowing if (this->shouldAutoScale(opts)) { - scale = scaleByOpts(opts, scale, prio); + auto nodeScale = scaleByOpts(opts, scale, prio); // CCMenuItemSpriteExtra is quirky af if (auto btn = typeinfo_cast(node)) { - btn->m_baseScale = scale; + btn->m_baseScale = nodeScale; } - node->setScale(scale); + node->setScale(nodeScale); } if (!ix) { - rowAxisPos += row->axisEndsLength * scale / 2 * (1.f - rowSquish); + rowAxisPos += row->axisEndsLength * scale / 2 * (1.f - squish); } - auto pos = nodeAxis(node, m_axis, rowSquish); + auto pos = nodeAxis(node, m_axis, squish); float axisPos; if (m_axisAlignment == AxisAlignment::Even) { axisPos = rowAxisPos + evenSpace / 2 - pos.axisLength * (.5f - pos.axisAnchor); rowAxisPos += evenSpace - - row->axisEndsLength * scale * (1.f - rowSquish) * 1.f / nodes->count(); + row->axisEndsLength * scale * (1.f - squish) * 1.f / nodes->count(); } else { if (ix) { - rowAxisPos += this->nextGap(prev, opts) * scale * rowSquish; + rowAxisPos += this->nextGap(prev, opts) * scale * squish; } axisPos = rowAxisPos + pos.axisLength * pos.axisAnchor; rowAxisPos += pos.axisLength - - row->axisEndsLength * scale * (1.f - rowSquish) * 1.f / nodes->count(); + row->axisEndsLength * scale * (1.f - squish) * 1.f / nodes->count(); } float crossOffset; switch (m_crossAlignment) { @@ -634,6 +650,7 @@ void AxisLayout::apply(CCNode* on) { } } + log::debug("applying"); this->tryFitLayout( on, nodes, minMaxPrio, doAutoScale, diff --git a/loader/src/internal/about.hpp.in b/loader/src/internal/about.hpp.in index a0cdc50e..6da36c01 100644 --- a/loader/src/internal/about.hpp.in +++ b/loader/src/internal/about.hpp.in @@ -11,6 +11,6 @@ static constexpr geode::VersionInfo LOADER_VERSION = { @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, - @PROJECT_VERSION_TYPE@, + @PROJECT_VERSION_TAG_CONSTR@, }; static constexpr const char* LOADER_MOD_JSON = R"JSON_SEPARATOR(@LOADER_MOD_JSON@)JSON_SEPARATOR"; diff --git a/loader/src/ui/internal/list/ModListCell.cpp b/loader/src/ui/internal/list/ModListCell.cpp index a2b89bf1..b70c83eb 100644 --- a/loader/src/ui/internal/list/ModListCell.cpp +++ b/loader/src/ui/internal/list/ModListCell.cpp @@ -79,9 +79,7 @@ void ModListCell::setupInfo( this->addChild(versionLabel); if (auto tag = info.version().getTag()) { - auto tagLabel = TagNode::create( - versionTagToString(tag.value()).c_str() - ); + auto tagLabel = TagNode::create(tag.value().toString().c_str()); tagLabel->setAnchorPoint({ .0f, .5f }); tagLabel->setScale(.3f); tagLabel->setPosition( diff --git a/loader/src/utils/VersionInfo.cpp b/loader/src/utils/VersionInfo.cpp index 13f8aad7..23269a82 100644 --- a/loader/src/utils/VersionInfo.cpp +++ b/loader/src/utils/VersionInfo.cpp @@ -10,31 +10,57 @@ USE_GEODE_NAMESPACE(); // VersionTag -std::optional geode::versionTagFromString(std::string const& str) { - switch (hash(str.c_str())) { - case hash("alpha"): return VersionTag::Alpha; - case hash("beta"): return VersionTag::Beta; - case hash("prerelease"): return VersionTag::Prerelease; - default: return std::nullopt; +Result VersionTag::parse(std::stringstream& str) { + std::string iden; + while ('a' <= str.peek() && str.peek() <= 'z') { + iden += str.get(); } + if (str.fail()) { + return Err("Unable to parse tag"); + } + VersionTag tag = VersionTag::Alpha; + switch (hash(iden.c_str())) { + case hash("alpha"): tag = VersionTag::Alpha; break; + case hash("beta"): tag = VersionTag::Beta; break; + case hash("prerelease"): case hash("pr"): tag = VersionTag::Prerelease; break; + default: return Err("Invalid tag \"" + iden + "\""); + } + if (str.peek() == '.') { + str.get(); + size_t num; + str >> num; + if (str.fail()) { + return Err("Unable to parse tag number"); + } + tag.number = num; + } + return Ok(tag); } -std::string geode::versionTagToSuffixString(VersionTag tag) { - switch (tag) { - case VersionTag::Alpha: return "-alpha"; - case VersionTag::Beta: return "-beta"; - case VersionTag::Prerelease: return "-prerelease"; +std::string VersionTag::toSuffixString() const { + std::string res = ""; + switch (value) { + case Alpha: res += "-alpha"; break; + case Beta: res += "-beta"; break; + case Prerelease: res += "-prerelease"; break; } - return ""; + if (number) { + res += "." + std::to_string(number.value()); + } + return res; } -std::string geode::versionTagToString(VersionTag tag) { - switch (tag) { - case VersionTag::Alpha: return "Alpha"; - case VersionTag::Beta: return "Beta"; - case VersionTag::Prerelease: return "Prerelease"; +std::string VersionTag::toString() const { + std::string res = ""; + switch (value) { + case Alpha: res += "Alpha"; break; + case Beta: res += "Beta"; break; + case Prerelease: res += "Prerelease"; break; } - return ""; + if (number) { + res += " " + std::to_string(number.value()); + } + return res; } // VersionInfo @@ -77,18 +103,13 @@ Result VersionInfo::parse(std::string const& string) { std::optional tag; if (str.peek() == '-') { str.get(); - std::string iden; - str >> iden; - if (str.fail()) { - return Err("Unable to parse tag"); - } - if (auto t = versionTagFromString(iden)) { - tag = t; - } - else { - return Err("Invalid tag \"" + iden + "\""); - } + GEODE_UNWRAP_INTO(tag, VersionTag::parse(str)); } + + if (!str.eof()) { + return Err("Expected end of version, found '" + std::string(1, str.get()) + "'"); + } + return Ok(VersionInfo(major, minor, patch, tag)); } @@ -97,7 +118,7 @@ std::string VersionInfo::toString(bool includeTag) const { return fmt::format( "v{}.{}.{}{}", m_major, m_minor, m_patch, - versionTagToSuffixString(m_tag.value()) + m_tag.value().toSuffixString() ); } return fmt::format("v{}.{}.{}", m_major, m_minor, m_patch);