diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp index fd28aa0b7..d9d26b9c2 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.cpp @@ -179,142 +179,85 @@ bool processZIP(ResourcePack& pack, ProcessingLevel level) return true; } -struct TextFormatter { - // left is value, right is if the value was explicitly written - QPair color = { "#000000", false }; - QPair bold = { false, false }; - QPair italic = { false, false }; - QPair underlined = { false, false }; - QPair strikethrough = { false, false }; - QPair is_linked = { false, false }; - QPair link_url = { "", false }; - - void setColor(const QString& new_color, bool written) { color = { new_color, written }; } - void setBold(bool new_bold, bool written) { bold = { new_bold, written }; } - void setItalic(bool new_italic, bool written) { italic = { new_italic, written }; } - void setUnderlined(bool new_underlined, bool written) { underlined = { new_underlined, written }; } - void setStrikethrough(bool new_strikethrough, bool written) { strikethrough = { new_strikethrough, written }; } - void setIsLinked(bool new_is_linked, bool written) { is_linked = { new_is_linked, written }; } - void setLinkURL(const QString& new_url, bool written) { link_url = { new_url, written }; } - - void overrideFrom(const TextFormatter& child) - { - if (child.color.second) - color.first = child.color.first; - if (child.bold.second) - bold.first = child.bold.first; - if (child.italic.second) - italic.first = child.italic.first; - if (child.underlined.second) - underlined.first = child.underlined.first; - if (child.strikethrough.second) - strikethrough.first = child.strikethrough.first; - if (child.is_linked.second) - is_linked.first = child.is_linked.first; - if (child.link_url.second) - link_url.first = child.link_url.first; - } - - QString format(QString text) - { - if (text.isEmpty()) - return QString(); - - QString result; - - if (color.first != "#000000") - result.append(""); - if (bold.first) - result.append(""); - if (italic.first) - result.append(""); - if (underlined.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (is_linked.first) - result.append(""); - - result.append(text); - - if (is_linked.first) - result.append(""); - if (strikethrough.first) - result.append(""); - if (underlined.first) - result.append(""); - if (italic.first) - result.append(""); - if (bold.first) - result.append(""); - if (color.first != "#000000") - result.append(""); - - return result; - } - - bool readFormat(const QJsonObject& obj) - { - setColor(Json::ensureString(obj, "color", "#000000"), obj.contains("color")); - setBold(Json::ensureBoolean(obj, "bold", false), obj.contains("bold")); - setItalic(Json::ensureBoolean(obj, "italic", false), obj.contains("italic")); - setUnderlined(Json::ensureBoolean(obj, "underlined", false), obj.contains("underlined")); - setStrikethrough(Json::ensureBoolean(obj, "strikethrough", false), obj.contains("strikethrough")); - - auto click_event = Json::ensureObject(obj, "clickEvent"); - setIsLinked(Json::ensureBoolean(click_event, "open_url", false), click_event.contains("open_url")); - setLinkURL(Json::ensureString(click_event, "value"), click_event.contains("value")); - - return true; - } -}; - -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat) +QString buildStyle(const QJsonObject& obj) { - TextFormatter formatter; - if (parentFormat) - formatter = *parentFormat; - - if (value.isString()) { - result.append(formatter.format(value.toString())); - } else if (value.isBool()) { - result.append(formatter.format(value.toBool() ? "true" : "false")); - } else if (value.isDouble()) { - result.append(formatter.format(QString::number(value.toDouble()))); - } else if (value.isObject()) { - auto obj = value.toObject(); - - if (not formatter.readFormat(obj)) - return false; - - // override the parent format with our new one - TextFormatter mixed; - if (parentFormat) - mixed = *parentFormat; - - mixed.overrideFrom(formatter); - - result.append(mixed.format(Json::ensureString(obj, "text"))); - - // process any 'extra' children with this format - auto extra = obj.value("extra"); - if (not extra.isUndefined()) - return processComponent(extra, result, &mixed); - - } else if (value.isArray()) { - auto array = value.toArray(); - - for (const QJsonValue& current : array) { - if (not processComponent(current, result, parentFormat)) { - return false; - } + QStringList styles; + if (auto color = Json::ensureString(obj, "color"); !color.isEmpty()) { + styles << QString("color: %1;").arg(color); + } + if (obj.contains("bold")) { + QString weight = "normal"; + if (Json::ensureBoolean(obj, "bold", false)) { + weight = "bold"; } - } else { - qWarning() << "Invalid component type!"; - return false; + styles << QString("font-weight: %1;").arg(weight); + } + if (obj.contains("italic")) { + QString style = "normal"; + if (Json::ensureBoolean(obj, "italic", false)) { + style = "italic"; + } + styles << QString("font-style: %1;").arg(style); } - return true; + return styles.isEmpty() ? "" : QString("style=\"%1\"").arg(styles.join(" ")); +} + +QString processComponent(const QJsonArray& value, bool strikethrough, bool underline) +{ + QString result; + for (auto current : value) + result += processComponent(current, strikethrough, underline); + return result; +} + +QString processComponent(const QJsonObject& obj, bool strikethrough, bool underline) +{ + underline = Json::ensureBoolean(obj, "underlined", underline); + strikethrough = Json::ensureBoolean(obj, "strikethrough", strikethrough); + + QString result = Json::ensureString(obj, "text"); + if (underline) { + result = QString("%1").arg(result); + } + if (strikethrough) { + result = QString("%1").arg(result); + } + // the extra needs to be a array + result += processComponent(Json::ensureArray(obj, "extra"), strikethrough, underline); + if (auto style = buildStyle(obj); !style.isEmpty()) { + result = QString("%2").arg(style, result); + } + if (obj.contains("clickEvent")) { + auto click_event = Json::ensureObject(obj, "clickEvent"); + auto action = Json::ensureString(click_event, "action"); + auto value = Json::ensureString(click_event, "value"); + if (action == "open_url" && !value.isEmpty()) { + result = QString("%2").arg(value, result); + } + } + return result; +} + +QString processComponent(const QJsonValue& value, bool strikethrough, bool underline) +{ + if (value.isString()) { + return value.toString(); + } + if (value.isBool()) { + return value.toBool() ? "true" : "false"; + } + if (value.isDouble()) { + return QString::number(value.toDouble()); + } + if (value.isArray()) { + return processComponent(value.toArray(), strikethrough, underline); + } + if (value.isObject()) { + return processComponent(value.toObject(), strikethrough, underline); + } + qWarning() << "Invalid component type!"; + return {}; } // https://minecraft.fandom.com/wiki/Tutorials/Creating_a_resource_pack#Formatting_pack.mcmeta @@ -327,12 +270,7 @@ bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data) pack.setPackFormat(Json::ensureInteger(pack_obj, "pack_format", 0)); - auto desc_val = pack_obj.value("description"); - QString desc{}; - if (not processComponent(desc_val, desc)) - return false; - - pack.setDescription(desc); + pack.setDescription(processComponent(pack_obj.value("description"))); } catch (Json::JsonException& e) { qWarning() << "JsonException: " << e.what() << e.cause(); diff --git a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h index 945499a91..97bf7b2ba 100644 --- a/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -34,8 +34,7 @@ bool process(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processZIP(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); bool processFolder(ResourcePack& pack, ProcessingLevel level = ProcessingLevel::Full); -struct TextFormatter; -bool processComponent(const QJsonValue& value, QString& result, const TextFormatter* parentFormat = nullptr); +QString processComponent(const QJsonValue& value, bool strikethrough = false, bool underline = false); bool processMCMeta(ResourcePack& pack, QByteArray&& raw_data); bool processPackPNG(const ResourcePack& pack, QByteArray&& raw_data); diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 6423e88d6..2cf3e7a93 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -287,8 +287,6 @@ void InfoFrame::setDescription(QString text) ui->descriptionLabel->setTextFormat(Qt::TextFormat::RichText); // This allows injecting HTML here. m_description = text; - const QString elidedPostfix = "..."; - // move the cursor to the character elide, doesn't see html QTextCursor cursor(&doc); cursor.movePosition(QTextCursor::End); @@ -296,7 +294,7 @@ void InfoFrame::setDescription(QString text) cursor.removeSelectedText(); // insert the post fix at the cursor - cursor.insertHtml(elidedPostfix); + cursor.insertHtml("..."); labeltext.append(doc.toHtml()); QObject::connect(ui->descriptionLabel, &QLabel::linkActivated, this, &InfoFrame::descriptionEllipsisHandler); @@ -335,7 +333,7 @@ void InfoFrame::setLicense(QString text) if (finaltext.length() > 290) { ui->licenseLabel->setOpenExternalLinks(false); ui->licenseLabel->setTextFormat(Qt::TextFormat::RichText); - m_description = text; + m_license = text; // This allows injecting HTML here. labeltext.append("" + finaltext.left(287) + "..."); QObject::connect(ui->licenseLabel, &QLabel::linkActivated, this, &InfoFrame::licenseEllipsisHandler); diff --git a/tests/MetaComponentParse_test.cpp b/tests/MetaComponentParse_test.cpp index 1b1e2ce3e..9979a9fa6 100644 --- a/tests/MetaComponentParse_test.cpp +++ b/tests/MetaComponentParse_test.cpp @@ -64,16 +64,14 @@ class MetaComponentParseTest : public QObject { QJsonValue description_json = obj.value("description"); QJsonValue expected_json = obj.value("expected_output"); - QVERIFY(description_json.isUndefined() == false); - QVERIFY(expected_json.isString() == true); + QVERIFY(!description_json.isUndefined()); + QVERIFY(expected_json.isString()); QString expected = expected_json.toString(); - QString processed; - bool valid = ResourcePackUtils::processComponent(description_json, processed); + QString processed = ResourcePackUtils::processComponent(description_json); - QVERIFY(processed == expected); - QVERIFY(valid == true); + QCOMPARE(processed, expected); } private slots: diff --git a/tests/testdata/MetaComponentParse/component_with_extra.json b/tests/testdata/MetaComponentParse/component_with_extra.json index e26b2abff..887becdbe 100644 --- a/tests/testdata/MetaComponentParse/component_with_extra.json +++ b/tests/testdata/MetaComponentParse/component_with_extra.json @@ -7,12 +7,15 @@ "italic": true, "extra": [ { - "extra": "Component!", + "extra": [ + "Component!" + ], "bold": false, "italic": false } ] } ], - "expected_output": "Hello, Component!" + "expected_output": + "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_format.json b/tests/testdata/MetaComponentParse/component_with_format.json index 00dfc7daf..1078886a6 100644 --- a/tests/testdata/MetaComponentParse/component_with_format.json +++ b/tests/testdata/MetaComponentParse/component_with_format.json @@ -9,5 +9,5 @@ "strikethrough": true } ], - "expected_output": "Hello, Component!" + "expected_output": "Hello, Component!" } \ No newline at end of file diff --git a/tests/testdata/MetaComponentParse/component_with_link.json b/tests/testdata/MetaComponentParse/component_with_link.json index b1e34c7d6..188c004cd 100644 --- a/tests/testdata/MetaComponentParse/component_with_link.json +++ b/tests/testdata/MetaComponentParse/component_with_link.json @@ -3,7 +3,7 @@ { "text": "Hello, Component!", "clickEvent": { - "open_url": true, + "action": "open_url", "value": "https://google.com" } } diff --git a/tests/testdata/MetaComponentParse/component_with_mixed.json b/tests/testdata/MetaComponentParse/component_with_mixed.json index 7c8c5b032..661fc1a3e 100644 --- a/tests/testdata/MetaComponentParse/component_with_mixed.json +++ b/tests/testdata/MetaComponentParse/component_with_mixed.json @@ -10,14 +10,16 @@ "color": "#873600", "bold": true, "underlined": true, - "extra": { - "text": "jumped over ", - "color": "blue", - "bold": false, - "underlined": false, - "italic": true, - "strikethrough": true - } + "extra": [ + { + "text": "jumped over ", + "color": "blue", + "bold": false, + "underlined": false, + "italic": true, + "strikethrough": true + } + ] }, { "text": "the lazy dog's back. ", @@ -31,11 +33,13 @@ "text": "1234567890 ", "color": "black", "strikethrough": false, - "extra": "How vexingly quick daft zebras jump!" + "extra": [ + "How vexingly quick daft zebras jump!" + ] } ] } ], "expected_output": - "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" + "The quick brown fox jumped over the lazy dog's back. 1234567890 How vexingly quick daft zebras jump!" }