Merge branch 'better-layouts' of https://github.com/geode-sdk/geode into better-layouts

This commit is contained in:
HJfod 2023-02-22 16:06:46 +02:00
commit 8fb118fb12
9 changed files with 218 additions and 102 deletions

View file

@ -15,14 +15,32 @@ string(STRIP "${GEODE_VERSION}" GEODE_VERSION)
# Check if version has a tag like v1.0.0-alpha # Check if version has a tag like v1.0.0-alpha
string(FIND ${GEODE_VERSION} "-" GEODE_VERSION_HAS_TAG) string(FIND ${GEODE_VERSION} "-" GEODE_VERSION_HAS_TAG)
if (GEODE_VERSION_HAS_TAG) if (NOT ${GEODE_VERSION_HAS_TAG} EQUAL "-1")
string(REGEX MATCH "[a-z]+[0-9]?$" GEODE_VERSION_TAG ${GEODE_VERSION}) string(REGEX MATCH "[a-z]+(\.[0-9]+)?$" GEODE_VERSION_TAG ${GEODE_VERSION})
string(SUBSTRING "${GEODE_VERSION}" 0 ${GEODE_VERSION_HAS_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() else()
set(GEODE_VERSION_TAG "") set(GEODE_VERSION_TAG_TYPE "${GEODE_VERSION_TAG}")
set(GEODE_VERSION_TAG_NUMBER "")
endif() endif()
message(STATUS "Version: ${GEODE_VERSION}, tag: ${GEODE_VERSION_TAG}") # 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} (type: ${GEODE_VERSION_TAG_TYPE}, number: ${GEODE_VERSION_TAG_NUMBER})")
project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C) project(geode-sdk VERSION ${GEODE_VERSION} LANGUAGES CXX C)

View file

@ -1 +1 @@
1.0.0-beta 1.0.0-beta.6

View file

@ -1,8 +1,21 @@
cmake_minimum_required(VERSION 3.21 FATAL_ERROR) cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
project(geode-loader VERSION ${GEODE_VERSION} LANGUAGES C CXX) project(geode-loader VERSION ${GEODE_VERSION} LANGUAGES C CXX)
set(PROJECT_VERSION_TYPE geode::VersionTag::Beta) if (GEODE_VERSION_TAG_TYPE)
set(PROJECT_VERSION_SUFFIX -beta) 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 # Package info file for internal representation
configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json) configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json)

View file

@ -14,24 +14,73 @@ namespace geode {
}; };
/** /**
* A version label, like v1.0.0-alpha or v2.3.4-prerelease. Purely semantic, * A version label, like v1.0.0-alpha or v2.3.4-prerelease. Limited to these
* and not used in comparisons; so for example v1.0.0-alpha == v1.0.0. * options; arbitary identifiers are not supported. Additional numbering
* may be added after the identifier, such as v1.0.0-beta.1
*/ */
enum class VersionTag { struct VersionTag {
enum {
Alpha, Alpha,
Beta, Beta,
Prerelease, Prerelease,
} value;
std::optional<size_t> number;
using Type = decltype(value);
constexpr VersionTag(Type const& value) : value(value) {}
constexpr VersionTag(Type const& value, std::optional<size_t> 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<VersionTag> parse(std::stringstream& str);
std::string toSuffixString() const;
std::string toString() const;
}; };
GEODE_DLL std::optional<VersionTag> 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 * Class representing version information. Uses a limited subset of SemVer;
* regard to identifiers; identifiers are restricted to a few common ones, * identifiers are restricted to a few predefined ones, and only one
* and are purely semantic, i.e. not used in comparisons. See VersionTag * identifier is allowed. See VersionTag for details
* for details
* @class VersionInfo
*/ */
class GEODE_DLL VersionInfo final { class GEODE_DLL VersionInfo final {
protected: protected:
@ -78,24 +127,24 @@ namespace geode {
// Apple clang does not support operator<=>! Yippee! // Apple clang does not support operator<=>! Yippee!
constexpr bool operator==(VersionInfo const& other) const { constexpr bool operator==(VersionInfo const& other) const {
return std::tie(m_major, m_minor, m_patch) == return std::tie(m_major, m_minor, m_patch, m_tag) ==
std::tie(other.m_major, other.m_minor, other.m_patch); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
constexpr bool operator<(VersionInfo const& other) const { constexpr bool operator<(VersionInfo const& other) const {
return std::tie(m_major, m_minor, m_patch) < return std::tie(m_major, m_minor, m_patch, m_tag) <
std::tie(other.m_major, other.m_minor, other.m_patch); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
constexpr bool operator<=(VersionInfo const& other) const { constexpr bool operator<=(VersionInfo const& other) const {
return std::tie(m_major, m_minor, m_patch) <= return std::tie(m_major, m_minor, m_patch, m_tag) <=
std::tie(other.m_major, other.m_minor, other.m_patch); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
constexpr bool operator>(VersionInfo const& other) const { constexpr bool operator>(VersionInfo const& other) const {
return std::tie(m_major, m_minor, m_patch) > return std::tie(m_major, m_minor, m_patch, m_tag) >
std::tie(other.m_major, other.m_minor, other.m_patch); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
constexpr bool operator>=(VersionInfo const& other) const { constexpr bool operator>=(VersionInfo const& other) const {
return std::tie(m_major, m_minor, m_patch) >= return std::tie(m_major, m_minor, m_patch, m_tag) >=
std::tie(other.m_major, other.m_minor, other.m_patch); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
std::string toString(bool includeTag = true) const; std::string toString(bool includeTag = true) const;

View file

@ -1,5 +1,5 @@
{ {
"geode": "@PROJECT_VERSION@", "geode": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@",
"id": "geode.loader", "id": "geode.loader",
"version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@", "version": "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@",
"name": "Geode", "name": "Geode",

View file

@ -269,8 +269,9 @@ AxisLayout::Row* AxisLayout::fitInRow(
} }
} }
axisLength += pos.axisLength; axisLength += pos.axisLength;
if (pos.crossLength > crossLength) { // squishing doesn't affect cross length, that's done separately
crossLength = pos.crossLength; if (pos.crossLength / squish > crossLength) {
crossLength = pos.crossLength / squish;
} }
prev = opts; prev = opts;
ix++; ix++;
@ -303,7 +304,7 @@ AxisLayout::Row* AxisLayout::fitInRow(
available.axisLength / nextAxisLength * scale * squish, available.axisLength / nextAxisLength * scale * squish,
// how much should the nodes be squished to fit the next item in this // how much should the nodes be squished to fit the next item in this
// row // row
available.axisLength / nextAxisLength * scale * squish, available.axisLength / nextAxisLength,
axisLength, axisLength,
crossLength, crossLength,
axisEndsLength, axisEndsLength,
@ -322,6 +323,8 @@ void AxisLayout::tryFitLayout(
// like i genuinely have no clue fr why some of these work tho, // like i genuinely have no clue fr why some of these work tho,
// i just threw in random equations and numbers until it worked // i just threw in random equations and numbers until it worked
log::debug("tryFitLayout -> scale: {}, squish: {}, prio: {}", scale, squish, prio);
auto rows = CCArray::create(); auto rows = CCArray::create();
float totalRowCrossLength = 0.f; float totalRowCrossLength = 0.f;
float crossScaleDownFactor = 0.f; float crossScaleDownFactor = 0.f;
@ -358,16 +361,32 @@ void AxisLayout::tryFitLayout(
} }
newNodes->release(); newNodes->release();
if (!rows->count()) {
return;
}
log::debug("crossScaleDownFactor: {}, crossSquishFactor: {}", crossScaleDownFactor, crossSquishFactor);
auto available = nodeAxis(on, m_axis, 1.f); 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 // 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 // down layout if there are any nodes with auto-scale enabled (or
// auto-scale is enabled by default) // auto-scale is enabled by default)
if ( if (
(
!m_allowCrossAxisOverflow && !m_allowCrossAxisOverflow &&
doAutoScale && doAutoScale &&
totalRowCrossLength > available.crossLength totalRowCrossLength > available.crossLength
) || (
!m_growCrossAxis &&
static_cast<Row*>(rows->firstObject())->axisLength > available.axisLength
)
) { ) {
log::debug("scaling");
bool attemptRescale = false; bool attemptRescale = false;
auto minScaleForPrio = this->minScaleForPrio(nodes, prio); auto minScaleForPrio = this->minScaleForPrio(nodes, prio);
if ( if (
@ -379,7 +398,9 @@ void AxisLayout::tryFitLayout(
crossScaleDownFactor == scale crossScaleDownFactor == scale
) { ) {
// is there still some lower priority nodes we could try scaling? // is there still some lower priority nodes we could try scaling?
log::debug("prio: {} :: scale: {}", prio, scale);
if (prio > minMaxPrios.first) { if (prio > minMaxPrios.first) {
log::debug("scale to max");
while (true) { while (true) {
prio -= 1; prio -= 1;
auto scale = this->maxScaleForPrio(nodes, prio); auto scale = this->maxScaleForPrio(nodes, prio);
@ -391,13 +412,17 @@ void AxisLayout::tryFitLayout(
} }
attemptRescale = true; attemptRescale = true;
} }
// otherwise we're just gonna squish // otherwise set scale to min and squish
else {
scale = minScaleForPrio;
}
} }
// otherwise scale as usual // otherwise scale as usual
else { else {
attemptRescale = true; attemptRescale = true;
} }
if (attemptRescale) { if (attemptRescale) {
log::debug("gonna try with scale {} from {}", crossScaleDownFactor, scale);
rows->release(); rows->release();
return this->tryFitLayout( return this->tryFitLayout(
on, nodes, on, nodes,
@ -408,10 +433,21 @@ void AxisLayout::tryFitLayout(
} }
// if we're still overflowing, squeeze nodes closer together // 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<Row*>(rows->firstObject())->axisLength > available.axisLength
)
) {
log::debug("gonna try with squish {} from {}", crossSquishFactor, squish);
// if squishing rows would take less squishing that squishing columns, // if squishing rows would take less squishing that squishing columns,
// then squish rows // then squish rows
if (totalRowCrossLength / available.crossLength < crossSquishFactor) { if (
!m_growCrossAxis || totalRowCrossLength / available.crossLength < crossSquishFactor
) {
rows->release(); rows->release();
return this->tryFitLayout( return this->tryFitLayout(
on, nodes, on, nodes,
@ -489,25 +525,6 @@ void AxisLayout::tryFitLayout(
rowCrossPos -= row->crossLength * columnSquish; 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; float rowAxisPos;
switch (m_axisAlignment) { switch (m_axisAlignment) {
case AxisAlignment::Start: { case AxisAlignment::Start: {
@ -532,34 +549,33 @@ void AxisLayout::tryFitLayout(
size_t ix = 0; size_t ix = 0;
AxisLayoutOptions const* prev = nullptr; AxisLayoutOptions const* prev = nullptr;
for (auto& node : CCArrayExt<CCNode*>(row->nodes)) { for (auto& node : CCArrayExt<CCNode*>(row->nodes)) {
auto scale = rowScale;
auto opts = axisOpts(node); auto opts = axisOpts(node);
// rescale node if overflowing // rescale node if overflowing
if (this->shouldAutoScale(opts)) { if (this->shouldAutoScale(opts)) {
scale = scaleByOpts(opts, scale, prio); auto nodeScale = scaleByOpts(opts, scale, prio);
// CCMenuItemSpriteExtra is quirky af // CCMenuItemSpriteExtra is quirky af
if (auto btn = typeinfo_cast<CCMenuItemSpriteExtra*>(node)) { if (auto btn = typeinfo_cast<CCMenuItemSpriteExtra*>(node)) {
btn->m_baseScale = scale; btn->m_baseScale = nodeScale;
} }
node->setScale(scale); node->setScale(nodeScale);
} }
if (!ix) { 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; float axisPos;
if (m_axisAlignment == AxisAlignment::Even) { if (m_axisAlignment == AxisAlignment::Even) {
axisPos = rowAxisPos + evenSpace / 2 - pos.axisLength * (.5f - pos.axisAnchor); axisPos = rowAxisPos + evenSpace / 2 - pos.axisLength * (.5f - pos.axisAnchor);
rowAxisPos += evenSpace - rowAxisPos += evenSpace -
row->axisEndsLength * scale * (1.f - rowSquish) * 1.f / nodes->count(); row->axisEndsLength * scale * (1.f - squish) * 1.f / nodes->count();
} }
else { else {
if (ix) { if (ix) {
rowAxisPos += this->nextGap(prev, opts) * scale * rowSquish; rowAxisPos += this->nextGap(prev, opts) * scale * squish;
} }
axisPos = rowAxisPos + pos.axisLength * pos.axisAnchor; axisPos = rowAxisPos + pos.axisLength * pos.axisAnchor;
rowAxisPos += pos.axisLength - 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; float crossOffset;
switch (m_crossAlignment) { switch (m_crossAlignment) {
@ -634,6 +650,7 @@ void AxisLayout::apply(CCNode* on) {
} }
} }
log::debug("applying");
this->tryFitLayout( this->tryFitLayout(
on, nodes, on, nodes,
minMaxPrio, doAutoScale, minMaxPrio, doAutoScale,

View file

@ -11,6 +11,6 @@ static constexpr geode::VersionInfo LOADER_VERSION = {
@PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MAJOR@,
@PROJECT_VERSION_MINOR@, @PROJECT_VERSION_MINOR@,
@PROJECT_VERSION_PATCH@, @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"; static constexpr const char* LOADER_MOD_JSON = R"JSON_SEPARATOR(@LOADER_MOD_JSON@)JSON_SEPARATOR";

View file

@ -79,9 +79,7 @@ void ModListCell::setupInfo(
this->addChild(versionLabel); this->addChild(versionLabel);
if (auto tag = info.version().getTag()) { if (auto tag = info.version().getTag()) {
auto tagLabel = TagNode::create( auto tagLabel = TagNode::create(tag.value().toString().c_str());
versionTagToString(tag.value()).c_str()
);
tagLabel->setAnchorPoint({ .0f, .5f }); tagLabel->setAnchorPoint({ .0f, .5f });
tagLabel->setScale(.3f); tagLabel->setScale(.3f);
tagLabel->setPosition( tagLabel->setPosition(

View file

@ -10,31 +10,57 @@ USE_GEODE_NAMESPACE();
// VersionTag // VersionTag
std::optional<VersionTag> geode::versionTagFromString(std::string const& str) { Result<VersionTag> VersionTag::parse(std::stringstream& str) {
switch (hash(str.c_str())) { std::string iden;
case hash("alpha"): return VersionTag::Alpha; while ('a' <= str.peek() && str.peek() <= 'z') {
case hash("beta"): return VersionTag::Beta; iden += str.get();
case hash("prerelease"): return VersionTag::Prerelease;
default: return std::nullopt;
} }
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) { std::string VersionTag::toSuffixString() const {
switch (tag) { std::string res = "";
case VersionTag::Alpha: return "-alpha"; switch (value) {
case VersionTag::Beta: return "-beta"; case Alpha: res += "-alpha"; break;
case VersionTag::Prerelease: return "-prerelease"; 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) { std::string VersionTag::toString() const {
switch (tag) { std::string res = "";
case VersionTag::Alpha: return "Alpha"; switch (value) {
case VersionTag::Beta: return "Beta"; case Alpha: res += "Alpha"; break;
case VersionTag::Prerelease: return "Prerelease"; case Beta: res += "Beta"; break;
case Prerelease: res += "Prerelease"; break;
} }
return ""; if (number) {
res += " " + std::to_string(number.value());
}
return res;
} }
// VersionInfo // VersionInfo
@ -77,18 +103,13 @@ Result<VersionInfo> VersionInfo::parse(std::string const& string) {
std::optional<VersionTag> tag; std::optional<VersionTag> tag;
if (str.peek() == '-') { if (str.peek() == '-') {
str.get(); str.get();
std::string iden; GEODE_UNWRAP_INTO(tag, VersionTag::parse(str));
str >> iden;
if (str.fail()) {
return Err("Unable to parse tag");
}
if (auto t = versionTagFromString(iden)) {
tag = t;
}
else {
return Err("Invalid tag \"" + iden + "\"");
} }
if (!str.eof()) {
return Err("Expected end of version, found '" + std::string(1, str.get()) + "'");
} }
return Ok(VersionInfo(major, minor, patch, tag)); return Ok(VersionInfo(major, minor, patch, tag));
} }
@ -97,7 +118,7 @@ std::string VersionInfo::toString(bool includeTag) const {
return fmt::format( return fmt::format(
"v{}.{}.{}{}", "v{}.{}.{}{}",
m_major, m_minor, m_patch, m_major, m_minor, m_patch,
versionTagToSuffixString(m_tag.value()) m_tag.value().toSuffixString()
); );
} }
return fmt::format("v{}.{}.{}", m_major, m_minor, m_patch); return fmt::format("v{}.{}.{}", m_major, m_minor, m_patch);